aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--HOWTO/BENCHMARKS.md73
-rw-r--r--HOWTO/BOOTSTRAP.md (renamed from README.bootstrap)24
-rw-r--r--HOWTO/DTRACE.md (renamed from README.dtrace.md)33
-rw-r--r--HOWTO/INSTALL-CROSS.md560
-rw-r--r--HOWTO/INSTALL-WIN32.md (renamed from INSTALL-WIN32.md)3
-rw-r--r--HOWTO/INSTALL.md (renamed from INSTALL.md)31
-rw-r--r--HOWTO/MARKDOWN.md (renamed from README.md.txt)4
-rw-r--r--HOWTO/SYSTEMTAP.md75
-rw-r--r--HOWTO/TESTING.md150
l---------INSTALL-CROSS.md1
-rw-r--r--README.md10
-rw-r--r--README.systemtap.md72
-rw-r--r--TAR.include8
-rw-r--r--aclocal.m4108
-rw-r--r--bootstrap/bin/start.bootbin5254 -> 5248 bytes
-rw-r--r--bootstrap/bin/start_clean.bootbin5254 -> 5248 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_type.beambin13852 -> 14032 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_utils.beambin14552 -> 14604 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_validator.beambin34476 -> 34536 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compile.beambin37112 -> 37460 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compiler.app69
-rw-r--r--bootstrap/lib/compiler/ebin/compiler.appup1
-rw-r--r--bootstrap/lib/compiler/ebin/sys_pre_expand.beambin16292 -> 16292 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code.beambin6708 -> 6960 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code_server.beambin26228 -> 26256 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_1.beambin24896 -> 24928 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_ddll.beambin2560 -> 2868 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/error_logger.beambin4344 -> 4416 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erts_debug.beambin3316 -> 3916 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file.beambin14244 -> 14416 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/hipe_unified_loader.beambin12500 -> 12732 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet.beambin20168 -> 20172 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_config.beambin8192 -> 7468 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_parse.beambin13056 -> 12832 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.app120
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.appup27
-rw-r--r--bootstrap/lib/kernel/ebin/net_kernel.beambin22720 -> 22800 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/os.beambin5228 -> 5276 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/rpc.beambin8748 -> 8748 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/binary.beambin2640 -> 3660 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/epp.beambin24188 -> 24276 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_lint.beambin85064 -> 85172 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/escript.beambin16876 -> 17056 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ets.beambin20008 -> 21724 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_server.beambin17256 -> 17260 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/lists.beambin29204 -> 29556 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/math.beambin308 -> 1104 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/otp_internal.beambin7160 -> 7156 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc_pt.beambin71584 -> 71588 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/re.beambin12348 -> 12544 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/stdlib.app105
-rw-r--r--bootstrap/lib/stdlib/ebin/stdlib.appup27
-rw-r--r--bootstrap/lib/stdlib/ebin/string.beambin4744 -> 4872 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/unicode.beambin11412 -> 11620 bytes
-rw-r--r--erts/aclocal.m4108
-rw-r--r--erts/autoconf/vxworks/sed.general2
-rw-r--r--erts/configure.in69
-rw-r--r--erts/doc/src/Makefile6
-rw-r--r--erts/doc/src/erl.xml8
-rw-r--r--erts/doc/src/erl_driver.xml10
-rw-r--r--erts/doc/src/erl_nif.xml26
-rw-r--r--erts/doc/src/erl_set_memory_block.xml172
-rw-r--r--erts/doc/src/erlang.xml3835
-rw-r--r--erts/doc/src/erlc.xml4
-rw-r--r--erts/doc/src/erlsrv.xml15
-rw-r--r--erts/doc/src/notes.xml375
-rw-r--r--erts/doc/src/ref_man.xml1
-rw-r--r--erts/emulator/Makefile.in53
-rw-r--r--erts/emulator/beam/beam_bif_load.c765
-rw-r--r--erts/emulator/beam/beam_bp.c2002
-rw-r--r--erts/emulator/beam/beam_bp.h233
-rw-r--r--erts/emulator/beam/beam_catches.c136
-rw-r--r--erts/emulator/beam/beam_catches.h11
-rw-r--r--erts/emulator/beam/beam_debug.c30
-rw-r--r--erts/emulator/beam/beam_emu.c393
-rw-r--r--erts/emulator/beam/beam_load.c600
-rw-r--r--erts/emulator/beam/beam_load.h49
-rw-r--r--erts/emulator/beam/beam_ranges.c349
-rw-r--r--erts/emulator/beam/benchmark.c2
-rw-r--r--erts/emulator/beam/benchmark.h2
-rw-r--r--erts/emulator/beam/bif.c161
-rw-r--r--erts/emulator/beam/bif.h18
-rw-r--r--erts/emulator/beam/bif.tab9
-rw-r--r--erts/emulator/beam/break.c147
-rw-r--r--erts/emulator/beam/code_ix.c166
-rw-r--r--erts/emulator/beam/code_ix.h141
-rw-r--r--erts/emulator/beam/dist.c63
-rw-r--r--erts/emulator/beam/erl_alloc.c20
-rw-r--r--erts/emulator/beam/erl_alloc.types30
-rw-r--r--erts/emulator/beam/erl_alloc_util.c129
-rw-r--r--erts/emulator/beam/erl_async.c12
-rw-r--r--erts/emulator/beam/erl_bif_binary.c69
-rw-r--r--erts/emulator/beam/erl_bif_chksum.c13
-rw-r--r--erts/emulator/beam/erl_bif_ddll.c12
-rwxr-xr-xerts/emulator/beam/erl_bif_info.c24
-rw-r--r--erts/emulator/beam/erl_bif_os.c110
-rw-r--r--erts/emulator/beam/erl_bif_port.c16
-rw-r--r--erts/emulator/beam/erl_bif_re.c11
-rw-r--r--erts/emulator/beam/erl_bif_timer.c4
-rw-r--r--erts/emulator/beam/erl_bif_trace.c623
-rw-r--r--erts/emulator/beam/erl_bits.c12
-rw-r--r--erts/emulator/beam/erl_cpu_topology.c5
-rw-r--r--erts/emulator/beam/erl_db.c96
-rw-r--r--erts/emulator/beam/erl_db.h10
-rw-r--r--erts/emulator/beam/erl_db_tree.c12
-rw-r--r--erts/emulator/beam/erl_db_util.c16
-rw-r--r--erts/emulator/beam/erl_db_util.h4
-rw-r--r--erts/emulator/beam/erl_debug.c2
-rw-r--r--erts/emulator/beam/erl_debug.h2
-rw-r--r--erts/emulator/beam/erl_driver.h11
-rw-r--r--erts/emulator/beam/erl_fun.h4
-rw-r--r--erts/emulator/beam/erl_gc.c29
-rw-r--r--erts/emulator/beam/erl_init.c78
-rw-r--r--erts/emulator/beam/erl_lock_check.c9
-rw-r--r--erts/emulator/beam/erl_lock_count.c2
-rw-r--r--erts/emulator/beam/erl_lock_count.h2
-rw-r--r--erts/emulator/beam/erl_message.c268
-rw-r--r--erts/emulator/beam/erl_message.h32
-rw-r--r--erts/emulator/beam/erl_mtrace.c2
-rw-r--r--erts/emulator/beam/erl_nif.c1149
-rw-r--r--erts/emulator/beam/erl_nif.h17
-rw-r--r--erts/emulator/beam/erl_node_container_utils.h43
-rw-r--r--erts/emulator/beam/erl_node_tables.c40
-rw-r--r--erts/emulator/beam/erl_port_task.c224
-rw-r--r--erts/emulator/beam/erl_port_task.h13
-rw-r--r--erts/emulator/beam/erl_printf_term.c5
-rw-r--r--erts/emulator/beam/erl_process.c4079
-rw-r--r--erts/emulator/beam/erl_process.h557
-rw-r--r--erts/emulator/beam/erl_process_dump.c15
-rw-r--r--erts/emulator/beam/erl_process_lock.c610
-rw-r--r--erts/emulator/beam/erl_process_lock.h423
-rw-r--r--erts/emulator/beam/erl_sched_spec_pre_alloc.c113
-rw-r--r--erts/emulator/beam/erl_sched_spec_pre_alloc.h7
-rw-r--r--erts/emulator/beam/erl_term.c2
-rw-r--r--erts/emulator/beam/erl_thr_progress.h10
-rw-r--r--erts/emulator/beam/erl_threads.h31
-rw-r--r--erts/emulator/beam/erl_time.h4
-rw-r--r--erts/emulator/beam/erl_time_sup.c101
-rw-r--r--erts/emulator/beam/erl_trace.c374
-rw-r--r--erts/emulator/beam/erl_unicode.c185
-rw-r--r--erts/emulator/beam/erl_vm.h2
-rw-r--r--erts/emulator/beam/erlang_dtrace.d2916
-rw-r--r--erts/emulator/beam/export.c354
-rw-r--r--erts/emulator/beam/export.h48
-rw-r--r--erts/emulator/beam/external.c8
-rwxr-xr-xerts/emulator/beam/global.h259
-rw-r--r--erts/emulator/beam/index.c23
-rw-r--r--erts/emulator/beam/index.h14
-rw-r--r--erts/emulator/beam/io.c89
-rw-r--r--erts/emulator/beam/module.c159
-rw-r--r--erts/emulator/beam/module.h64
-rw-r--r--erts/emulator/beam/ops.tab18
-rw-r--r--erts/emulator/beam/register.c24
-rw-r--r--erts/emulator/beam/sys.h175
-rw-r--r--erts/emulator/beam/utils.c334
-rw-r--r--erts/emulator/drivers/common/efile_drv.c16
-rw-r--r--erts/emulator/drivers/common/gzio.c5
-rw-r--r--erts/emulator/drivers/common/inet_drv.c242
-rw-r--r--erts/emulator/drivers/unix/unix_efile.c561
-rw-r--r--erts/emulator/drivers/win32/registry_drv.c2
-rw-r--r--erts/emulator/drivers/win32/win_con.c4
-rw-r--r--erts/emulator/drivers/win32/win_efile.c7
-rw-r--r--erts/emulator/hipe/hipe_arm.c2
-rw-r--r--erts/emulator/hipe/hipe_arm_bifs.m42
-rw-r--r--erts/emulator/hipe/hipe_bif0.c55
-rw-r--r--erts/emulator/hipe/hipe_bif1.c2
-rw-r--r--erts/emulator/hipe/hipe_bif2.c9
-rw-r--r--erts/emulator/hipe/hipe_bif2.tab3
-rw-r--r--erts/emulator/hipe/hipe_bif_list.m41
-rw-r--r--erts/emulator/hipe/hipe_debug.c2
-rw-r--r--erts/emulator/hipe/hipe_gc.c2
-rw-r--r--erts/emulator/hipe/hipe_mkliterals.c2
-rw-r--r--erts/emulator/hipe/hipe_mode_switch.c10
-rw-r--r--erts/emulator/hipe/hipe_native_bif.h3
-rw-r--r--erts/emulator/hipe/hipe_ppc_bifs.m416
-rw-r--r--erts/emulator/hipe/hipe_primops.h1
-rw-r--r--erts/emulator/hipe/hipe_stack.c10
-rw-r--r--erts/emulator/hipe/hipe_stack.h6
-rw-r--r--erts/emulator/hipe/hipe_x86_gc.h5
-rw-r--r--erts/emulator/hipe/hipe_x86_glue.h3
-rw-r--r--erts/emulator/sys/common/erl_check_io.c5
-rw-r--r--erts/emulator/sys/common/erl_poll.c57
-rw-r--r--erts/emulator/sys/common/erl_poll.h2
-rw-r--r--erts/emulator/sys/unix/sys.c69
-rw-r--r--erts/emulator/sys/unix/sys_float.c8
-rw-r--r--erts/emulator/sys/vxworks/driver_int.h30
-rw-r--r--erts/emulator/sys/vxworks/erl_main.c45
-rw-r--r--erts/emulator/sys/vxworks/erl_vxworks_sys.h184
-rw-r--r--erts/emulator/sys/vxworks/erl_vxworks_sys_ddll.c253
-rw-r--r--erts/emulator/sys/vxworks/sys.c2610
-rw-r--r--erts/emulator/sys/win32/erl_win_sys.h11
-rwxr-xr-xerts/emulator/sys/win32/sys.c96
-rw-r--r--erts/emulator/sys/win32/sys_env.c570
-rw-r--r--erts/emulator/sys/win32/sys_float.c8
-rw-r--r--erts/emulator/sys/win32/sys_interrupt.c6
-rw-r--r--erts/emulator/sys/win32/sys_time.c6
-rw-r--r--erts/emulator/test/Makefile11
-rw-r--r--erts/emulator/test/bif_SUITE.erl283
-rw-r--r--erts/emulator/test/binary_SUITE.erl2
-rw-r--r--erts/emulator/test/bs_construct_SUITE.erl2
-rw-r--r--erts/emulator/test/call_trace_SUITE.erl133
-rw-r--r--erts/emulator/test/call_trace_SUITE_data/my_upgrade_test.erl26
-rw-r--r--erts/emulator/test/code_SUITE.erl2
-rw-r--r--erts/emulator/test/code_parallel_load_SUITE.erl198
-rw-r--r--erts/emulator/test/distribution_SUITE.erl23
-rw-r--r--erts/emulator/test/driver_SUITE_data/chkio_drv.c2
-rw-r--r--erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c2
-rw-r--r--erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c2
-rw-r--r--erts/emulator/test/driver_SUITE_data/missing_callback_drv.c2
-rw-r--r--erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c2
-rw-r--r--erts/emulator/test/driver_SUITE_data/timer_drv.c12
-rw-r--r--erts/emulator/test/emulator.spec.vxworks26
-rw-r--r--erts/emulator/test/emulator_bench.spec1
-rw-r--r--erts/emulator/test/estone_SUITE.erl16
-rw-r--r--erts/emulator/test/estone_SUITE_data/estone_cat.c4
-rw-r--r--erts/emulator/test/float_SUITE.erl11
-rw-r--r--erts/emulator/test/fun_SUITE.erl2
-rw-r--r--erts/emulator/test/gc_SUITE.erl59
-rw-r--r--erts/emulator/test/hibernate_SUITE.erl2
-rw-r--r--erts/emulator/test/mtx_SUITE_data/Makefile.src7
-rw-r--r--erts/emulator/test/node_container_SUITE.erl2
-rw-r--r--erts/emulator/test/nofrag_SUITE.erl26
-rw-r--r--erts/emulator/test/port_SUITE.erl1212
-rw-r--r--erts/emulator/test/port_SUITE_data/dead_port.c24
-rw-r--r--erts/emulator/test/port_SUITE_data/port_test.c35
-rw-r--r--erts/emulator/test/port_SUITE_data/reclaim.h60
-rw-r--r--erts/emulator/test/port_bif_SUITE.erl2
-rw-r--r--erts/emulator/test/port_bif_SUITE_data/port_test.c37
-rw-r--r--erts/emulator/test/port_bif_SUITE_data/reclaim.h60
-rw-r--r--erts/emulator/test/process_SUITE.erl10
-rw-r--r--erts/emulator/test/timer_bif_SUITE.erl67
-rw-r--r--erts/emulator/test/trace_local_SUITE.erl186
-rw-r--r--erts/emulator/test/trace_port_SUITE.erl7
-rwxr-xr-xerts/emulator/utils/make_preload3
-rwxr-xr-xerts/emulator/utils/make_tables2
-rw-r--r--erts/epmd/src/epmd.c2
-rw-r--r--erts/epmd/src/epmd_srv.c7
-rw-r--r--erts/epmd/test/Makefile2
-rw-r--r--erts/epmd/test/epmd_SUITE.erl2
-rw-r--r--erts/etc/common/Makefile.in102
-rw-r--r--erts/etc/common/dialyzer.c2
-rw-r--r--erts/etc/common/erlc.c4
-rw-r--r--erts/etc/common/erlexec.c173
-rw-r--r--erts/etc/common/heart.c113
-rw-r--r--erts/etc/common/inet_gethost.c12
-rw-r--r--erts/etc/unix/Install.src2
-rw-r--r--erts/etc/unix/cerl.src4
-rw-r--r--erts/etc/unix/erl.src.src2
-rw-r--r--erts/etc/unix/etp-commands772
-rw-r--r--erts/etc/unix/run_erl.c2
-rw-r--r--erts/etc/vxworks/README.VxWorks350
-rw-r--r--erts/etc/vxworks/erl.exec.c129
-rw-r--r--erts/etc/vxworks/erl_io.c108
-rw-r--r--erts/etc/vxworks/erl_script.sam.in100
-rw-r--r--erts/etc/vxworks/heart_config.c60
-rw-r--r--erts/etc/vxworks/heart_config.h35
-rw-r--r--erts/etc/vxworks/rdate.c87
-rw-r--r--erts/etc/vxworks/reclaim.c551
-rw-r--r--erts/etc/vxworks/reclaim.h150
-rw-r--r--erts/etc/vxworks/reclaim_private.h44
-rw-r--r--erts/etc/vxworks/resolv.conf6
-rw-r--r--erts/etc/vxworks/vxcall.c145
-rw-r--r--erts/etc/vxworks/wd_example.c141
-rw-r--r--erts/etc/win32/Makefile2
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_service.c5
-rw-r--r--erts/etc/win32/nsis/Makefile2
-rw-r--r--erts/etc/win32/port_entry.c4
-rw-r--r--erts/etc/win32/start_erl.c5
-rw-r--r--erts/include/internal/ethread.h2
-rw-r--r--erts/lib_src/common/erl_printf_format.c15
-rw-r--r--erts/lib_src/common/ethr_aux.c2
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin52904 -> 54148 bytes
-rw-r--r--erts/preloaded/ebin/erlang.beambin42416 -> 88548 bytes
-rw-r--r--erts/preloaded/ebin/init.beambin48064 -> 48580 bytes
-rw-r--r--erts/preloaded/ebin/otp_ring0.beambin1448 -> 1448 bytes
-rw-r--r--erts/preloaded/ebin/prim_file.beambin40608 -> 40816 bytes
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin70100 -> 69764 bytes
-rw-r--r--erts/preloaded/ebin/prim_zip.beambin23460 -> 23460 bytes
-rw-r--r--erts/preloaded/ebin/zlib.beambin12804 -> 12796 bytes
-rw-r--r--erts/preloaded/src/Makefile2
-rw-r--r--erts/preloaded/src/erl_prim_loader.erl361
-rw-r--r--erts/preloaded/src/erlang.erl2354
-rw-r--r--erts/preloaded/src/init.erl16
-rw-r--r--erts/preloaded/src/prim_file.erl48
-rw-r--r--erts/preloaded/src/prim_inet.erl13
-rw-r--r--erts/start_scripts/Makefile2
-rw-r--r--erts/test/Makefile14
-rw-r--r--erts/test/autoimport_SUITE.erl196
-rw-r--r--erts/test/erl_print_SUITE_data/Makefile.src2
-rw-r--r--erts/test/erl_print_SUITE_data/erl_print_tests.c2
-rw-r--r--erts/test/erlc_SUITE.erl2
-rw-r--r--erts/test/erlexec_SUITE_data/Makefile.src2
-rw-r--r--erts/test/ethread_SUITE_data/Makefile.src2
-rw-r--r--erts/test/install_SUITE.erl2
-rw-r--r--erts/test/otp_SUITE.erl22
-rw-r--r--erts/test/run_erl_SUITE.erl2
-rw-r--r--erts/test/system.spec.vxworks2
-rw-r--r--erts/test/utils/gccifier.c2
-rwxr-xr-xerts/test/utils/gccifier.sh2
-rw-r--r--erts/vsn.mk4
-rw-r--r--lib/.gitignore1
-rw-r--r--lib/Makefile2
-rw-r--r--lib/appmon/doc/src/Makefile2
-rw-r--r--lib/appmon/priv/Makefile2
-rw-r--r--lib/appmon/src/Makefile2
-rw-r--r--lib/asn1/c_src/Makefile7
-rw-r--r--lib/asn1/doc/src/Makefile2
-rw-r--r--lib/asn1/doc/src/notes.xml71
-rw-r--r--lib/asn1/src/asn1ct.erl2
-rw-r--r--lib/asn1/src/asn1ct_check.erl2
-rw-r--r--lib/asn1/src/asn1ct_constructed_per.erl6
-rw-r--r--lib/asn1/src/asn1ct_gen.erl31
-rw-r--r--lib/asn1/src/asn1ct_gen_ber_bin_v2.erl2
-rw-r--r--lib/asn1/src/asn1ct_gen_per.erl16
-rw-r--r--lib/asn1/src/asn1ct_gen_per_rt2ct.erl15
-rw-r--r--lib/asn1/src/asn1rt_ber_bin_v2.erl6
-rw-r--r--lib/asn1/src/asn1rt_check.erl3
-rw-r--r--lib/asn1/src/asn1rt_per_bin.erl50
-rw-r--r--lib/asn1/src/asn1rt_per_bin_rt2ct.erl46
-rw-r--r--lib/asn1/test/asn1_SUITE.erl24
-rw-r--r--lib/asn1/test/asn1_SUITE_data/Constraints.py5
-rw-r--r--lib/asn1/test/asn1_SUITE_data/EnumN2N.asn125
-rw-r--r--lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn2
-rw-r--r--lib/asn1/test/asn1_SUITE_data/LargeConstraints.py9
-rw-r--r--lib/asn1/test/testConstraints.erl74
-rw-r--r--lib/asn1/vsn.mk2
-rw-r--r--lib/common_test/doc/src/Makefile7
-rw-r--r--lib/common_test/doc/src/common_test_app.xml61
-rw-r--r--lib/common_test/doc/src/config_file_chapter.xml23
-rw-r--r--lib/common_test/doc/src/cover_chapter.xml4
-rw-r--r--lib/common_test/doc/src/ct_hooks.xml20
-rw-r--r--lib/common_test/doc/src/ct_hooks_chapter.xml13
-rw-r--r--lib/common_test/doc/src/ct_master_chapter.xml51
-rw-r--r--lib/common_test/doc/src/ct_run.xml28
-rw-r--r--lib/common_test/doc/src/event_handler_chapter.xml12
-rw-r--r--lib/common_test/doc/src/example_chapter.xml5
-rw-r--r--lib/common_test/doc/src/getting_started_chapter.xml57
-rw-r--r--lib/common_test/doc/src/notes.xml232
-rw-r--r--lib/common_test/doc/src/ref_man.xml3
-rw-r--r--lib/common_test/doc/src/run_test_chapter.xml557
-rw-r--r--lib/common_test/doc/src/write_test_chapter.xml134
-rw-r--r--lib/common_test/include/ct.hrl15
-rw-r--r--lib/common_test/priv/Makefile.in10
-rw-r--r--lib/common_test/priv/ct_default.css17
-rw-r--r--lib/common_test/priv/jquery-latest.js154
-rw-r--r--lib/common_test/priv/jquery.tablesorter.min.js4
-rw-r--r--lib/common_test/src/Makefile8
-rw-r--r--lib/common_test/src/common_test.app.src3
-rw-r--r--lib/common_test/src/ct.erl566
-rw-r--r--lib/common_test/src/ct_config.erl258
-rw-r--r--lib/common_test/src/ct_conn_log_h.erl230
-rw-r--r--lib/common_test/src/ct_event.erl18
-rw-r--r--lib/common_test/src/ct_framework.erl124
-rw-r--r--lib/common_test/src/ct_ftp.erl8
-rw-r--r--lib/common_test/src/ct_gen_conn.erl256
-rw-r--r--lib/common_test/src/ct_hooks.erl9
-rw-r--r--lib/common_test/src/ct_logs.erl500
-rw-r--r--lib/common_test/src/ct_master.erl5
-rw-r--r--lib/common_test/src/ct_master_logs.erl145
-rw-r--r--lib/common_test/src/ct_netconfc.erl1835
-rw-r--r--lib/common_test/src/ct_netconfc.hrl58
-rw-r--r--lib/common_test/src/ct_repeat.erl88
-rw-r--r--lib/common_test/src/ct_run.erl767
-rw-r--r--lib/common_test/src/ct_snmp.erl65
-rw-r--r--lib/common_test/src/ct_ssh.erl292
-rw-r--r--lib/common_test/src/ct_telnet.erl4
-rw-r--r--lib/common_test/src/ct_testspec.erl943
-rw-r--r--lib/common_test/src/ct_util.erl105
-rw-r--r--lib/common_test/src/ct_util.hrl14
-rw-r--r--lib/common_test/src/cth_conn_log.erl124
-rw-r--r--lib/common_test/src/cth_surefire.erl66
-rw-r--r--lib/common_test/test/Makefile10
-rw-r--r--lib/common_test/test/common_test.spec2
-rw-r--r--lib/common_test/test/ct_auto_compile_SUITE.erl187
-rw-r--r--lib/common_test/test/ct_auto_compile_SUITE_data/bad_SUITE.erl (renamed from erts/test/autoimport_SUITE_data/dummy.txt)10
-rw-r--r--lib/common_test/test/ct_auto_compile_SUITE_data/dummy_SUITE.erl130
-rw-r--r--lib/common_test/test/ct_basic_html_SUITE.erl180
-rw-r--r--lib/common_test/test/ct_basic_html_SUITE_data/babbling_SUITE.erl130
-rw-r--r--lib/common_test/test/ct_config_SUITE.erl114
-rw-r--r--lib/common_test/test/ct_config_SUITE_data/config/config.txt3
-rw-r--r--lib/common_test/test/ct_config_SUITE_data/config/config.xml1
-rw-r--r--lib/common_test/test/ct_config_SUITE_data/config/shadow.txt12
-rw-r--r--lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl92
-rw-r--r--lib/common_test/test/ct_error_SUITE.erl6
-rw-r--r--lib/common_test/test/ct_misc_1_SUITE.erl4
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE.erl124
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg6
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl1132
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/ns.erl555
-rw-r--r--lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl26
-rw-r--r--lib/common_test/test/ct_shell_SUITE.erl133
-rw-r--r--lib/common_test/test/ct_shell_SUITE_data/cfgdata2
-rw-r--r--lib/common_test/test/ct_snmp_SUITE.erl141
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp.cfg20
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp1_SUITE.erl152
-rw-r--r--lib/common_test/test/ct_system_error_SUITE.erl132
-rw-r--r--lib/common_test/test/ct_system_error_SUITE_data/a_SUITE.erl122
-rw-r--r--lib/common_test/test/ct_test_support.erl50
-rw-r--r--lib/common_test/test/ct_testspec_1_SUITE.erl114
-rw-r--r--lib/common_test/test/ct_testspec_2_SUITE.erl759
-rw-r--r--lib/common_test/test/ct_verbosity_SUITE.erl244
-rw-r--r--lib/common_test/test/ct_verbosity_SUITE_data/io_test_SUITE.erl156
-rw-r--r--lib/common_test/vsn.mk2
-rw-r--r--lib/compiler/doc/src/Makefile2
-rw-r--r--lib/compiler/doc/src/compile.xml8
-rw-r--r--lib/compiler/doc/src/notes.xml78
-rw-r--r--lib/compiler/src/Makefile2
-rw-r--r--lib/compiler/src/beam_type.erl17
-rw-r--r--lib/compiler/src/beam_utils.erl5
-rw-r--r--lib/compiler/src/beam_validator.erl18
-rw-r--r--lib/compiler/src/compile.erl30
-rw-r--r--lib/compiler/src/sys_pre_expand.erl7
-rw-r--r--lib/compiler/src/v3_core.erl7
-rw-r--r--lib/compiler/src/v3_kernel.erl7
-rw-r--r--lib/compiler/test/bs_construct_SUITE.erl6
-rw-r--r--lib/compiler/test/compile_SUITE.erl41
-rw-r--r--lib/compiler/test/compile_SUITE_data/simple-phony.mk3
-rw-r--r--lib/compiler/test/error_SUITE.erl2
-rw-r--r--lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S8
-rw-r--r--lib/compiler/test/misc_SUITE.erl11
-rw-r--r--lib/compiler/vsn.mk2
-rw-r--r--lib/cosEvent/doc/src/Makefile2
-rw-r--r--lib/cosEvent/src/Makefile2
-rw-r--r--lib/cosEvent/test/Makefile2
-rw-r--r--lib/cosEventDomain/doc/src/Makefile2
-rw-r--r--lib/cosEventDomain/src/Makefile2
-rw-r--r--lib/cosEventDomain/test/Makefile2
-rw-r--r--lib/cosFileTransfer/doc/src/Makefile2
-rw-r--r--lib/cosFileTransfer/src/Makefile2
-rw-r--r--lib/cosFileTransfer/test/Makefile2
-rw-r--r--lib/cosFileTransfer/test/fileTransfer_SUITE.erl43
-rw-r--r--lib/cosNotification/doc/src/Makefile2
-rw-r--r--lib/cosNotification/src/Makefile2
-rw-r--r--lib/cosNotification/test/Makefile2
-rw-r--r--lib/cosProperty/doc/src/Makefile2
-rw-r--r--lib/cosProperty/src/Makefile2
-rw-r--r--lib/cosProperty/test/Makefile2
-rw-r--r--lib/cosTime/doc/src/Makefile2
-rw-r--r--lib/cosTime/src/Makefile2
-rw-r--r--lib/cosTime/test/Makefile2
-rw-r--r--lib/cosTransactions/doc/src/Makefile2
-rw-r--r--lib/cosTransactions/examples/Makefile2
-rw-r--r--lib/cosTransactions/src/Makefile2
-rw-r--r--lib/cosTransactions/test/Makefile2
-rw-r--r--lib/crypto/c_src/Makefile.in2
-rw-r--r--lib/crypto/c_src/crypto.c796
-rw-r--r--lib/crypto/doc/src/crypto.xml163
-rw-r--r--lib/crypto/doc/src/notes.xml47
-rw-r--r--lib/crypto/src/Makefile2
-rw-r--r--lib/crypto/src/crypto.erl234
-rw-r--r--lib/crypto/test/crypto_SUITE.erl429
-rw-r--r--lib/crypto/vsn.mk2
-rw-r--r--lib/debugger/doc/src/Makefile2
-rw-r--r--lib/debugger/doc/src/notes.xml2
-rw-r--r--lib/debugger/priv/Makefile2
-rw-r--r--lib/debugger/src/Makefile2
-rw-r--r--lib/debugger/test/Makefile2
-rw-r--r--lib/debugger/test/erl_eval_SUITE.erl34
-rw-r--r--lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl6
-rw-r--r--lib/dialyzer/doc/src/notes.xml60
-rw-r--r--lib/dialyzer/src/dialyzer.erl4
-rw-r--r--lib/dialyzer/src/dialyzer.hrl9
-rw-r--r--lib/dialyzer/src/dialyzer_analysis_callgraph.erl21
-rw-r--r--lib/dialyzer/src/dialyzer_cl.erl5
-rw-r--r--lib/dialyzer/src/dialyzer_cl_parse.erl9
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl2
-rw-r--r--lib/dialyzer/src/dialyzer_coordinator.erl7
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl16
-rw-r--r--lib/dialyzer/src/dialyzer_gui_wx.erl324
-rw-r--r--lib/dialyzer/src/dialyzer_options.erl14
-rw-r--r--lib/dialyzer/src/dialyzer_succ_typings.erl48
-rw-r--r--lib/dialyzer/src/dialyzer_typesig.erl594
-rw-r--r--lib/dialyzer/src/dialyzer_utils.erl2
-rw-r--r--lib/dialyzer/src/dialyzer_worker.erl2
-rw-r--r--lib/dialyzer/test/r9c_SUITE_data/results/asn14
-rw-r--r--lib/dialyzer/test/r9c_SUITE_data/results/inets16
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/fun_ref_match2
-rw-r--r--lib/dialyzer/test/user_SUITE_data/results/wsp_pdu2
-rw-r--r--lib/dialyzer/vsn.mk2
-rwxr-xr-xlib/diameter/autoconf/configure.vxworks147
-rw-r--r--lib/diameter/autoconf/vxworks/sed.general124
-rw-r--r--lib/diameter/autoconf/vxworks/sed.vxworks_cpu3245
-rw-r--r--lib/diameter/autoconf/vxworks/sed.vxworks_ppc3252
-rw-r--r--lib/diameter/autoconf/vxworks/sed.vxworks_ppc60351
-rw-r--r--lib/diameter/autoconf/vxworks/sed.vxworks_ppc603_nolongcall51
-rw-r--r--lib/diameter/autoconf/vxworks/sed.vxworks_ppc86050
-rw-r--r--lib/diameter/autoconf/vxworks/sed.vxworks_simlinux59
-rw-r--r--lib/diameter/autoconf/vxworks/sed.vxworks_simso64
-rw-r--r--lib/diameter/autoconf/vxworks/sed.vxworks_sparc38
-rw-r--r--lib/diameter/doc/src/Makefile2
-rw-r--r--lib/diameter/doc/src/diameter.xml922
-rw-r--r--lib/diameter/doc/src/diameter_app.xml148
-rw-r--r--lib/diameter/doc/src/diameter_compile.xml45
-rw-r--r--lib/diameter/doc/src/diameter_dict.xml10
-rw-r--r--lib/diameter/doc/src/diameter_examples.xml3
-rw-r--r--lib/diameter/doc/src/diameter_intro.xml27
-rw-r--r--lib/diameter/doc/src/diameter_sctp.xml52
-rw-r--r--lib/diameter/doc/src/diameter_tcp.xml53
-rw-r--r--lib/diameter/doc/src/diameter_transport.xml68
-rw-r--r--lib/diameter/doc/src/diameter_using.xml4
-rw-r--r--lib/diameter/doc/src/notes.xml100
-rw-r--r--lib/diameter/examples/code/peer.erl5
-rw-r--r--lib/diameter/include/diameter.hrl5
-rw-r--r--lib/diameter/src/.gitignore1
-rw-r--r--lib/diameter/src/Makefile30
-rw-r--r--lib/diameter/src/base/diameter.appup.src25
-rw-r--r--lib/diameter/src/base/diameter.erl10
-rw-r--r--lib/diameter/src/base/diameter_codec.erl11
-rw-r--r--lib/diameter/src/base/diameter_config.erl5
-rw-r--r--lib/diameter/src/base/diameter_peer.erl143
-rw-r--r--lib/diameter/src/base/diameter_peer_fsm.erl176
-rw-r--r--lib/diameter/src/base/diameter_service.erl542
-rw-r--r--lib/diameter/src/base/diameter_stats.erl265
-rw-r--r--lib/diameter/src/base/diameter_watchdog.erl47
-rw-r--r--lib/diameter/src/modules.mk3
-rw-r--r--lib/diameter/src/transport/diameter_etcp.erl22
-rw-r--r--lib/diameter/src/transport/diameter_sctp.erl129
-rw-r--r--lib/diameter/src/transport/diameter_tcp.erl79
-rw-r--r--lib/diameter/src/transport/diameter_transport.erl55
-rw-r--r--lib/diameter/test/Makefile9
-rw-r--r--lib/diameter/test/diameter_capx_SUITE.erl6
-rw-r--r--lib/diameter/test/diameter_compiler_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_ct.erl13
-rw-r--r--lib/diameter/test/diameter_dict_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_failover_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_gen_sctp_SUITE.erl19
-rw-r--r--lib/diameter/test/diameter_reg_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_relay_SUITE.erl11
-rw-r--r--lib/diameter/test/diameter_stats_SUITE.erl94
-rw-r--r--lib/diameter/test/diameter_sync_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_tls_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl137
-rw-r--r--lib/diameter/test/diameter_util.erl37
-rw-r--r--lib/diameter/vsn.mk4
-rw-r--r--lib/edoc/doc/src/notes.xml19
-rw-r--r--lib/edoc/src/edoc_data.erl5
-rw-r--r--lib/edoc/src/edoc_lib.erl4
-rw-r--r--lib/edoc/src/edoc_parser.yrl4
-rw-r--r--lib/edoc/vsn.mk2
-rw-r--r--lib/erl_docgen/doc/src/Makefile2
-rw-r--r--lib/erl_docgen/doc/src/notes.xml19
-rw-r--r--lib/erl_docgen/priv/bin/Makefile2
-rw-r--r--lib/erl_docgen/priv/css/Makefile2
-rw-r--r--lib/erl_docgen/priv/dtd/Makefile2
-rw-r--r--lib/erl_docgen/priv/dtd_html_entities/Makefile2
-rw-r--r--lib/erl_docgen/priv/dtd_man_entities/Makefile2
-rw-r--r--lib/erl_docgen/priv/images/Makefile2
-rw-r--r--lib/erl_docgen/priv/js/flipmenu/Makefile2
-rw-r--r--lib/erl_docgen/priv/xsl/Makefile2
-rw-r--r--lib/erl_docgen/priv/xsl/db_html.xsl21
-rw-r--r--lib/erl_docgen/priv/xsl/db_man.xsl26
-rw-r--r--lib/erl_docgen/priv/xsl/db_pdf.xsl20
-rw-r--r--lib/erl_docgen/src/Makefile2
-rw-r--r--lib/erl_docgen/src/docgen_edoc_xml_cb.erl14
-rw-r--r--lib/erl_docgen/vsn.mk2
-rw-r--r--lib/erl_interface/aclocal.m4108
-rw-r--r--lib/erl_interface/doc/src/Makefile2
-rw-r--r--lib/erl_interface/doc/src/notes.xml19
-rw-r--r--lib/erl_interface/src/Makefile.in2
-rw-r--r--lib/erl_interface/test/Makefile2
-rw-r--r--lib/erl_interface/test/all_SUITE_data/Makefile.src2
-rw-r--r--lib/erl_interface/test/all_SUITE_data/gccifier.c2
-rw-r--r--lib/erl_interface/test/all_SUITE_data/init_tc.erl2
-rw-r--r--lib/erl_interface/test/ei_tmo_SUITE.erl2
-rw-r--r--lib/erl_interface/test/erl_match_SUITE.erl2
-rw-r--r--lib/erl_interface/vsn.mk2
-rw-r--r--lib/et/doc/src/Makefile2
-rw-r--r--lib/et/doc/src/et_intro.xml2
-rw-r--r--lib/et/doc/src/notes.xml17
-rw-r--r--lib/et/examples/Makefile2
-rw-r--r--lib/et/src/Makefile2
-rw-r--r--lib/et/src/et_wx_contents_viewer.erl23
-rw-r--r--lib/et/test/Makefile2
-rw-r--r--lib/et/vsn.mk2
-rw-r--r--lib/eunit/doc/overview.edoc8
-rw-r--r--lib/eunit/doc/src/notes.xml32
-rw-r--r--lib/eunit/include/eunit.hrl13
-rw-r--r--lib/eunit/src/eunit.app.src1
-rw-r--r--lib/eunit/src/eunit.erl7
-rw-r--r--lib/eunit/src/eunit_data.erl39
-rw-r--r--lib/eunit/src/eunit_lib.erl94
-rw-r--r--lib/eunit/src/eunit_surefire.erl32
-rw-r--r--lib/eunit/src/eunit_test.erl26
-rw-r--r--lib/eunit/src/eunit_tty.erl59
-rw-r--r--lib/eunit/test/Makefile2
-rw-r--r--lib/eunit/vsn.mk2
-rw-r--r--lib/gs/contribs/bonk/Makefile2
-rw-r--r--lib/gs/contribs/cols/Makefile2
-rw-r--r--lib/gs/contribs/mandel/Makefile2
-rw-r--r--lib/gs/contribs/othello/Makefile2
-rw-r--r--lib/gs/doc/src/Makefile2
-rw-r--r--lib/gs/doc/src/notes.xml2
-rw-r--r--lib/gs/examples/Makefile2
-rw-r--r--lib/gs/src/Makefile2
-rw-r--r--lib/gs/tcl/Makefile.in2
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl2576
-rw-r--r--lib/hipe/cerl/erl_types.erl16
-rw-r--r--lib/hipe/doc/src/notes.xml46
-rw-r--r--lib/hipe/icode/hipe_beam_to_icode.erl69
-rw-r--r--lib/hipe/icode/hipe_icode.hrl8
-rw-r--r--lib/hipe/icode/hipe_icode_primops.erl8
-rw-r--r--lib/hipe/icode/hipe_icode_type.erl6
-rw-r--r--lib/hipe/misc/Makefile2
-rw-r--r--lib/hipe/rtl/hipe_rtl_primops.erl2
-rw-r--r--lib/hipe/sparc/hipe_rtl_to_sparc.erl38
-rw-r--r--lib/hipe/tools/Makefile2
-rw-r--r--lib/hipe/vsn.mk2
-rw-r--r--lib/ic/c_src/Makefile.in2
-rw-r--r--lib/ic/doc/src/Makefile2
-rw-r--r--lib/ic/doc/src/ic_clib.xml14
-rw-r--r--lib/ic/doc/src/notes.xml26
-rw-r--r--lib/ic/examples/pre_post_condition/Makefile2
-rw-r--r--lib/ic/java_src/com/ericsson/otp/ic/Makefile2
-rw-r--r--lib/ic/src/Makefile2
-rw-r--r--lib/ic/src/ic.erl2
-rw-r--r--lib/ic/src/ic_pp.erl2
-rw-r--r--lib/ic/test/Makefile2
-rw-r--r--lib/ic/test/java_client_erl_server_SUITE.erl2
-rw-r--r--lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src5
-rw-r--r--lib/ic/vsn.mk2
-rw-r--r--lib/inets/doc/src/httpc.xml143
-rw-r--r--lib/inets/doc/src/notes.xml140
-rw-r--r--lib/inets/examples/httpd_load_test/Makefile2
-rw-r--r--lib/inets/examples/server_root/Makefile2
-rw-r--r--lib/inets/priv/Makefile2
-rw-r--r--lib/inets/src/ftp/Makefile2
-rw-r--r--lib/inets/src/http_client/Makefile2
-rw-r--r--lib/inets/src/http_client/httpc.erl26
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl29
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl197
-rw-r--r--lib/inets/src/http_lib/Makefile2
-rw-r--r--lib/inets/src/http_server/httpd_log.erl12
-rw-r--r--lib/inets/src/inets_app/Makefile2
-rw-r--r--lib/inets/src/inets_app/inets.appup.src82
-rw-r--r--lib/inets/src/inets_app/inets.mk6
-rw-r--r--lib/inets/src/inets_app/inets_service.erl17
-rw-r--r--lib/inets/src/tftp/Makefile2
-rw-r--r--lib/inets/src/tftp/tftp.erl18
-rw-r--r--lib/inets/test/Makefile2
-rw-r--r--lib/inets/test/httpc_SUITE.erl230
-rw-r--r--lib/inets/test/httpd_SUITE.erl31
-rw-r--r--lib/inets/test/httpd_mod.erl237
-rw-r--r--lib/inets/test/inets.spec.vxworks5
-rw-r--r--lib/inets/test/inets_test_lib.erl39
-rw-r--r--lib/inets/test/rules.mk7
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/inviso/AUTHORS4
-rw-r--r--lib/inviso/Makefile37
-rw-r--r--lib/inviso/doc/html/.gitignore0
-rw-r--r--lib/inviso/doc/man3/.gitignore0
-rw-r--r--lib/inviso/doc/pdf/.gitignore0
-rw-r--r--lib/inviso/doc/src/Makefile122
-rw-r--r--lib/inviso/doc/src/book.xml48
-rw-r--r--lib/inviso/doc/src/fascicules.xml18
-rw-r--r--lib/inviso/doc/src/inviso.xml693
-rw-r--r--lib/inviso/doc/src/inviso_as_lib.xml94
-rw-r--r--lib/inviso/doc/src/inviso_chapter.xml362
-rw-r--r--lib/inviso/doc/src/inviso_lfm.xml93
-rw-r--r--lib/inviso/doc/src/inviso_lfm_tpfreader.xml83
-rw-r--r--lib/inviso/doc/src/inviso_rt.xml246
-rw-r--r--lib/inviso/doc/src/inviso_rt_meta.xml78
-rw-r--r--lib/inviso/doc/src/inviso_users_guide_pic1.gifbin13514 -> 0 bytes
-rw-r--r--lib/inviso/doc/src/inviso_users_guide_pic1.ps24489
-rw-r--r--lib/inviso/doc/src/notes.xml278
-rw-r--r--lib/inviso/doc/src/part_notes.xml35
-rw-r--r--lib/inviso/doc/src/ref_man.xml40
-rw-r--r--lib/inviso/ebin/.gitignore0
-rw-r--r--lib/inviso/include/.gitignore0
-rw-r--r--lib/inviso/info2
-rw-r--r--lib/inviso/priv0
-rw-r--r--lib/inviso/src/Makefile104
-rw-r--r--lib/inviso/src/inviso.app.src13
-rw-r--r--lib/inviso/src/inviso.appup.src1
-rw-r--r--lib/inviso/src/inviso.erl1056
-rw-r--r--lib/inviso/src/inviso_c.erl1335
-rw-r--r--lib/inviso/src/inviso_lfm.erl431
-rw-r--r--lib/inviso/src/inviso_lfm_tpfreader.erl388
-rw-r--r--lib/inviso/src/inviso_tool.erl3255
-rw-r--r--lib/inviso/src/inviso_tool_lib.erl379
-rw-r--r--lib/inviso/src/inviso_tool_sh.erl1749
-rw-r--r--lib/inviso/test/Makefile61
-rw-r--r--lib/inviso/test/inviso.cover2
-rw-r--r--lib/inviso/test/inviso.spec1
-rw-r--r--lib/inviso/test/inviso_tool_SUITE.erl1166
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase1_off.trc12
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase1_on.trc17
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase2_off.trc12
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase2_on.trc16
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase3_on.trc9
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase4_on.trc9
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase5_off.trc11
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase5_on.trc11
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase_def.txt8
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase_init.trc10
-rw-r--r--lib/inviso/vsn.mk1
-rw-r--r--lib/jinterface/doc/src/Makefile2
-rw-r--r--lib/jinterface/doc/src/notes.xml2
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile2
-rw-r--r--lib/jinterface/test/Makefile2
-rw-r--r--lib/jinterface/test/jitu.erl12
-rw-r--r--lib/kernel/doc/src/Makefile2
-rw-r--r--lib/kernel/doc/src/code.xml15
-rw-r--r--lib/kernel/doc/src/erl_ddll.xml159
-rw-r--r--lib/kernel/doc/src/error_logger.xml5
-rw-r--r--lib/kernel/doc/src/file.xml6
-rw-r--r--lib/kernel/doc/src/gen_sctp.xml2
-rw-r--r--lib/kernel/doc/src/gen_udp.xml2
-rw-r--r--lib/kernel/doc/src/global.xml12
-rw-r--r--lib/kernel/doc/src/inet.xml10
-rw-r--r--lib/kernel/doc/src/notes.xml93
-rw-r--r--lib/kernel/doc/src/os.xml58
-rw-r--r--lib/kernel/doc/src/packages.xml8
-rw-r--r--lib/kernel/examples/uds_dist/c_src/uds_drv.c4
-rw-r--r--lib/kernel/src/Makefile2
-rw-r--r--lib/kernel/src/application.erl5
-rw-r--r--lib/kernel/src/application_controller.erl17
-rw-r--r--lib/kernel/src/code.erl86
-rw-r--r--lib/kernel/src/code_server.erl4
-rw-r--r--lib/kernel/src/disk_log_1.erl2
-rw-r--r--lib/kernel/src/erl_ddll.erl94
-rw-r--r--lib/kernel/src/error_handler.erl2
-rw-r--r--lib/kernel/src/error_logger.erl14
-rw-r--r--lib/kernel/src/erts_debug.erl129
-rw-r--r--lib/kernel/src/file.erl18
-rw-r--r--lib/kernel/src/gen_udp.erl2
-rw-r--r--lib/kernel/src/global.erl12
-rw-r--r--lib/kernel/src/heart.erl6
-rw-r--r--lib/kernel/src/hipe_unified_loader.erl62
-rw-r--r--lib/kernel/src/inet.erl58
-rw-r--r--lib/kernel/src/inet_config.erl59
-rw-r--r--lib/kernel/src/inet_int.hrl7
-rw-r--r--lib/kernel/src/inet_parse.erl16
-rw-r--r--lib/kernel/src/kernel.appup.src12
-rw-r--r--lib/kernel/src/net_kernel.erl13
-rw-r--r--lib/kernel/src/os.erl79
-rw-r--r--lib/kernel/src/rpc.erl4
-rw-r--r--lib/kernel/test/Makefile2
-rw-r--r--lib/kernel/test/bif_SUITE.erl2
-rw-r--r--lib/kernel/test/code_SUITE.erl90
-rw-r--r--lib/kernel/test/code_SUITE_data/other.erl38
-rw-r--r--lib/kernel/test/code_SUITE_data/upgrade_client.erl259
-rw-r--r--lib/kernel/test/code_SUITE_data/upgradee.erl123
-rw-r--r--lib/kernel/test/disk_log_SUITE.erl43
-rw-r--r--lib/kernel/test/disk_log_SUITE_data/Makefile.src15
-rw-r--r--lib/kernel/test/disk_log_SUITE_data/nfs_check.c46
-rw-r--r--lib/kernel/test/erl_prim_loader_SUITE.erl92
-rw-r--r--lib/kernel/test/file_SUITE.erl556
-rw-r--r--lib/kernel/test/file_name_SUITE.erl54
-rw-r--r--lib/kernel/test/gen_sctp_SUITE.erl200
-rw-r--r--lib/kernel/test/gen_tcp_echo_SUITE.erl25
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl122
-rw-r--r--lib/kernel/test/heart_SUITE.erl17
-rw-r--r--lib/kernel/test/heart_SUITE_data/simple_echo.c5
-rw-r--r--lib/kernel/test/inet_SUITE.erl19
-rw-r--r--lib/kernel/test/inet_sockopt_SUITE.erl2
-rw-r--r--lib/kernel/test/inet_sockopt_SUITE_data/sockopt_helper.c11
-rw-r--r--lib/kernel/test/init_SUITE.erl153
-rw-r--r--lib/kernel/test/interactive_shell_SUITE.erl2
-rw-r--r--lib/kernel/test/kernel.spec.wxworks63
-rw-r--r--lib/kernel/test/os_SUITE.erl2
-rw-r--r--lib/kernel/test/pdict_SUITE.erl1
-rw-r--r--lib/kernel/test/prim_file_SUITE.erl217
-rw-r--r--lib/kernel/vsn.mk2
-rw-r--r--lib/megaco/Makefile2
-rw-r--r--lib/megaco/configure.in2
-rw-r--r--lib/megaco/doc/src/Makefile2
-rw-r--r--lib/megaco/doc/src/notes.xml72
-rw-r--r--lib/megaco/examples/meas/Makefile.in2
-rw-r--r--lib/megaco/examples/simple/Makefile2
-rw-r--r--lib/megaco/src/app/Makefile2
-rw-r--r--lib/megaco/src/app/megaco.appup.src11
-rw-r--r--lib/megaco/src/binary/Makefile2
-rw-r--r--lib/megaco/src/engine/Makefile2
-rw-r--r--lib/megaco/src/engine/modules.mk2
-rw-r--r--lib/megaco/src/flex/Makefile.in11
-rw-r--r--lib/megaco/src/rules.mk4
-rw-r--r--lib/megaco/src/tcp/Makefile2
-rw-r--r--lib/megaco/src/text/Makefile2
-rw-r--r--lib/megaco/src/udp/Makefile2
-rw-r--r--lib/megaco/test/Makefile2
-rw-r--r--lib/megaco/test/megaco.spec.vxworks5
-rw-r--r--lib/megaco/vsn.mk2
-rw-r--r--lib/mnesia/doc/src/Makefile2
-rw-r--r--lib/mnesia/doc/src/notes.xml34
-rw-r--r--lib/mnesia/examples/Makefile2
-rw-r--r--lib/mnesia/examples/mnesia_tpcb.erl56
-rw-r--r--lib/mnesia/include/Makefile2
-rw-r--r--lib/mnesia/src/Makefile2
-rw-r--r--lib/mnesia/src/mnesia.appup.src6
-rw-r--r--lib/mnesia/src/mnesia_index.erl8
-rw-r--r--lib/mnesia/test/Makefile5
-rw-r--r--lib/mnesia/test/mnesia.spec.vxworks362
-rw-r--r--lib/mnesia/test/mnesia_bench.spec1
-rw-r--r--lib/mnesia/test/mnesia_bench_SUITE.erl69
-rw-r--r--lib/mnesia/test/mnesia_consistency_test.erl2
-rw-r--r--lib/mnesia/test/mnesia_dirty_access_test.erl5
-rw-r--r--lib/mnesia/test/mnesia_recovery_test.erl92
-rw-r--r--lib/mnesia/test/mnesia_test_lib.erl16
-rw-r--r--lib/mnesia/test/mnesia_trans_access_test.erl6
-rw-r--r--lib/mnesia/vsn.mk2
-rw-r--r--lib/observer/doc/src/notes.xml69
-rw-r--r--lib/observer/src/observer.app.src14
-rw-r--r--lib/observer/src/observer_app_wx.erl10
-rw-r--r--lib/observer/src/observer_lib.erl54
-rw-r--r--lib/observer/src/observer_perf_wx.erl2
-rw-r--r--lib/observer/src/observer_pro_wx.erl3
-rw-r--r--lib/observer/src/observer_procinfo.erl10
-rw-r--r--lib/observer/src/observer_sys_wx.erl4
-rw-r--r--lib/observer/src/observer_trace_wx.erl4
-rw-r--r--lib/observer/src/observer_tv_table.erl64
-rw-r--r--lib/observer/src/observer_wx.erl17
-rw-r--r--lib/observer/vsn.mk2
-rw-r--r--lib/odbc/aclocal.m4108
-rw-r--r--lib/odbc/c_src/Makefile.in2
-rw-r--r--lib/odbc/c_src/odbcserver.c60
-rw-r--r--lib/odbc/c_src/odbcserver.h2
-rw-r--r--lib/odbc/doc/src/Makefile2
-rw-r--r--lib/odbc/doc/src/notes.xml22
-rw-r--r--lib/odbc/src/Makefile2
-rw-r--r--lib/odbc/src/odbc.appup.src8
-rw-r--r--lib/odbc/src/odbc.erl33
-rw-r--r--lib/odbc/test/Makefile2
-rw-r--r--lib/odbc/test/odbc_test_lib.erl3
-rw-r--r--lib/odbc/vsn.mk2
-rw-r--r--lib/orber/COSS/CosNaming/Makefile2
-rw-r--r--lib/orber/c_src/Makefile.in16
-rw-r--r--lib/orber/doc/src/Makefile2
-rw-r--r--lib/orber/doc/src/notes.xml2
-rw-r--r--lib/orber/examples/Stack/Makefile2
-rw-r--r--lib/orber/java_src/Orber/Makefile2
-rw-r--r--lib/orber/priv/Makefile2
-rw-r--r--lib/orber/src/Makefile2
-rw-r--r--lib/orber/test/Makefile2
-rw-r--r--lib/orber/test/csiv2_SUITE.erl104
-rw-r--r--lib/orber/test/orber_test_lib.erl26
-rw-r--r--lib/os_mon/c_src/Makefile.in10
-rw-r--r--lib/os_mon/c_src/cpu_sup.c16
-rw-r--r--lib/os_mon/c_src/memsup.c96
-rw-r--r--lib/os_mon/c_src/win32sysinfo.c32
-rw-r--r--lib/os_mon/doc/src/Makefile2
-rw-r--r--lib/os_mon/doc/src/notes.xml23
-rw-r--r--lib/os_mon/mibs/Makefile2
-rw-r--r--lib/os_mon/src/Makefile2
-rw-r--r--lib/os_mon/src/disksup.erl2
-rw-r--r--lib/os_mon/src/memsup.erl4
-rw-r--r--lib/os_mon/src/os_mon.erl2
-rw-r--r--lib/os_mon/test/Makefile2
-rw-r--r--lib/os_mon/vsn.mk2
-rw-r--r--lib/otp_mibs/doc/src/Makefile2
-rw-r--r--lib/otp_mibs/mibs/Makefile2
-rw-r--r--lib/otp_mibs/src/Makefile2
-rw-r--r--lib/parsetools/doc/src/Makefile2
-rw-r--r--lib/parsetools/src/Makefile2
-rw-r--r--lib/parsetools/test/Makefile2
-rw-r--r--lib/percept/doc/src/Makefile2
-rw-r--r--lib/percept/priv/Makefile2
-rw-r--r--lib/percept/src/Makefile2
-rw-r--r--lib/percept/src/percept.app.src30
-rw-r--r--lib/percept/test/Makefile2
-rw-r--r--lib/pman/doc/src/Makefile2
-rw-r--r--lib/pman/src/Makefile2
-rw-r--r--lib/public_key/asn1/Makefile2
-rw-r--r--lib/public_key/asn1/OTP-PKIX.asn118
-rw-r--r--lib/public_key/asn1/PKCS-1.asn13
-rw-r--r--lib/public_key/doc/src/Makefile2
-rw-r--r--lib/public_key/doc/src/notes.xml27
-rw-r--r--lib/public_key/doc/src/public_key.xml25
-rw-r--r--lib/public_key/src/Makefile2
-rw-r--r--lib/public_key/src/pubkey_cert.erl4
-rw-r--r--lib/public_key/src/pubkey_cert_records.erl11
-rw-r--r--lib/public_key/src/pubkey_ssh.erl246
-rw-r--r--lib/public_key/src/public_key.erl81
-rw-r--r--lib/public_key/test/Makefile2
-rw-r--r--lib/public_key/test/public_key_SUITE.erl84
-rw-r--r--lib/public_key/test/public_key_SUITE_data/auth_keys4
-rw-r--r--lib/public_key/test/public_key_SUITE_data/known_hosts5
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys6
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts1
-rw-r--r--lib/public_key/vsn.mk2
-rw-r--r--lib/reltool/doc/src/Makefile2
-rw-r--r--lib/reltool/doc/src/notes.xml25
-rw-r--r--lib/reltool/doc/src/reltool.xml76
-rw-r--r--lib/reltool/doc/src/reltool_usage.xml11
-rw-r--r--lib/reltool/examples/Makefile2
-rw-r--r--lib/reltool/src/Makefile2
-rw-r--r--lib/reltool/src/reltool.hrl3
-rw-r--r--lib/reltool/src/reltool_server.erl92
-rw-r--r--lib/reltool/src/reltool_target.erl149
-rw-r--r--lib/reltool/test/reltool_server_SUITE.erl288
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/Makefile.src11
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app2
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y0.erl5
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-first/ebin/b.app6
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-first/src/b.erl4
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-second/ebin/b.app6
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-second/src/b.erl4
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/ebin/a.app (renamed from lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app)5
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a.erl (renamed from lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl)2
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a_sup.erl (renamed from lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl)0
-rw-r--r--lib/reltool/vsn.mk2
-rw-r--r--lib/runtime_tools/c_src/Makefile.in16
-rw-r--r--lib/runtime_tools/c_src/dtrace_user.d3115
-rw-r--r--lib/runtime_tools/c_src/dyntrace.c1136
-rw-r--r--lib/runtime_tools/c_src/trace_ip_drv.c31
-rw-r--r--lib/runtime_tools/doc/src/Makefile9
-rw-r--r--lib/runtime_tools/doc/src/book.xml3
-rw-r--r--lib/runtime_tools/doc/src/dyntrace.xml2
-rw-r--r--lib/runtime_tools/doc/src/notes.xml48
-rw-r--r--lib/runtime_tools/doc/src/part.xml (renamed from lib/inviso/doc/src/part.xml)19
-rw-r--r--lib/runtime_tools/examples/user-probe-n.d44
-rw-r--r--lib/runtime_tools/examples/user-probe-n.systemtap53
-rw-r--r--lib/runtime_tools/examples/user-probe.systemtap4
-rw-r--r--lib/runtime_tools/src/Makefile6
-rw-r--r--lib/runtime_tools/src/dbg.erl6
-rw-r--r--lib/runtime_tools/src/inviso_as_lib.erl155
-rw-r--r--lib/runtime_tools/src/inviso_autostart.erl201
-rw-r--r--lib/runtime_tools/src/inviso_autostart_server.erl311
-rw-r--r--lib/runtime_tools/src/inviso_rt.erl2885
-rw-r--r--lib/runtime_tools/src/inviso_rt_lib.erl474
-rw-r--r--lib/runtime_tools/src/inviso_rt_meta.erl1207
-rw-r--r--lib/runtime_tools/src/runtime_tools.app.src7
-rw-r--r--lib/runtime_tools/src/runtime_tools_sup.erl12
-rw-r--r--lib/runtime_tools/test/Makefile2
-rw-r--r--lib/runtime_tools/test/dbg_SUITE.erl65
-rw-r--r--lib/runtime_tools/test/inviso_SUITE.erl2838
-rw-r--r--lib/runtime_tools/test/inviso_testmodule1_foo.erl9
-rw-r--r--lib/runtime_tools/test/runtime_tools_SUITE.erl21
-rw-r--r--lib/runtime_tools/vsn.mk2
-rw-r--r--lib/sasl/doc/src/notes.xml2
-rw-r--r--lib/sasl/examples/src/Makefile2
-rw-r--r--lib/sasl/src/Makefile2
-rw-r--r--lib/sasl/src/systools_make.erl40
-rw-r--r--lib/sasl/src/systools_rc.erl40
-rw-r--r--lib/sasl/test/Makefile2
-rw-r--r--lib/sasl/test/installer.erl6
-rw-r--r--lib/sasl/test/rb_SUITE.erl27
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl36
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/Makefile.src12
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/c/c.app2
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app2
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app2
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.app2
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/ebin/b.app2
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.app2
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.0/ebin/many_mods.app12
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.1/ebin/many_mods.app12
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/many_mods-2.0/ebin/many_mods.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-2.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-500.18.7/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.app2
-rw-r--r--lib/sasl/test/systools_rc_SUITE.erl18
-rw-r--r--lib/snmp/doc/src/Makefile2
-rw-r--r--lib/snmp/doc/src/notes.xml92
-rw-r--r--lib/snmp/doc/src/snmp_config.xml3
-rw-r--r--lib/snmp/doc/src/snmpa_network_interface_filter.xml8
-rw-r--r--lib/snmp/doc/src/snmpm.xml14
-rw-r--r--lib/snmp/doc/src/snmpm_network_interface_filter.xml4
-rw-r--r--lib/snmp/examples/ex1/Makefile2
-rw-r--r--lib/snmp/examples/ex2/Makefile2
-rw-r--r--lib/snmp/mibs/Makefile.in2
-rw-r--r--lib/snmp/priv/conf/agent/Makefile2
-rw-r--r--lib/snmp/priv/conf/manager/Makefile2
-rw-r--r--lib/snmp/src/agent/Makefile2
-rw-r--r--lib/snmp/src/agent/snmp_user_based_sm_mib.erl190
-rw-r--r--lib/snmp/src/agent/snmp_view_based_acm_mib.erl113
-rw-r--r--lib/snmp/src/app/Makefile2
-rw-r--r--lib/snmp/src/app/snmp.appup.src49
-rw-r--r--lib/snmp/src/compile/Makefile2
-rw-r--r--lib/snmp/src/manager/Makefile2
-rw-r--r--lib/snmp/src/manager/snmpm.erl2
-rw-r--r--lib/snmp/src/misc/Makefile2
-rw-r--r--lib/snmp/src/misc/snmp_pdus.erl112
-rw-r--r--lib/snmp/test/exp/snmp_agent_bl_test.erl57
-rw-r--r--lib/snmp/test/exp/snmp_agent_ms_test.erl50
-rw-r--r--lib/snmp/test/exp/snmp_agent_mt_test.erl50
-rw-r--r--lib/snmp/test/exp/snmp_agent_v2_test.erl50
-rw-r--r--lib/snmp/test/exp/snmp_agent_v3_test.erl49
-rw-r--r--lib/snmp/test/modules.mk2
-rw-r--r--lib/snmp/test/snmp.spec.vxworks4
-rw-r--r--lib/snmp/test/snmp_agent_test.erl114
-rw-r--r--lib/snmp/test/snmp_agent_test_lib.erl9
-rw-r--r--lib/snmp/test/snmp_pdus_test.erl178
-rw-r--r--lib/snmp/test/snmp_test_lib.erl2
-rw-r--r--lib/snmp/test/snmp_test_mgr.erl1
-rw-r--r--lib/snmp/vsn.mk2
-rw-r--r--lib/ssh/doc/src/Makefile2
-rw-r--r--lib/ssh/doc/src/notes.xml72
-rw-r--r--lib/ssh/doc/src/ssh.xml14
-rw-r--r--lib/ssh/examples/Makefile2
-rw-r--r--lib/ssh/src/ssh.appup.src24
-rw-r--r--lib/ssh/src/ssh.erl35
-rw-r--r--lib/ssh/src/ssh_connection.erl33
-rw-r--r--lib/ssh/src/ssh_connection_sup.erl10
-rw-r--r--lib/ssh/src/ssh_file.erl11
-rw-r--r--lib/ssh/src/ssh_sftpd_file_api.erl62
-rw-r--r--lib/ssh/test/ssh.spec.vxworks3
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl40
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/doc/src/Makefile2
-rw-r--r--lib/ssl/doc/src/notes.xml67
-rw-r--r--lib/ssl/doc/src/ssl.xml60
-rw-r--r--lib/ssl/examples/certs/Makefile2
-rw-r--r--lib/ssl/examples/src/Makefile2
-rw-r--r--lib/ssl/examples/src/client_server.erl24
-rw-r--r--lib/ssl/src/Makefile2
-rw-r--r--lib/ssl/src/ssl.appup.src2
-rw-r--r--lib/ssl/src/ssl.erl143
-rw-r--r--lib/ssl/src/ssl_alert.erl4
-rw-r--r--lib/ssl/src/ssl_alert.hrl4
-rw-r--r--lib/ssl/src/ssl_certificate.erl9
-rw-r--r--lib/ssl/src/ssl_certificate_db.erl14
-rw-r--r--lib/ssl/src/ssl_cipher.erl320
-rw-r--r--lib/ssl/src/ssl_cipher.hrl46
-rw-r--r--lib/ssl/src/ssl_connection.erl420
-rw-r--r--lib/ssl/src/ssl_handshake.erl665
-rw-r--r--lib/ssl/src/ssl_handshake.hrl45
-rw-r--r--lib/ssl/src/ssl_internal.hrl14
-rw-r--r--lib/ssl/src/ssl_manager.erl6
-rw-r--r--lib/ssl/src/ssl_record.erl40
-rw-r--r--lib/ssl/src/ssl_record.hrl13
-rw-r--r--lib/ssl/src/ssl_ssl3.erl54
-rw-r--r--lib/ssl/src/ssl_tls1.erl188
-rw-r--r--lib/ssl/test/Makefile4
-rw-r--r--lib/ssl/test/make_certs.erl19
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl363
-rw-r--r--lib/ssl/test/ssl_cipher_SUITE.erl68
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl5
-rw-r--r--lib/ssl/test/ssl_npn_handshake_SUITE.erl310
-rw-r--r--lib/ssl/test/ssl_npn_hello_SUITE.erl117
-rw-r--r--lib/ssl/test/ssl_packet_SUITE.erl352
-rw-r--r--lib/ssl/test/ssl_payload_SUITE.erl57
-rw-r--r--lib/ssl/test/ssl_session_cache_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_test_lib.erl34
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl882
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/doc/src/Makefile2
-rw-r--r--lib/stdlib/doc/src/binary.xml224
-rw-r--r--lib/stdlib/doc/src/epp.xml2
-rw-r--r--lib/stdlib/doc/src/ets.xml533
-rw-r--r--lib/stdlib/doc/src/filelib.xml9
-rw-r--r--lib/stdlib/doc/src/lists.xml68
-rw-r--r--lib/stdlib/doc/src/math.xml57
-rw-r--r--lib/stdlib/doc/src/notes.xml80
-rw-r--r--lib/stdlib/doc/src/re.xml90
-rw-r--r--lib/stdlib/doc/src/string.xml26
-rw-r--r--lib/stdlib/doc/src/unicode.xml50
-rw-r--r--lib/stdlib/doc/src/unicode_usage.xml10
-rw-r--r--lib/stdlib/examples/erl_id_trans.erl9
-rw-r--r--lib/stdlib/src/Makefile3
-rw-r--r--lib/stdlib/src/binary.erl194
-rw-r--r--lib/stdlib/src/dets.erl151
-rw-r--r--lib/stdlib/src/epp.erl2
-rw-r--r--lib/stdlib/src/erl_lint.erl65
-rw-r--r--lib/stdlib/src/erl_scan.erl2
-rw-r--r--lib/stdlib/src/escript.erl18
-rw-r--r--lib/stdlib/src/ets.erl442
-rw-r--r--lib/stdlib/src/filelib.erl38
-rw-r--r--lib/stdlib/src/filename.erl4
-rw-r--r--lib/stdlib/src/gen_server.erl2
-rw-r--r--lib/stdlib/src/io_lib.erl7
-rw-r--r--lib/stdlib/src/lists.erl65
-rw-r--r--lib/stdlib/src/math.erl112
-rw-r--r--lib/stdlib/src/otp_internal.erl2
-rw-r--r--lib/stdlib/src/qlc_pt.erl4
-rw-r--r--lib/stdlib/src/re.erl66
-rw-r--r--lib/stdlib/src/stdlib.appup.src12
-rw-r--r--lib/stdlib/src/string.erl26
-rw-r--r--lib/stdlib/src/unicode.erl48
-rw-r--r--lib/stdlib/src/zip.erl4
-rw-r--r--lib/stdlib/test/dets_SUITE.erl72
-rw-r--r--lib/stdlib/test/erl_eval_SUITE.erl39
-rw-r--r--lib/stdlib/test/erl_expand_records_SUITE.erl2
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl52
-rw-r--r--lib/stdlib/test/escript_SUITE.erl153
-rw-r--r--lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl19
-rw-r--r--lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl105
-rw-r--r--lib/stdlib/test/ets_SUITE.erl40
-rw-r--r--lib/stdlib/test/filelib_SUITE.erl57
-rw-r--r--lib/stdlib/test/filename_SUITE.erl263
-rw-r--r--lib/stdlib/test/gen_fsm_SUITE.erl17
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl32
-rw-r--r--lib/stdlib/test/id_transform_SUITE.erl11
-rw-r--r--lib/stdlib/test/qlc_SUITE.erl19
-rw-r--r--lib/stdlib/test/shell_SUITE.erl9
-rw-r--r--lib/stdlib/test/stdlib.spec.vxworks8
-rw-r--r--lib/stdlib/test/sys_SUITE.erl88
-rw-r--r--lib/stdlib/test/timer_SUITE.erl8
-rw-r--r--lib/stdlib/vsn.mk2
-rw-r--r--lib/syntax_tools/doc/overview.edoc6
-rw-r--r--lib/syntax_tools/doc/src/Makefile2
-rw-r--r--lib/syntax_tools/doc/src/notes.xml20
-rw-r--r--lib/syntax_tools/src/epp_dodger.erl4
-rw-r--r--lib/syntax_tools/src/erl_comment_scan.erl2
-rw-r--r--lib/syntax_tools/src/erl_prettypr.erl4
-rw-r--r--lib/syntax_tools/src/erl_recomment.erl4
-rw-r--r--lib/syntax_tools/src/erl_syntax.erl1913
-rw-r--r--lib/syntax_tools/src/erl_syntax_lib.erl4
-rw-r--r--lib/syntax_tools/src/erl_tidy.erl4
-rw-r--r--lib/syntax_tools/src/igor.erl4
-rw-r--r--lib/syntax_tools/src/prettypr.erl4
-rw-r--r--lib/syntax_tools/vsn.mk2
-rw-r--r--lib/test_server/doc/src/Makefile2
-rw-r--r--lib/test_server/doc/src/notes.xml74
-rw-r--r--lib/test_server/doc/src/test_server_ctrl.xml8
-rw-r--r--lib/test_server/doc/src/ts.xml31
-rw-r--r--lib/test_server/src/Makefile10
-rw-r--r--lib/test_server/src/configure.in2
-rw-r--r--lib/test_server/src/test_server.erl296
-rw-r--r--lib/test_server/src/test_server_ctrl.erl133
-rw-r--r--lib/test_server/src/test_server_internal.hrl2
-rw-r--r--lib/test_server/src/test_server_node.erl43
-rw-r--r--lib/test_server/src/test_server_sup.erl6
-rw-r--r--lib/test_server/src/ts.erl112
-rw-r--r--lib/test_server/src/ts.hrl2
-rw-r--r--lib/test_server/src/ts_autoconf_vxworks.erl191
-rw-r--r--lib/test_server/src/ts_autoconf_win32.erl2
-rw-r--r--lib/test_server/src/ts_benchmark.erl91
-rw-r--r--lib/test_server/src/ts_erl_config.erl14
-rw-r--r--lib/test_server/src/ts_install.erl8
-rw-r--r--lib/test_server/src/ts_install_cth.erl2
-rw-r--r--lib/test_server/src/ts_lib.erl70
-rw-r--r--lib/test_server/src/ts_make.erl2
-rw-r--r--lib/test_server/src/ts_reports.erl545
-rw-r--r--lib/test_server/src/ts_run.erl56
-rw-r--r--lib/test_server/src/ts_selftest.erl120
-rw-r--r--lib/test_server/src/vxworks_client.erl243
-rw-r--r--lib/test_server/test/Makefile2
-rw-r--r--lib/test_server/test/test_server.cover5
-rw-r--r--lib/test_server/vsn.mk2
-rw-r--r--lib/toolbar/doc/src/Makefile2
-rw-r--r--lib/toolbar/doc/src/notes.xml2
-rw-r--r--lib/toolbar/src/Makefile2
-rw-r--r--lib/tools/doc/src/Makefile2
-rw-r--r--lib/tools/doc/src/eprof.xml2
-rw-r--r--lib/tools/doc/src/notes.xml65
-rw-r--r--lib/tools/doc/src/xref_chapter.xml4
-rw-r--r--lib/tools/emacs/Makefile16
-rw-r--r--lib/tools/emacs/erlang.el37
-rw-r--r--lib/tools/emacs/test.erl.indented7
-rw-r--r--lib/tools/emacs/test.erl.orig7
-rw-r--r--lib/tools/src/Makefile2
-rw-r--r--lib/tools/src/tools.app.src1
-rw-r--r--lib/tools/src/xref_utils.erl4
-rw-r--r--lib/tools/test/Makefile2
-rw-r--r--lib/tools/test/cover_SUITE.erl2
-rw-r--r--lib/tools/test/xref_SUITE.erl16
-rw-r--r--lib/tv/doc/src/Makefile2
-rw-r--r--lib/tv/doc/src/notes.xml2
-rw-r--r--lib/tv/priv/Makefile2
-rw-r--r--lib/tv/src/Makefile2
-rw-r--r--lib/typer/src/Makefile2
-rw-r--r--lib/typer/src/typer.erl4
-rw-r--r--lib/typer/vsn.mk2
-rw-r--r--lib/webtool/src/Makefile2
-rw-r--r--lib/wx/aclocal.m4108
-rw-r--r--lib/wx/api_gen/wx_extra/wxEvtHandler.erl2
-rwxr-xr-xlib/wx/configure.in2
-rw-r--r--lib/wx/doc/src/notes.xml2
-rw-r--r--lib/wx/src/gen/wxEvtHandler.erl2
-rw-r--r--lib/xmerl/doc/src/Makefile2
-rw-r--r--lib/xmerl/doc/src/notes.xml2
-rw-r--r--lib/xmerl/src/xmerl_sax_parser_base.erlsrc2
-rw-r--r--lib/xmerl/test/Makefile2
-rw-r--r--make/emd2exml.in23
-rw-r--r--make/otp.mk.in16
-rw-r--r--make/otp_release_targets.mk2
-rwxr-xr-xotp_build2
-rw-r--r--system/README7
-rw-r--r--system/doc/design_principles/Makefile2
-rw-r--r--system/doc/efficiency_guide/Makefile2
-rw-r--r--system/doc/embedded/Makefile2
-rw-r--r--system/doc/embedded/vxworks.xml193
-rw-r--r--system/doc/embedded/xmlfiles.mk3
-rw-r--r--system/doc/getting_started/Makefile2
-rw-r--r--system/doc/installation_guide/Makefile10
-rw-r--r--system/doc/installation_guide/part.xml4
-rw-r--r--system/doc/installation_guide/xmlfiles.mk3
-rw-r--r--system/doc/oam/Makefile2
-rw-r--r--system/doc/programming_examples/Makefile2
-rw-r--r--system/doc/reference_manual/Makefile2
-rw-r--r--system/doc/reference_manual/expressions.xml11
-rw-r--r--system/doc/system_architecture_intro/Makefile2
-rw-r--r--system/doc/system_principles/Makefile2
-rw-r--r--system/doc/top/Makefile2
-rw-r--r--system/doc/top/templates/index.html.src15
-rw-r--r--system/doc/tutorial/Makefile2
-rw-r--r--xcomp/README.md6
-rw-r--r--xcomp/erl-xcomp-TileraMDE2.0-tilepro.conf11
-rw-r--r--xcomp/erl-xcomp-avr32-atmel-linux-gnu.conf9
-rw-r--r--xcomp/erl-xcomp-mips-linux.conf11
-rw-r--r--xcomp/erl-xcomp-powerpc-dso-linux-gnu.conf258
-rw-r--r--xcomp/erl-xcomp-vars.sh4
-rw-r--r--xcomp/erl-xcomp-vxworks_ppc32.conf11
-rw-r--r--xcomp/erl-xcomp-x86_64-saf-linux-gnu.conf11
-rw-r--r--xcomp/erl-xcomp.conf.template11
1214 files changed, 51615 insertions, 82950 deletions
diff --git a/.gitignore b/.gitignore
index 0986bb6e4c..3b0e21bde3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -86,6 +86,8 @@ lib/os_mon/priv/obj/win32/
lib/runtime_tools/c_src/win32/
lib/runtime_tools/priv/lib/
lib/runtime_tools/priv/obj/
+lib/runtime_tools/doc/src/DTRACE.xml
+lib/runtime_tools/doc/src/SYSTEMTAP.xml
lib/tools/bin/win32/
lib/tools/c_src/win32/
lib/tools/obj/win32/
@@ -335,6 +337,7 @@ JAVADOC-GENERATED
/system/doc/installation_guide/INSTALL.xml
/system/doc/installation_guide/INSTALL-CROSS.xml
/system/doc/installation_guide/INSTALL-WIN32.xml
+/system/doc/installation_guide/MARKDOWN.xml
# test_server
diff --git a/HOWTO/BENCHMARKS.md b/HOWTO/BENCHMARKS.md
new file mode 100644
index 0000000000..b3df4638ea
--- /dev/null
+++ b/HOWTO/BENCHMARKS.md
@@ -0,0 +1,73 @@
+Benchmarking Erlang/OTP
+=======================
+
+The Erlang/OTP source tree contains a number of benchmarks. The same framework
+is used to run these benchmarks as is used to run tests. Therefore in order to
+run benchmarks you have to [release the tests][] just as you normally would.
+
+Note that many of these benchmarks were developed to test a specific feature
+under a specific setting. We strive to keep the benchmarks up-to-date, but alas
+time is not an endless resource so some benchmarks will be outdated and
+irrelevant.
+
+Running the benchmarks
+----------------------
+
+As with testing, `ts` is used to run the benchmarks. Before running any
+benchmarks you have to [install the tests][]. To get a listing of all
+benchmarks you have available call `ts:benchmarks()`.
+
+To run all benchmarks call `ts:bench()`. This will run all benchmarks using
+the emulator which is in you `$PATH` (Note that this does not have to be the
+same as from which the benchmarks were built from). All the results of the
+benchmarks are put in a folder in `$TESTROOT/test_server/` called
+`YYYY_MO_DDTHH_MI_SS`.
+
+Each benchmark is run multiple times and the data for all runs is collected in
+the files within the benchmark folder. All benchmarks are written so that a
+higher values are better.
+
+Writing benchmarks
+------------------
+
+Benchmarks are just normal testcases in Common Test suites. They are marked as
+benchmarks by being included in the `AppName_bench.spec` which is located in
+`lib/AppName/test/` for the applications which have benchmarks. Note that you
+might want to add a skip clause to `AppName.spec` for the benchmarks if you do
+not want them to be run in the nightly tests.
+
+Results of benchmarks are sent using the ct_event mechanism and automatically
+collected and formatted by ts.
+
+ ct_event:notify(
+ #event{name = benchmark_data,
+ data = [{value,TPS}]}).
+
+The application, suite and testcase associated with the value is automatically
+detected. If you want to supply your own you can include `suite` andor `name`
+with the data. i.e.
+
+ ct_event:notify(
+ #event{name = benchmark_data,
+ data = [{suite,"erts_bench"},
+ {name,"ets_transactions_per_sec"},
+ {value,TPS}]}).
+
+The reason for using the internal ct_event and not ct is because the benchmark
+code has to be backwards compatible with at least R14.
+
+The value which is reported should be as raw as possible. i.e. you should not
+do any averaging of the value before reporting. The tools we use to collect the
+benchmark data over time will do averages, means, stddev and more with the data.
+So the more data which is sent using `ct_event` the better.
+
+Viewing benchmarks
+------------------
+
+At the moment of writing this HOWTO the tool for viewing benchmark results is
+not available as opensource. This will hopefully change in the near future.
+
+
+ [release the tests]: TESTING.md#releasing-tests
+ [install the tests]: TESTING.md#configuring-the-test-environment
+
diff --git a/README.bootstrap b/HOWTO/BOOTSTRAP.md
index f42bc7aa47..59e31165cf 100644
--- a/README.bootstrap
+++ b/HOWTO/BOOTSTRAP.md
@@ -1,16 +1,16 @@
Notes about prebuilt beam files under version control
------------------------------------------------------
+=====================================================
This information applies mostly to developers, some parts only
to developers of the main branch i.e. Ericsson and HiPE personel.
There are two types of derived code under version control, namely:
-primary bootstrap - Resides in the $ERL_TOP/bootstrap/{lib,bin} directories.
-preloaded code - Resides in the $ERL_TOP/erts/preloaded directory.
+primary bootstrap - Resides in the `$ERL_TOP/bootstrap/{lib,bin}` directories.
+preloaded code - Resides in the `$ERL_TOP/erts/preloaded` directory.
Primary bootstrap
-.................
+-----------------
The two types of version controlled code are fundamentally
different. The primary bootstrap is code compiled from source files in
@@ -19,7 +19,7 @@ lib/orber/include. They are checked in in the version control system
to make it possible to build directly from the code base tree without
the need for an earlier version of the compiler. When a new version of
OTP is released, these files are updated manually (or rather, by using
-the $ERL_TOP/otp_build script) and checked in. The files can also be
+the `$ERL_TOP/otp_build` script) and checked in. The files can also be
updated due to changes in the compiler during the development
process. The primary bootstrap is always updated as a separate
deliberate process, never during a normal development build.
@@ -27,31 +27,31 @@ deliberate process, never during a normal development build.
If a prebuilt open source version of erlang is used, the directory
bootstrap initially does not contain any beam files, the directory is
instead populated by copying beam files from the
-$ERL_TOP/lib/{kernel,stdlib,compiler}/ebin directories. This
+`$ERL_TOP/lib/{kernel,stdlib,compiler}/ebin` directories. This
construction is to save space in the distribution, but the result
would be the same. Open source developers need not provide patches for
the precompiled beam files in the primary bootstrap, the bootstrap
update is always performed by the main developers.
Preloaded code
-..............
+--------------
-The directory $ERL_TOP/preloaded contains both src and ebin
+The directory `$ERL_TOP/preloaded` contains both src and ebin
subdirectories. The preloaded code is compiled into the virtual
machine and always present. When compiling the virtual machine, those
beam files need to be present and they are considered a part of the
virtual machine rather than a part of the kernel application. When
preloaded files are to be updated, the source code is built using a
-special Makefile in the $ERL_TOP/preloaded/src directory, which
+special Makefile in the `$ERL_TOP/preloaded/src` directory, which
creates beam files in the same directory. When they seem to compile
successfully, they can be used in an emulator build by being copied
-to the ebin directory. otp_build update_preloaded can be used to
+to the ebin directory. `otp_build update_preloaded` can be used to
ease the process (there are also similar targets in the
-$ERL_TOP/preloaded/src/Makefile).
+`$ERL_TOP/preloaded/src/Makefile`).
In prebuilt open source distributions, these beam files are also
present, but to update them one might need to change permission on the
-$ERL_TOP/preloaded/ebin directory, then build and then manually copy
+`$ERL_TOP/preloaded/ebin` directory, then build and then manually copy
the beam files from the source directory to ../ebin. If patches are
created that involve the source files used to build preloaded code,
always note this specially as the preloaded/ebin directory needs
diff --git a/README.dtrace.md b/HOWTO/DTRACE.md
index 5bc042f9fc..b719c68c59 100644
--- a/README.dtrace.md
+++ b/HOWTO/DTRACE.md
@@ -5,7 +5,7 @@ History
-------
The first implementation of DTrace probes for the Erlang virtual
-machine was presented at the [2008 Erlang User Conference] [4]. That
+machine was presented at the [2008 Erlang User Conference] [1]. That
work, based on the Erlang/OTP R12 release, was discontinued due to
what appears to be miscommunication with the original developers.
@@ -16,13 +16,13 @@ e.g. `foo_module:dtrace_probe("message goes here!")`.
Goals
-----
-1. Annotate as much of the Erlang VM as is practical.
+* Annotate as much of the Erlang VM as is practical.
* The initial goal is to trace file I/O operations.
-2. Support all platforms that implement DTrace: OS X, Solaris,
- and (I hope) FreeBSD and NetBSD.
-3. To the extent that it's practical, support SystemTap on Linux
- via DTrace provider compatibility.
-4. Allow Erlang code to supply annotations.
+* Support all platforms that implement DTrace: OS X, Solaris,
+ and (I hope) FreeBSD and NetBSD.
+* To the extent that it's practical, support SystemTap on Linux
+ via DTrace provider compatibility.
+* Allow Erlang code to supply annotations.
Supported platforms
-------------------
@@ -33,8 +33,8 @@ Supported platforms
OpenIndiana release 151a, and both appear to work.
* FreeBSD 9.0, though please see the "FreeBSD 9.0 Release Notes"
section below!
-* Linux via SystemTap compatibility. Please see the file
- `README.systemtap.md` for more details.
+* Linux via SystemTap compatibility. Please see
+ [$ERL_TOP/HOWTO/SYSTEMTAP.md][] for more details.
Just add the `--with-dynamic-trace=dtrace` option to your command when you
run the `configure` script. If you are using systemtap, the configure option
@@ -43,10 +43,10 @@ is `--with-dynamic-trace=systemtap`
Status
------
-As of R15B01, the dynamic trace code is included in the main OTP distribution,
+As of R15B01, the dynamic trace code is included in the OTP source distribution,
although it's considered experimental. The main development of the dtrace code
still happens outside of Ericsson, but there is no need to fetch a patched
-version of OTP to get the basic funtionality.
+version of the OTP source to get the basic funtionality.
Implementation summary
----------------------
@@ -66,9 +66,7 @@ following may be executed in a different Pthread:
* `efile_drv` command execution (C code)
* `efile_drv` status return (C code)
-**TODO: keep this description up-to-date.**
-
-Example output from `lib/dtrace/examples/efile_drv.d` while executing
+Example output from `lib/runtime_tools/examples/efile_drv.d` while executing
`file:rename("old-name", "new-name")`:
efile_drv enter tag={3,84} user tag some-user-tag | RENAME (12) | args: old-name new-name , 0 0 (port #Port<0.59>)
@@ -83,7 +81,7 @@ Example output from `lib/dtrace/examples/efile_drv.d` while executing
these two numbers form a unique ID for the I/O operation.
* `12` is the command number for the rename operation. See the
definition for `FILE_RENAME` in the source code file `efile_drv.c`
- or the `BEGIN` section of the D script `lib/dtrace/examples/efile_drv.d`.
+ or the `BEGIN` section of the D script `lib/runtime_tools/examples/efile_drv.d`.
* `old-name` and `new-name` are the two string arguments for the
source and destination of the `rename(2)` system call.
The two integer arguments are unused; the simple formatting code
@@ -101,8 +99,8 @@ So, where does the `some-user-tag` string come from?
At the moment, the user tag comes from code like the following:
- put(dtrace_utag, "some-user-tag"),
- file:rename("old-name", "new-name").
+ dyntrace:put_tag("some-user-tag"),
+ file:rename("old-name", "new-name"),
This method of tagging I/O at the Erlang level is subject to change.
@@ -391,3 +389,4 @@ Guide to efile_drv.c probe arguments
probe arg9 = C driver dt_i4 = advise_type;
[1]: http://www.erlang.org/euc/08/
+ [$ERL_TOP/HOWTO/SYSTEMTAP.md]: SYSTEMTAP.md
diff --git a/HOWTO/INSTALL-CROSS.md b/HOWTO/INSTALL-CROSS.md
new file mode 100644
index 0000000000..8a2b1bc8ad
--- /dev/null
+++ b/HOWTO/INSTALL-CROSS.md
@@ -0,0 +1,560 @@
+Cross Compiling Erlang/OTP
+==========================
+
+Introduction
+------------
+
+This document describes how to cross compile Erlang/OTP-%OTP-REL%. Note that
+the support for cross compiling Erlang/OTP should be considered as
+experimental. As far as we know, the %OTP-REL% release should cross compile
+fine, but since we currently have a very limited set of cross compilation
+environments to test with we cannot be sure. The cross compilation support
+will remain in an experimental state until we get a lot more cross compilation
+environments to test with.
+
+You are advised to read the whole document before attempting to cross
+compile Erlang/OTP. However, before reading this document, you should read
+the [$ERL_TOP/HOWTO/INSTALL.md][] document which describes building and installing
+Erlang/OTP in general. `$ERL_TOP` is the top directory in the source tree.
+
+### otp\_build Versus configure/make ###
+
+Building Erlang/OTP can be done either by using the `$ERL_TOP/otp_build`
+script, or by invoking `$ERL_TOP/configure` and `make` directly. Building using
+`otp_build` is easier since it involves fewer steps, but the `otp_build` build
+procedure is not as flexible as the `configure`/`make` build procedure. Note
+that `otp_build configure` will produce a default configuration that differs
+from what `configure` will produce by default. For example, currently
+`--disable-dynamic-ssl-lib` is added to the `configure` command line arguments
+unless `--enable-dynamic-ssl-lib` has been explicitly passed. The binary
+releases that we deliver are built using `otp_build`. The defaults used by
+`otp_build configure` may change at any time without prior notice.
+
+### Cross Configuration ###
+
+The `$ERL_TOP/xcomp/erl-xcomp.conf.template` file contains all available cross
+configuration variables and can be used as a template when creating a cross
+compilation configuration. All [cross configuration variables][] are also
+listed at the end of this document. For examples of working cross
+configurations see the `$ERL_TOP/xcomp/erl-xcomp-TileraMDE2.0-tilepro.conf`
+file and the `$ERL_TOP/xcomp/erl-xcomp-x86_64-saf-linux-gnu.conf` file. If the
+default behavior of a variable is satisfactory, the variable does not need to
+be set. However, the `configure` script will issue a warning when a default
+value is used. When a variable has been set, no warning will be issued.
+
+A cross configuration file can be passed to `otp_build configure` using the
+`--xcomp-conf` command line argument. Note that `configure` does not accept
+this command line argument. When using the `configure` script directly, pass
+the configuration variables as arguments to `configure` using a
+`<VARIABLE>=<VALUE>` syntax. Variables can also be passed as environment
+variables to `configure`. However, if you pass the configuration in the
+environment, make sure to unset all of these environment variables before
+invoking `make`; otherwise, the environment variables might set make variables
+in some applications, or parts of some applications, and you may end up with
+an erroneously configured build.
+
+### What can be Cross Compiled? ###
+
+All Erlang/OTP applications except the `wx` application can be cross compiled.
+The build of the `wx` driver will currently be automatically disabled when
+cross compiling.
+
+### Compatibility ###
+
+The build system, including cross compilation configuration variables used,
+may be subject to non backward compatible changes without prior notice.
+Current cross build system has been tested when cross compiling some Linux/GNU
+systems, but has only been partly tested for more esoteric platforms. The
+VxWorks example file is highly dependent on our environment and is here more
+or less only for internal use.
+
+### Patches ###
+
+Please submit any patches for cross compiling in a way consistent with this
+system. All input is welcome as we have a very limited set of cross compiling
+environments to test with. If a new configuration variable is needed, add it
+to `$ERL_TOP/xcomp/erl-xcomp.conf.template`, and use it in `configure.in`.
+Other files that might need to be updated are:
+
+- `$ERL_TOP/xcomp/erl-xcomp-vars.sh`
+- `$ERL_TOP/erl-build-tool-vars.sh`
+- `$ERL_TOP/erts/aclocal.m4`
+- `$ERL_TOP/xcomp/README.md`
+- `$ERL_TOP/xcomp/erl-xcomp-*.conf`
+
+Note that this might be an incomplete list of files that need to be updated.
+
+General information on how to submit patches can be found at:
+ <http://wiki.github.com/erlang/otp/submitting-patches>
+
+Build and Install Procedure
+---------------------------
+
+If you are building in Git, you want to read the [Building in Git][] section
+of [$ERL_TOP/HOWTO/INSTALL.md][] before proceeding.
+
+We will first go through the `configure`/`make` build procedure which people
+probably are most familiar with.
+
+### Building With configure/make Directly ###
+
+ (1)
+
+Change directory into the top directory of the Erlang/OTP source tree.
+
+ $ cd $ERL_TOP
+
+In order to compile Erlang code, a small Erlang bootstrap system has to be
+built, or an Erlang/OTP system of the same release as the one being built
+has to be provided in the `$PATH`. The Erlang/OTP for the target system will
+be built using this Erlang system, together with the cross compilation tools
+provided.
+
+If you want to build the documentation out of the same source tree as you are
+cross compiling in, you currently need a full Erlang/OTP system of the same
+release as the one being built for the build machine. If this is the case,
+build and install one for the build machine (or use one already built) and add
+it to the `$PATH` before cross building, and building the documentation. See
+the [How to Build the Documentation][] section in the [$ERL_TOP/HOWTO/INSTALL.md][]
+document for information on how to build the documentation.
+
+If you want to build using a compatible Erlang/OTP system in the `$PATH`,
+jump to (3).
+
+#### Building a Bootstrap System ####
+
+ (2)
+
+ $ ./configure --enable-bootstrap-only
+ $ make
+
+The `--enable-bootstrap-only` argument to `configure` isn't strictly necessary,
+but will speed things up. It will only run `configure` in applications
+necessary for the bootstrap, and will disable a lot of things not needed by
+the bootstrap system. If you run `configure` without `--enable-boostrap-only`
+you also have to run make as `make bootstrap`; otherwise, the whole system will
+be built.
+
+#### Cross Building the System ####
+
+ (3)
+
+ $ ./configure --host=<HOST> --build=<BUILD> [Other Config Args]
+ $ make
+
+`<HOST>` is the host/target system that you build for. It does not have to be
+a full `CPU-VENDOR-OS` triplet, but can be. The full `CPU-VENDOR-OS` triplet
+will be created by executing `$ERL_TOP/erts/autoconf/config.sub <HOST>`. If
+`config.sub` fails, you need to be more specific.
+
+`<BUILD>` should equal the `CPU-VENDOR-OS` triplet of the system that you
+build on. If you execute `$ERL_TOP/erts/autoconf/config.guess`, it will in
+most cases print the triplet you want to use for this.
+
+Pass the cross compilation variables as command line arguments to `configure`
+using a `<VARIABLE>=<VALUE>` syntax.
+
+> *NOTE*: You can *not* pass a configuration file using the `--xcomp-conf`
+> argument when you invoke `configure` directly. The `--xcomp-conf` argument
+> can only be passed to `otp_build configure`.
+
+`make` will verify that the Erlang/OTP system used when building is of the
+same release as the system being built, and will fail if this is not the case.
+It is possible, however not recommended, to force the cross compilation even
+though the wrong Erlang/OTP system is used. This by invoking `make` like this:
+`make ERL_XCOMP_FORCE_DIFFERENT_OTP=yes`.
+
+> *WARNING*: Invoking `make ERL_XCOMP_FORCE_DIFFERENT_OTP=yes` might fail,
+> silently produce suboptimal code, or silently produce erroneous code.
+
+#### Installing ####
+
+You can either install using the installation paths determined by `configure`
+(4), or install manually using (5).
+
+##### Installing Using Paths Determined by configure #####
+
+ (4)
+
+ $ make install DESTDIR=<TEMPORARY_PREFIX>
+
+`make install` will install at a location specified when doing `configure`.
+`configure` arguments specifying where the installation should reside are for
+example: `--prefix`, `--exec-prefix`, `--libdir`, `--bindir`, etc. By default
+it will install under `/usr/local`. You typically do not want to install your
+cross build under `/usr/local` on your build machine. Using [DESTDIR][]
+will cause the installation paths to be prefixed by `$DESTDIR`. This makes it
+possible to install and package the installation on the build machine without
+having to place the installation in the same directory on the build machine as
+it should be executed from on the target machine.
+
+When `make install` has finished, change directory into `$DESTDIR`, package
+the system, move it to the target machine, and unpack it. Note that the
+installation will only be working on the target machine at the location
+determined by `configure`.
+
+##### Installing Manually #####
+
+ (5)
+
+ $ make release RELEASE_ROOT=<RELEASE_DIR>
+
+`make release` will copy what you have built for the target machine to
+`<RELEASE_DIR>`. The `Install` script will not be run. The content of
+`<RELEASE_DIR>` is what by default ends up in `/usr/local/lib/erlang`.
+
+The `Install` script used when installing Erlang/OTP requires common Unix
+tools such as `sed` to be present in your `$PATH`. If your target system
+does not have such tools, you need to run the `Install` script on your
+build machine before packaging Erlang/OTP. The `Install` script should
+currently be invoked as follows in the directory where it resides
+(the top directory):
+
+ $ ./Install [-cross] [-minimal|-sasl] <ERL_ROOT>
+
+where:
+
+* `-minimal` Creates an installation that starts up a minimal amount
+ of applications, i.e., only `kernel` and `stdlib` are started. The
+ minimal system is normally enough, and is what `make install` uses.
+* `-sasl` Creates an installation that also starts up the `sasl`
+ application.
+* `-cross` For cross compilation. Informs the install script that it
+ is run on the build machine.
+* `<ERL_ROOT>` - The absolute path to the Erlang installation to use
+ at run time. This is often the same as the current working directory,
+ but does not have to be. It can follow any other path through the file
+ system to the same directory.
+
+If neither `-minimal`, nor `-sasl` is passed as argument you will be
+prompted.
+
+You can now either do:
+
+ (6)
+
+* Decide where the installation should be located on the target machine,
+ run the `Install` script on the build machine, and package the installed
+ installation. The installation just need to be unpacked at the right
+ location on the target machine:
+
+ $ cd <RELEASE_DIR>
+ $ ./Install -cross [-minimal|-sasl] <ABSOLUTE_INSTALL_DIR_ON_TARGET>
+
+or:
+
+ (7)
+
+* Package the installation in `<RELEASE_DIR>`, place it wherever you want
+ on your target machine, and run the `Install` script on your target
+ machine:
+
+ $ cd <ABSOLUTE_INSTALL_DIR_ON_TARGET>
+ $ ./Install [-minimal|-sasl] <ABSOLUTE_INSTALL_DIR_ON_TARGET>
+
+### Building With the otp\_build Script ###
+
+ (8)
+
+ $ cd $ERL_TOP
+
+ (9)
+
+ $ ./otp_build configure --xcomp-conf=<FILE> [Other Config Args]
+
+alternatively:
+
+ $ ./otp_build configure --host=<HOST> --build=<BUILD> [Other Config Args]
+
+If you have your cross compilation configuration in a file, pass it using the
+`--xcomp-conf=<FILE>` command line argument. If not, pass `--host=<HOST>`,
+`--build=<BUILD>`, and the configuration variables using a `<VARIABLE>=<VALUE>`
+syntax on the command line (same as in (3)). Note that `<HOST>` and `<BUILD>`
+have to be passed one way or the other; either by using `erl_xcomp_host=<HOST>`
+and `erl_xcomp_build=<BUILD>` in the configuration file, or by using the
+`--host=<HOST>`, and `--build=<BUILD>` command line arguments.
+
+`otp_build configure` will configure both for the boostrap system on the
+build machine and the cross host system.
+
+ (10)
+
+ $ ./otp_build boot -a
+
+`otp_build boot -a` will first build a bootstrap system for the build machine
+and then do the cross build of the system.
+
+ (11)
+
+ $ ./otp_build release -a <RELEASE_DIR>
+
+`otp_build release -a` will do the same as (5), and you will after this have
+to do a manual install either by doing (6), or (7).
+
+Testing the cross compiled system
+---------------------------------
+Some of the tests that come with erlang use native code to test. This means
+that when cross compiling erlang you also have to cross compile test suites
+in order to run tests on the target host. To do this you first have to release
+the tests as usual.
+
+ $ make release_tests
+
+or
+
+ $ ./otp_build tests
+
+The tests will be released into `$ERL_TOP/release/tests`. After releasing the
+tests you have to install the tests on the build machine. You supply the same
+xcomp file as to `./otp_build` in (9).
+
+ $ cd $ERL_TOP/release/tests/test_server/
+ $ $ERL_TOP/bootstrap/bin/erl -eval 'ts:install([{xcomp,"<FILE>"}])' -s ts compile_testcases -s init stop
+
+You should get a lot of printouts as the testcases are compiled. Once done you
+should copy the entire `$ERL_TOP/release/tests` folder to the cross host system.
+
+Then go to the cross host system and setup the erlang installed in (4) or (5)
+to be in your `$PATH`. Then go to what previously was
+`$ERL_TOP/release/tests/test_server` and issue the following command.
+
+ $ erl -s ts install -s ts run all_tests -s init stop
+
+The configure should be skipped and all tests should hopefully pass. For more
+details about how to use ts run `erl -s ts help -s init stop`
+
+Currently Used Configuration Variables
+--------------------------------------
+
+Note that you cannot define arbitrary variables in a cross compilation
+configuration file. Only the ones listed below will be guaranteed to be
+visible throughout the whole execution of all `configure` scripts. Other
+variables needs to be defined as arguments to `configure` or exported in
+the environment.
+
+### Variables for otp\_build Only ###
+
+Variables in this section are only used, when configuring Erlang/OTP for
+cross compilation using `$ERL_TOP/otp_build configure`.
+
+> *NOTE*: These variables currently have *no* effect if you configure using
+> the `configure` script directly.
+
+* `erl_xcomp_build` - The build system used. This value will be passed as
+ `--build=$erl_xcomp_build` argument to the `configure` script. It does
+ not have to be a full `CPU-VENDOR-OS` triplet, but can be. The full
+ `CPU-VENDOR-OS` triplet will be created by
+ `$ERL_TOP/erts/autoconf/config.sub $erl_xcomp_build`. If set to `guess`,
+ the build system will be guessed using
+ `$ERL_TOP/erts/autoconf/config.guess`.
+
+* `erl_xcomp_host` - Cross host/target system to build for. This value will
+ be passed as `--host=$erl_xcomp_host` argument to the `configure` script.
+ It does not have to be a full `CPU-VENDOR-OS` triplet, but can be. The
+ full `CPU-VENDOR-OS` triplet will be created by
+ `$ERL_TOP/erts/autoconf/config.sub $erl_xcomp_host`.
+
+* `erl_xcomp_configure_flags` - Extra configure flags to pass to the
+ `configure` script.
+
+### Cross Compiler and Other Tools ###
+
+If the cross compilation tools are prefixed by `<HOST>-` you probably do
+not need to set these variables (where `<HOST>` is what has been passed as
+`--host=<HOST>` argument to `configure`).
+
+All variables in this section can also be used when native compiling.
+
+* `CC` - C compiler.
+
+* `CFLAGS` - C compiler flags.
+
+* `STATIC_CFLAGS` - Static C compiler flags.
+
+* `CFLAG_RUNTIME_LIBRARY_PATH` - This flag should set runtime library
+ search path for the shared libraries. Note that this actually is a
+ linker flag, but it needs to be passed via the compiler.
+
+* `CPP` - C pre-processor.
+
+* `CPPFLAGS` - C pre-processor flags.
+
+* `CXX` - C++ compiler.
+
+* `CXXFLAGS` - C++ compiler flags.
+
+* `LD` - Linker.
+
+* `LDFLAGS` - Linker flags.
+
+* `LIBS` - Libraries.
+
+#### Dynamic Erlang Driver Linking ####
+
+> *NOTE*: Either set all or none of the `DED_LD*` variables.
+
+* `DED_LD` - Linker for Dynamically loaded Erlang Drivers.
+
+* `DED_LDFLAGS` - Linker flags to use with `DED_LD`.
+
+* `DED_LD_FLAG_RUNTIME_LIBRARY_PATH` - This flag should set runtime library
+ search path for shared libraries when linking with `DED_LD`.
+
+#### Large File Support ####
+
+> *NOTE*: Either set all or none of the `LFS_*` variables.
+
+* `LFS_CFLAGS` - Large file support C compiler flags.
+
+* `LFS_LDFLAGS` - Large file support linker flags.
+
+* `LFS_LIBS` - Large file support libraries.
+
+#### Other Tools ####
+
+* `RANLIB` - `ranlib` archive index tool.
+
+* `AR` - `ar` archiving tool.
+
+* `GETCONF` - `getconf` system configuration inspection tool. `getconf` is
+ currently used for finding out large file support flags to use, and
+ on Linux systems for finding out if we have an NPTL thread library or
+ not.
+
+### Cross System Root Locations ###
+
+* `erl_xcomp_sysroot` - The absolute path to the system root of the cross
+ compilation environment. Currently, the `crypto`, `odbc`, `ssh` and
+ `ssl` applications need the system root. These applications will be
+ skipped if the system root has not been set. The system root might be
+ needed for other things too. If this is the case and the system root
+ has not been set, `configure` will fail and request you to set it.
+
+* `erl_xcomp_isysroot` - The absolute path to the system root for includes
+ of the cross compilation environment. If not set, this value defaults
+ to `$erl_xcomp_sysroot`, i.e., only set this value if the include system
+ root path is not the same as the system root path.
+
+### Optional Feature, and Bug Tests ###
+
+These tests cannot (always) be done automatically when cross compiling. You
+usually do not need to set these variables.
+
+> *WARNING*: Setting these variables wrong may cause hard to detect
+> runtime errors. If you need to change these values, *really* make sure
+> that the values are correct.
+
+> *NOTE*: Some of these values will override results of tests performed
+> by `configure`, and some will not be used until `configure` is sure that
+> it cannot figure the result out.
+
+The `configure` script will issue a warning when a default value is used.
+When a variable has been set, no warning will be issued.
+
+* `erl_xcomp_after_morecore_hook` - `yes|no`. Defaults to `no`. If `yes`,
+ the target system must have a working `__after_morecore_hook` that can be
+ used for tracking used `malloc()` implementations core memory usage.
+ This is currently only used by unsupported features.
+
+* `erl_xcomp_bigendian` - `yes|no`. No default. If `yes`, the target system
+ must be big endian. If `no`, little endian. This can often be
+ automatically detected, but not always. If not automatically detected,
+ `configure` will fail unless this variable is set. Since no default
+ value is used, `configure` will try to figure this out automatically.
+
+* `erl_xcomp_double_middle` - `yes|no`. Defaults to `no`.
+ If `yes`, the target system must have doubles in "middle-endian" format. If
+ `no`, it has "regular" endianness.
+
+* `erl_xcomp_clock_gettime_cpu_time` - `yes|no`. Defaults to `no`. If `yes`,
+ the target system must have a working `clock_gettime()` implementation
+ that can be used for retrieving process CPU time.
+
+* `erl_xcomp_getaddrinfo` - `yes|no`. Defaults to `no`. If `yes`, the target
+ system must have a working `getaddrinfo()` implementation that can
+ handle both IPv4 and IPv6.
+
+* `erl_xcomp_gethrvtime_procfs_ioctl` - `yes|no`. Defaults to `no`. If `yes`,
+ the target system must have a working `gethrvtime()` implementation and
+ is used with procfs `ioctl()`.
+
+* `erl_xcomp_dlsym_brk_wrappers` - `yes|no`. Defaults to `no`. If `yes`, the
+ target system must have a working `dlsym(RTLD_NEXT, <S>)` implementation
+ that can be used on `brk` and `sbrk` symbols used by the `malloc()`
+ implementation in use, and by this track the `malloc()` implementations
+ core memory usage. This is currently only used by unsupported features.
+
+* `erl_xcomp_kqueue` - `yes|no`. Defaults to `no`. If `yes`, the target
+ system must have a working `kqueue()` implementation that returns a file
+ descriptor which can be used by `poll()` and/or `select()`. If `no` and
+ the target system has not got `epoll()` or `/dev/poll`, the kernel-poll
+ feature will be disabled.
+
+* `erl_xcomp_linux_clock_gettime_correction` - `yes|no`. Defaults to `yes` on
+ Linux; otherwise, `no`. If `yes`, `clock_gettime(CLOCK_MONOTONIC, _)` on
+ the target system must work. This variable is recommended to be set to
+ `no` on Linux systems with kernel versions less than 2.6.
+
+* `erl_xcomp_linux_nptl` - `yes|no`. Defaults to `yes` on Linux; otherwise,
+ `no`. If `yes`, the target system must have NPTL (Native POSIX Thread
+ Library). Older Linux systems have LinuxThreads instead of NPTL (Linux
+ kernel versions typically less than 2.6).
+
+* `erl_xcomp_linux_usable_sigaltstack` - `yes|no`. Defaults to `yes` on Linux;
+ otherwise, `no`. If `yes`, `sigaltstack()` must be usable on the target
+ system. `sigaltstack()` on Linux kernel versions less than 2.4 are
+ broken.
+
+* `erl_xcomp_linux_usable_sigusrx` - `yes|no`. Defaults to `yes`. If `yes`,
+ the `SIGUSR1` and `SIGUSR2` signals must be usable by the ERTS. Old
+ LinuxThreads thread libraries (Linux kernel versions typically less than
+ 2.2) used these signals and made them unusable by the ERTS.
+
+* `erl_xcomp_poll` - `yes|no`. Defaults to `no` on Darwin/MacOSX; otherwise,
+ `yes`. If `yes`, the target system must have a working `poll()`
+ implementation that also can handle devices. If `no`, `select()` will be
+ used instead of `poll()`.
+
+* `erl_xcomp_putenv_copy` - `yes|no`. Defaults to `no`. If `yes`, the target
+ system must have a `putenv()` implementation that stores a copy of the
+ key/value pair.
+
+* `erl_xcomp_reliable_fpe` - `yes|no`. Defaults to `no`. If `yes`, the target
+ system must have reliable floating point exceptions.
+
+Copyright and License
+---------------------
+
+%CopyrightBegin%
+
+Copyright Ericsson AB 2009-2010. 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
+compliance with the License. You should have received a copy of the
+Erlang Public License along with this software. If not, it can be
+retrieved online at http://www.erlang.org/.
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+the License for the specific language governing rights and limitations
+under the License.
+
+%CopyrightEnd%
+
+Modifying This Document
+-----------------------
+
+Before modifying this document you need to have a look at the
+[$ERL_TOP/HOWTO/MARKDOWN.md][] document.
+
+
+
+ [$ERL_TOP/HOWTO/INSTALL.md]: INSTALL.md
+ [Building in Git]: INSTALL.md#How-to-Build-and-Install-ErlangOTP_Building-in-Git
+ [How to Build the Documentation]: INSTALL.md#The-ErlangOTP-Documentation_How-to-Build-the-Documentation
+ [cross configuration variables]: #Currently-Used-Configuration-Variables
+ [DESTDIR]: http://www.gnu.org/prep/standards/html_node/DESTDIR.html
+ [$ERL_TOP/HOWTO/MARKDOWN.md]: MARKDOWN.md
+
+ [?TOC]: true
diff --git a/INSTALL-WIN32.md b/HOWTO/INSTALL-WIN32.md
index ff253d3dfa..94d3688f23 100644
--- a/INSTALL-WIN32.md
+++ b/HOWTO/INSTALL-WIN32.md
@@ -1037,10 +1037,11 @@ Modifying This Document
-----------------------
Before modifying this document you need to have a look at the
-`$ERL_TOP/README.md.txt` document.
+[$ERL_TOP/HOWTO/MARKDOWN.md][] document.
[1]: http://www.erlang.org/faq.html "mailing lists"
+ [$ERL_TOP/HOWTO/MARKDOWN.md]: MARKDOWN.md
[?TOC]: true
diff --git a/INSTALL.md b/HOWTO/INSTALL.md
index 70d465831d..4f7c317a47 100644
--- a/INSTALL.md
+++ b/HOWTO/INSTALL.md
@@ -18,10 +18,10 @@ Erlang/OTP should be possible to build from source on any Unix system,
including Mac OS X. This document describes how to native compile Erlang/OTP
on Unix. For detailed instructions on how to
-* cross compile Erlang/OTP, see the [$ERL_TOP/INSTALL-CROSS.md][]
+* cross compile Erlang/OTP, see the [$ERL_TOP/HOWTO/INSTALL-CROSS.md][]
document.
-* build Erlang/OTP on Windows, see the [$ERL_TOP/INSTALL-WIN32.md][]
+* build Erlang/OTP on Windows, see the [$ERL_TOP/HOWTO/INSTALL-WIN32.md][]
document.
Binary releases for Windows can be found at
@@ -708,23 +708,25 @@ the best possible performance, do like this:
Install Xcode from the AppStore if it is not already installed.
-For Xcode 4.3 you will also need to download "Command Line Tools"
-via the Downloads preference pane i Xcode.
+If you have Xcode 4.3, or later, you will also need to download
+"Command Line Tools" via the Downloads preference pane in Xcode.
Some tools may still be lacking or out-of-date, we recommend using
[Homebrew](https://github.com/mxcl/homebrew/wiki/installation) or
-Macports update those tools.
+Macports to update those tools.
Install MacPorts (<http://www.macports.org/>). Then:
$ sudo port selfupdate
$ sudo port install gcc45 +universal
-If you want to build the `wx` application, get wxMac-2.8.12
+### Building with wxErlang ###
+
+If you want to build the `wx` application, you will need to get wxMac-2.8.12
(`wxMac-2.8.12.tar.gz` from
-<http://sourceforge.net/projects/wxwindows/files/2.8.12/>) and build:
+<http://sourceforge.net/projects/wxwindows/files/2.8.12/>) and install it.
-Export the path for MacOSX10.6.sdk,
+Export the path for MacOSX10.6.sdk:
$ export SDK=/Developer/SDKs/MacOSX10.6.sdk
@@ -732,18 +734,20 @@ In Xcode 4.3 the path has changed so use the following instead,
$ export SDK=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk
-Then configure and build wx,
+Then configure and build wxMac:
$ arch_flags="-arch i386" ./configure CFLAGS="$arch_flags" CXXFLAGS="$arch_flags" CPPFLAGS="$arch_flags" LDFLAGS="$arch_flags" OBJCFLAGS="$arch_flags" OBJCXXFLAGS="$arch_flags" --prefix=/usr/local --with-macosx-sdk="$SDK" --with-macosx-version-min=10.6 --enable-unicode --with-opengl --disable-shared
$ make
$ sudo make install
-To link wx properly we will also need to build and install `wxStyledTextCtrl`
+To link wx properly you will also need to build and install `wxStyledTextCtrl`:
$ cd contrib/src/stc
$ make
$ sudo make install
+### Finish up ###
+
Build Erlang with the MacPorts GCC as the main compiler (using `clang`
for the Objective-C Cocoa code in the `wx` application):
@@ -821,12 +825,12 @@ Modifying This Document
-----------------------
Before modifying this document you need to have a look at the
-`$ERL_TOP/README.md.txt` document.
+[$ERL_TOP/HOWTO/MARKDOWN.md][] document.
- [$ERL_TOP/INSTALL-CROSS.md]: INSTALL-CROSS
- [$ERL_TOP/INSTALL-WIN32.md]: INSTALL-WIN32
+ [$ERL_TOP/HOWTO/INSTALL-CROSS.md]: INSTALL-CROSS.md
+ [$ERL_TOP/HOWTO/INSTALL-WIN32.md]: INSTALL-WIN32.md
[DESTDIR]: http://www.gnu.org/prep/standards/html_node/DESTDIR.html
[Building in Git]: #How-to-Build-and-Install-ErlangOTP_Building-in-Git
[Pre-built Source Release]: #How-to-Build-and-Install-ErlangOTP_Prebuilt-Source-Release
@@ -834,5 +838,6 @@ Before modifying this document you need to have a look at the
[html documentation]: http://www.erlang.org/download/otp_doc_html_%OTP-REL%.tar.gz
[man pages]: http://www.erlang.org/download/otp_doc_man_%OTP-REL%.tar.gz
[the released source tar ball]: http://www.erlang.org/download/otp_src_%OTP-REL%.tar.gz
+ [$ERL_TOP/HOWTO/MARKDOWN.md]: MARKDOWN.md
[?TOC]: true
diff --git a/README.md.txt b/HOWTO/MARKDOWN.md
index a5a1bf4b7d..42045fb10c 100644
--- a/README.md.txt
+++ b/HOWTO/MARKDOWN.md
@@ -5,7 +5,7 @@ Introduction
------------
If you are looking for information on how to build and install Erlang/OTP you
-want to read the [$ERL_TOP/INSTALL.md][] document instead of this document
+want to read the [$ERL_TOP/HOWTO/INSTALL.md][] document instead of this document
(where `$ERL_TOP` is the top source directory in the source tree).
All files with the `.md` suffix (as well as this file) are ordinary text files
@@ -258,7 +258,7 @@ under the License.
- [$ERL_TOP/INSTALL.md]: doc/installation_guide:INSTALL
+ [$ERL_TOP/HOWTO/INSTALL.md]: INSTALL.md
[github]: http://github.com
[our github repository]: http://github.com/erlang/otp
diff --git a/HOWTO/SYSTEMTAP.md b/HOWTO/SYSTEMTAP.md
new file mode 100644
index 0000000000..ce9c0b2f0c
--- /dev/null
+++ b/HOWTO/SYSTEMTAP.md
@@ -0,0 +1,75 @@
+SystemTap and Erlang/OTP
+========================
+
+Introduction
+------------
+
+SystemTap is DTrace for Linux. In fact Erlang's SystemTap support
+is build using SystemTap's DTrace compatibility's layer. For an
+introduction to Erlang DTrace support read [$ERL_TOP/HOWTO/DTRACE.md][].
+
+Requisites
+----------
+
+* Linux Kernel with UTRACE support
+
+ check for UTRACE support in your current kernel:
+
+ # grep CONFIG_UTRACE /boot/config-`uname -r`
+ CONFIG_UTRACE=y
+
+ Fedora 16 is known to contain UTRACE, for most other Linux distributions
+ a custom build kernel will be required.
+ Check Fedora's SystemTap documentation for additional required packages
+ (e.g. Kernel Debug Symbols)
+
+* SystemTap > 1.6
+
+ A the time of writing this, the latest released version of SystemTap is
+ version 1.6. Erlang's DTrace support requires a MACRO that was introduced
+ after that release. So either get a newer release or build SystemTap from
+ git yourself (see: http://sourceware.org/systemtap/getinvolved.html)
+
+Building Erlang
+---------------
+
+Configure and build Erlang with SystemTap support:
+
+ # ./configure --with-dynamic-trace=systemtap + whatever args you need
+ # make
+
+Testing
+-------
+
+SystemTap, unlike DTrace, needs to know what binary it is tracing and has to
+be able to read that binary before it starts tracing. Your probe script
+therefor has to reference the correct beam emulator and stap needs to be able
+to find that binary.
+The examples are written for "beam", but other versions such as "beam.smp" or
+"beam.debug.smp" might exist (depending on your configuration). Make sure you
+either specify the full the path of the binary in the probe or your "beam"
+binary is in the search path.
+
+All available probes can be listed like this:
+
+ # stap -L 'process("beam").mark("*")'
+
+or:
+
+ # PATH=/path/to/beam:$PATH stap -L 'process("beam").mark("*")'
+
+
+Probes in the dtrace.so NIF library like this:
+
+ # PATH=/path/to/dtrace/priv/lib:$PATH stap -L 'process("dtrace.so").mark("*")'
+
+Running SystemTap scripts
+-------------------------
+
+Adjust the process("beam") reference to your beam version and attach the script
+to a running "beam" instance:
+
+ # stap /path/to/probe/script/port1.systemtap -x <pid of beam>
+
+
+ [$ERL_TOP/HOWTO/DTRACE.md]: DTRACE.md
diff --git a/HOWTO/TESTING.md b/HOWTO/TESTING.md
new file mode 100644
index 0000000000..34eaa68df8
--- /dev/null
+++ b/HOWTO/TESTING.md
@@ -0,0 +1,150 @@
+Testing Erlang/OTP
+==================
+
+Before you start testing you need to have the Erlang release which you
+are going to test in your path. See [$ERL_TOP/HOWTO/INSTALL.md][] for
+instructions on how to build an Erlang release.
+
+Short version
+-------------
+Move to the top directory of the Erlang release you want to test, i.e.
+cd /ldisk/work/otp
+
+ export ERL_TOP=`pwd`
+ ./otp_build setup -a
+ export PATH=`pwd`/bin:$PATH
+ ./otp_build tests
+ cd release/tests/test_server
+ erl -s ts install -s ts run all_tests -s init stop
+
+Where are the tests
+-------------------
+
+There are a lot of tests which test Erlang/OTP (as of 2012 about 12000) and
+they are located in different places throughout the source tree. Below is a list
+of the places where you can expect to find tests:
+
+* $ERL_TOP/lib/AppName/test/
+* $ERL_TOP/erts/test/
+* $ERL_TOP/erts/emulator/test/
+* $ERL_TOP/erts/epmd/test/
+
+Writing tests
+-------------
+
+All tests are [common_test][] suites and follow the same pattern as all
+[common_test][] suites. However, a couple of corner cases are
+handled by a test wrapper called `ts`. `ts` allows the test writer to put
+`Makefile.src` and `Makefile.first` files in the [data_dir][] and a special
+directory called `all_SUITE_data`.
+
+`Makefile.first` is run before any other Makefile and is typically used to
+generate .hrl files which are needed by other test suites. At the moment only
+the erl_interface tests use this feature and it should remain that way.
+
+`Makefile.src` is configured to a `Makefile` using the `variables` created when
+[configuring the tests][]. These `Makefile`s are later run by the test suite
+to compile whatever platform specific code the tests need to run.
+
+Releasing tests
+---------------
+
+If you cannot use [ct_run][] in the source tree you have to release the tests
+into a common test directory. The easiest way to do this is to use `otp_build`
+like this:
+
+ export ERL_TOP=`pwd`; ./otp_build tests
+
+This will release all tests in Erlang/OTP to `$ERL_TOP/release/tests/`. If you
+want to change the directory where the tests are released to use the `TESTROOT`
+environmental variable.
+
+In the `$TESTROOT` you should now see *_test folders. These folders contain
+everything needed to test Erlang/OTP and are platform independent; if you are
+testing Erlang on multiple platforms you just have to release on one and copy
+the tests to all other platforms.
+
+### Releasing cross tests
+
+For releasing tests in a cross compilation environment see [$ERL_TOP/HOWTO/INSTALL-CROSS.md][].
+
+Configuring and Running tests
+-----------------------------
+
+Running tests is done by first navigating to the `$TESTROOT/test_server` folder
+created when you released the tests and then start `erl` in that directory. The
+emulator flags specified will be used in the test runs. For example, if you want
+to test using async threads you have to supply `+A 10` to `erl` when you start it.
+
+To configure and run the tests `ts` is used. `ts` is a wrapper module to
+[common_test][] which takes care of configuration and build issues before
+[common_test][] is started.
+
+`ts` has a lot of special options and functions which can be usefull when
+testing Erlang/OTP. For a full listing issue `ts:help()` in the erlang shell.
+
+### Configuring the test environment
+
+Before running released tests you have to install them on the target system.
+Installing the tests is done by calling `ts:install().` in the Erlang shell
+which you intend to test from. `ts:install()` is basically a wrapper to a
+configure script and some Erlang code which figures out what your system looks
+like and what kind of emulator you are testing with. `ts:install()` can also
+take some arguments if necessary, see `ts:help()` for details.
+
+All variables created by `ts:install()` are found in
+`$TESTROOT/test_server/variables`.
+
+### Running the tests
+
+To run all test suites go to `$TESTROOT/test_server` fire up an Erlang shell and type:
+
+ ts:run().
+
+Note that running all tests will require several hours, so you may want to run
+the test cases for a single application
+
+ ts:run(Application, [batch]).
+
+or even part of the test suite for an application, for example
+
+ ts:run(emulator, bs, [batch]).
+
+to run all test suite modules starting with `bs` (i.e. all modules that test
+the bit syntax).
+
+To run a specific test case in a module, the full name of the module and test
+case must be specified:
+
+ ts:run(emulator, bs_bincomp_SUITE, byte_aligned, [batch]).
+
+Run `ts:help().` for more information.
+
+As of R14B02 it is also possibly to start all tests but the erl_interface tests
+by invoking Common Test directly from the released applications test directory,
+i.e.
+
+ cd $TESTROOT/test_server
+ $ERL_TOP/bin/ct_run -suite ../compiler_test/andor_SUITE -case t_orelse
+
+Running [ct_run][] from the command line still requires you to do the
+`ts:install()` step above.
+
+Examining the results
+---------------------
+
+Open the file `release/tests/test_server/index.html` in a web browser. Or open
+`release/tests/test_server/last_test.html` when a test suite is running to
+examine the results so far for the currently executing test suite (in R14B02 and
+later you want to open the `release/tests/test_server/all_runs.html` file to
+get to the currently running test)
+
+ [ct_run]: http://www.erlang.org/doc/man/ct_run.html
+ [ct hook]: http://www.erlang.org/doc/apps/common_test/ct_hooks_chapter.html
+ [$ERL_TOP/HOWTO/INSTALL.md]: INSTALL.md
+ [$ERL_TOP/HOWTO/INSTALL-CROSS.md]: INSTALL-CROSS.md#testing-the-cross-compiled-system
+ [common_test]: http://www.erlang.org/doc/man/ct.html
+ [data_dir]: http://www.erlang.org/doc/apps/common_test/write_test_chapter.html#data_priv_dir
+ [configuring the tests]: #configuring-the-test-environment
+
+ [?TOC]: true
diff --git a/INSTALL-CROSS.md b/INSTALL-CROSS.md
deleted file mode 120000
index 9e7743b9de..0000000000
--- a/INSTALL-CROSS.md
+++ /dev/null
@@ -1 +0,0 @@
-xcomp/README.md \ No newline at end of file
diff --git a/README.md b/README.md
index c38f097b0f..1bcf14e760 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ Building and Installing
-----------------------
Information on building and installing Erlang/OTP can be found
-in the `INSTALL.md` document.
+in the [$ERL_TOP/HOWTO/INSTALL.md] [5] document.
Contributing to Erlang/OTP
--------------------------
@@ -46,7 +46,7 @@ In short:
may suggest improvements that are needed before the change can be accepted
and merged.
-* Once or twice a week, a status email called "What's cooking in Erlang/OTP"
+* Once or twice a week, a status email called ["What's cooking in Erlang/OTP"] [4]
will be sent to the [`erlang-patches`] [3] mailing list.
Copyright and License
@@ -54,7 +54,7 @@ Copyright and License
> %CopyrightBegin%
>
-> Copyright Ericsson AB 2010. All Rights Reserved.
+> Copyright Ericsson AB 2010-2012. 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
@@ -73,4 +73,6 @@ Copyright and License
[1]: http://www.erlang.org
[2]: http://wiki.github.com/erlang/otp/submitting-patches
- [3]: http://www.erlang.org/faq.html
+ [3]: http://www.erlang.org/static/doc/mailinglist.html
+ [4]: http://erlang.github.com/otp/
+ [5]: HOWTO/INSTALL.md
diff --git a/README.systemtap.md b/README.systemtap.md
deleted file mode 100644
index c190bcc893..0000000000
--- a/README.systemtap.md
+++ /dev/null
@@ -1,72 +0,0 @@
-SystemTap and Erlang/OTP
-========================
-
-Introduction
-------------
-
-SystemTap is DTrace for Linux. In fact Erlang's SystemTap support
-is build using SystemTap's DTrace compatibility's layer. For an
-introduction to Erlang DTrace support read README.dtrace.md.
-
-Requisites
-----------
-
-* Linux Kernel with UTRACE support
-
- check for UTRACE support in your current kernel:
-
- # grep CONFIG_UTRACE /boot/config-`uname -r`
- CONFIG_UTRACE=y
-
- Fedora 16 is known to contain UTRACE, for most other Linux distributions
- a custom build kernel will be required.
- Check Fedora's SystemTap documentation for additional required packages
- (e.g. Kernel Debug Symbols)
-
-* SystemTap > 1.6
-
- A the time of writing this, the latest released version of SystemTap is
- version 1.6. Erlang's DTrace support requires a MACRO that was introduced
- after that release. So either get a newer release or build SystemTap from
- git yourself (see: http://sourceware.org/systemtap/getinvolved.html)
-
-Building Erlang
----------------
-
-Configure and build Erlang with SystemTap support:
-
- # ./configure --with-dynamic-trace=systemtap + whatever args you need
- # make
-
-Testing
--------
-
-SystemTap, unlike DTrace, needs to know what binary it is tracing and has to
-be able to read that binary before it starts tracing. Your probe script
-therefor has to reference the correct beam emulator and stap needs to be able
-to find that binary.
-The examples are written for "beam", but other versions such as "beam.smp" or
-"beam.debug.smp" might exist (depending on your configuration). Make sure you
-either specify the full the path of the binary in the probe or your "beam"
-binary is in the search path.
-
-All available probes can be listed like this:
-
- # stap -L 'process("beam").mark("*")'
-
-or:
-
- # PATH=/path/to/beam:$PATH stap -L 'process("beam").mark("*")'
-
-
-Probes in the dtrace.so NIF library like this:
-
- # PATH=/path/to/dtrace/priv/lib:$PATH stap -L 'process("dtrace.so").mark("*")'
-
-Running SystemTap scripts
--------------------------
-
-Adjust the process("beam") reference to your beam version and attach the script
-to a running "beam" instance:
-
- # stap /path/to/probe/script/port1.systemtap -x <pid of beam>
diff --git a/TAR.include b/TAR.include
index 5146458eea..87854d24c7 100644
--- a/TAR.include
+++ b/TAR.include
@@ -2,10 +2,10 @@ AUTHORS
EPLICENCE
Makefile.in
README.md
-README.md.txt
-INSTALL.md
-INSTALL-CROSS.md
-INSTALL-WIN32.md
+HOWTO/MARKDOWN.md
+HOWTO/INSTALL.md
+HOWTO/INSTALL-CROSS.md
+HOWTO/INSTALL-WIN32.md
configure.in
aclocal.m4
otp_build
diff --git a/aclocal.m4 b/aclocal.m4
index 339a15a2bb..b1cf1fe404 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,7 +1,7 @@
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1998-2011. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2012. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
@@ -59,6 +59,7 @@ AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only us
dnl Cross compilation variables
AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)])
@@ -606,6 +607,103 @@ ifelse([$5], , , [$5
fi
])
+dnl ----------------------------------------------------------------------
+dnl
+dnl AC_DOUBLE_MIDDLE_ENDIAN
+dnl
+dnl Checks whether doubles are represented in "middle-endian" format.
+dnl Sets ac_cv_double_middle_endian={no,yes,unknown} accordingly,
+dnl as well as DOUBLE_MIDDLE_ENDIAN.
+dnl
+dnl
+
+AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN],
+[AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian,
+[# It does not; compile a test program.
+AC_RUN_IFELSE(
+[AC_LANG_SOURCE([[#include <stdlib.h>
+
+int
+main(void)
+{
+ int i = 0;
+ int zero = 0;
+ int bigendian;
+ int zero_index = 0;
+
+ union
+ {
+ long int l;
+ char c[sizeof (long int)];
+ } u;
+
+ /* we'll use the one with 32-bit words */
+ union
+ {
+ double d;
+ unsigned int c[2];
+ } vint;
+
+ union
+ {
+ double d;
+ unsigned long c[2];
+ } vlong;
+
+ union
+ {
+ double d;
+ unsigned short c[2];
+ } vshort;
+
+
+ /* Are we little or big endian? From Harbison&Steele. */
+ u.l = 1;
+ bigendian = (u.c[sizeof (long int) - 1] == 1);
+
+ zero_index = bigendian ? 1 : 0;
+
+ vint.d = 1.0;
+ vlong.d = 1.0;
+ vshort.d = 1.0;
+
+ if (sizeof(unsigned int) == 4)
+ {
+ if (vint.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned long) == 4)
+ {
+ if (vlong.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned short) == 4)
+ {
+ if (vshort.c[zero_index] != 0)
+ zero = 1;
+ }
+
+ exit (zero);
+}
+]])],
+ [ac_cv_c_double_middle_endian=no],
+ [ac_cv_c_double_middle_endian=yes],
+ [ac_cv_c_double_middle=unknown])])
+case $ac_cv_c_double_middle_endian in
+ yes)
+ m4_default([$1],
+ [AC_DEFINE([DOUBLE_MIDDLE_ENDIAN], 1,
+ [Define to 1 if your processor stores the words in a double in
+ middle-endian format (like some ARMs).])]) ;;
+ no)
+ $2 ;;
+ *)
+ m4_default([$3],
+ [AC_MSG_WARN([unknown double endianness
+presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;;
+esac
+])# AC_C_DOUBLE_MIDDLE_ENDIAN
+
dnl ----------------------------------------------------------------------
dnl
@@ -1337,6 +1435,14 @@ if test "$ac_cv_c_bigendian" = "yes"; then
AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian])
fi
+case X$erl_xcomp_double_middle_endian in
+ X) ;;
+ Xyes|Xno|Xunknown) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;;
+ *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);;
+esac
+
+AC_C_DOUBLE_MIDDLE_ENDIAN
+
AC_ARG_ENABLE(native-ethr-impls,
AS_HELP_STRING([--disable-native-ethr-impls],
[disable native ethread implementations]),
diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot
index 3d2153e66d..c6ebc852a2 100644
--- a/bootstrap/bin/start.boot
+++ b/bootstrap/bin/start.boot
Binary files differ
diff --git a/bootstrap/bin/start_clean.boot b/bootstrap/bin/start_clean.boot
index 3d2153e66d..c6ebc852a2 100644
--- a/bootstrap/bin/start_clean.boot
+++ b/bootstrap/bin/start_clean.boot
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_type.beam b/bootstrap/lib/compiler/ebin/beam_type.beam
index 116da39f89..3cb5f06859 100644
--- a/bootstrap/lib/compiler/ebin/beam_type.beam
+++ b/bootstrap/lib/compiler/ebin/beam_type.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_utils.beam b/bootstrap/lib/compiler/ebin/beam_utils.beam
index 9c6ad019f9..523a6f03fb 100644
--- a/bootstrap/lib/compiler/ebin/beam_utils.beam
+++ b/bootstrap/lib/compiler/ebin/beam_utils.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_validator.beam b/bootstrap/lib/compiler/ebin/beam_validator.beam
index 52643de79a..41523ed185 100644
--- a/bootstrap/lib/compiler/ebin/beam_validator.beam
+++ b/bootstrap/lib/compiler/ebin/beam_validator.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/compile.beam b/bootstrap/lib/compiler/ebin/compile.beam
index e2bd3c4ce0..c685035e23 100644
--- a/bootstrap/lib/compiler/ebin/compile.beam
+++ b/bootstrap/lib/compiler/ebin/compile.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/compiler.app b/bootstrap/lib/compiler/ebin/compiler.app
new file mode 100644
index 0000000000..5170231c59
--- /dev/null
+++ b/bootstrap/lib/compiler/ebin/compiler.app
@@ -0,0 +1,69 @@
+% This is an -*- erlang -*- file.
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2011. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+{application, compiler,
+ [{description, "ERTS CXC 138 10"},
+ {vsn, "4.8.1"},
+ {modules, [
+ beam_asm,
+ beam_block,
+ beam_bool,
+ beam_bsm,
+ beam_clean,
+ beam_dead,
+ beam_dict,
+ beam_disasm,
+ beam_except,
+ beam_flatten,
+ beam_jump,
+ beam_listing,
+ beam_opcodes,
+ beam_peep,
+ beam_receive,
+ beam_split,
+ beam_trim,
+ beam_type,
+ beam_utils,
+ beam_validator,
+ cerl,
+ cerl_clauses,
+ cerl_inline,
+ cerl_trees,
+ compile,
+ core_scan,
+ core_lint,
+ core_parse,
+ core_pp,
+ core_lib,
+ erl_bifs,
+ rec_env,
+ sys_core_dsetel,
+ sys_core_fold,
+ sys_core_inline,
+ sys_expand_pmod,
+ sys_pre_attributes,
+ sys_pre_expand,
+ v3_codegen,
+ v3_core,
+ v3_kernel,
+ v3_kernel_pp,
+ v3_life
+ ]},
+ {registered, []},
+ {applications, [kernel, stdlib]},
+ {env, []}]}.
diff --git a/bootstrap/lib/compiler/ebin/compiler.appup b/bootstrap/lib/compiler/ebin/compiler.appup
new file mode 100644
index 0000000000..887d074b16
--- /dev/null
+++ b/bootstrap/lib/compiler/ebin/compiler.appup
@@ -0,0 +1 @@
+{"4.8.1",[],[]}.
diff --git a/bootstrap/lib/compiler/ebin/sys_pre_expand.beam b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam
index f7e9541e4c..1a176b2e3f 100644
--- a/bootstrap/lib/compiler/ebin/sys_pre_expand.beam
+++ b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/code.beam b/bootstrap/lib/kernel/ebin/code.beam
index 31ccb7367d..f1fd519f50 100644
--- a/bootstrap/lib/kernel/ebin/code.beam
+++ b/bootstrap/lib/kernel/ebin/code.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/code_server.beam b/bootstrap/lib/kernel/ebin/code_server.beam
index adc53aa5f3..dc10d111d2 100644
--- a/bootstrap/lib/kernel/ebin/code_server.beam
+++ b/bootstrap/lib/kernel/ebin/code_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log_1.beam b/bootstrap/lib/kernel/ebin/disk_log_1.beam
index 5e286cdd48..1deb5ad05e 100644
--- a/bootstrap/lib/kernel/ebin/disk_log_1.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log_1.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_ddll.beam b/bootstrap/lib/kernel/ebin/erl_ddll.beam
index b909dd2786..4fdec24b11 100644
--- a/bootstrap/lib/kernel/ebin/erl_ddll.beam
+++ b/bootstrap/lib/kernel/ebin/erl_ddll.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/error_logger.beam b/bootstrap/lib/kernel/ebin/error_logger.beam
index e07bef5657..bad3d6211f 100644
--- a/bootstrap/lib/kernel/ebin/error_logger.beam
+++ b/bootstrap/lib/kernel/ebin/error_logger.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erts_debug.beam b/bootstrap/lib/kernel/ebin/erts_debug.beam
index 83a981cb1d..5c6483e368 100644
--- a/bootstrap/lib/kernel/ebin/erts_debug.beam
+++ b/bootstrap/lib/kernel/ebin/erts_debug.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/file.beam b/bootstrap/lib/kernel/ebin/file.beam
index aa7dcf388d..243be75c43 100644
--- a/bootstrap/lib/kernel/ebin/file.beam
+++ b/bootstrap/lib/kernel/ebin/file.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
index 028b5215a2..1574686ba6 100644
--- a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
+++ b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet.beam b/bootstrap/lib/kernel/ebin/inet.beam
index 65f517ed39..fe2d597723 100644
--- a/bootstrap/lib/kernel/ebin/inet.beam
+++ b/bootstrap/lib/kernel/ebin/inet.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_config.beam b/bootstrap/lib/kernel/ebin/inet_config.beam
index fc1e2c8387..9479212ed0 100644
--- a/bootstrap/lib/kernel/ebin/inet_config.beam
+++ b/bootstrap/lib/kernel/ebin/inet_config.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_parse.beam b/bootstrap/lib/kernel/ebin/inet_parse.beam
index 14ca272933..b74c0ee730 100644
--- a/bootstrap/lib/kernel/ebin/inet_parse.beam
+++ b/bootstrap/lib/kernel/ebin/inet_parse.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel.app b/bootstrap/lib/kernel/ebin/kernel.app
new file mode 100644
index 0000000000..48f6250828
--- /dev/null
+++ b/bootstrap/lib/kernel/ebin/kernel.app
@@ -0,0 +1,120 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%% This is an -*- erlang -*- file.
+%%
+{application, kernel,
+ [
+ {description, "ERTS CXC 138 10"},
+ {vsn, "2.16"},
+ {modules, [application,
+ application_controller,
+ application_master,
+ application_starter,
+ auth,
+ code,
+ packages,
+ code_server,
+ dist_util,
+ erl_boot_server,
+ erl_distribution,
+ erl_reply,
+ error_handler,
+ error_logger,
+ file,
+ file_server,
+ file_io_server,
+ global,
+ global_group,
+ global_search,
+ group,
+ heart,
+ hipe_unified_loader,
+ inet6_tcp,
+ inet6_tcp_dist,
+ inet6_udp,
+ inet6_sctp,
+ inet_config,
+ inet_hosts,
+ inet_gethost_native,
+ inet_tcp_dist,
+ kernel,
+ kernel_config,
+ net,
+ net_adm,
+ net_kernel,
+ os,
+ ram_file,
+ rpc,
+ user,
+ user_drv,
+ user_sup,
+ disk_log,
+ disk_log_1,
+ disk_log_server,
+ disk_log_sup,
+ dist_ac,
+ erl_ddll,
+ erl_epmd,
+ erts_debug,
+ gen_tcp,
+ gen_udp,
+ gen_sctp,
+ inet,
+ inet_db,
+ inet_dns,
+ inet_parse,
+ inet_res,
+ inet_tcp,
+ inet_udp,
+ inet_sctp,
+ pg2,
+ seq_trace,
+ standard_error,
+ wrap_log_reader]},
+ {registered, [application_controller,
+ erl_reply,
+ auth,
+ boot_server,
+ code_server,
+ disk_log_server,
+ disk_log_sup,
+ erl_prim_loader,
+ error_logger,
+ file_server_2,
+ fixtable_server,
+ global_group,
+ global_name_server,
+ heart,
+ init,
+ kernel_config,
+ kernel_sup,
+ net_kernel,
+ net_sup,
+ rex,
+ user,
+ os_server,
+ ddll_server,
+ erl_epmd,
+ inet_db,
+ pg2]},
+ {applications, []},
+ {env, [{error_logger, tty}]},
+ {mod, {kernel, []}}
+ ]
+}.
diff --git a/bootstrap/lib/kernel/ebin/kernel.appup b/bootstrap/lib/kernel/ebin/kernel.appup
new file mode 100644
index 0000000000..b534b736be
--- /dev/null
+++ b/bootstrap/lib/kernel/ebin/kernel.appup
@@ -0,0 +1,27 @@
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2011. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+{"2.16",
+ %% Up from - max two major revisions back
+ [{<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R14
+ %% Down to - max two major revisions back
+ [{<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R14
+}.
diff --git a/bootstrap/lib/kernel/ebin/net_kernel.beam b/bootstrap/lib/kernel/ebin/net_kernel.beam
index a81f3e47d5..9eebd72403 100644
--- a/bootstrap/lib/kernel/ebin/net_kernel.beam
+++ b/bootstrap/lib/kernel/ebin/net_kernel.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/os.beam b/bootstrap/lib/kernel/ebin/os.beam
index 1291f1cdfa..ea33408b31 100644
--- a/bootstrap/lib/kernel/ebin/os.beam
+++ b/bootstrap/lib/kernel/ebin/os.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/rpc.beam b/bootstrap/lib/kernel/ebin/rpc.beam
index 4355f300e9..bfb80cf827 100644
--- a/bootstrap/lib/kernel/ebin/rpc.beam
+++ b/bootstrap/lib/kernel/ebin/rpc.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/binary.beam b/bootstrap/lib/stdlib/ebin/binary.beam
index 0cc6c6fce9..255c50aa01 100644
--- a/bootstrap/lib/stdlib/ebin/binary.beam
+++ b/bootstrap/lib/stdlib/ebin/binary.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/epp.beam b/bootstrap/lib/stdlib/ebin/epp.beam
index 55f5adc24d..2e2b3ef4ed 100644
--- a/bootstrap/lib/stdlib/ebin/epp.beam
+++ b/bootstrap/lib/stdlib/ebin/epp.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam
index 798924c19b..875ccf94c2 100644
--- a/bootstrap/lib/stdlib/ebin/erl_lint.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_lint.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/escript.beam b/bootstrap/lib/stdlib/ebin/escript.beam
index 17881e2552..e832f973d7 100644
--- a/bootstrap/lib/stdlib/ebin/escript.beam
+++ b/bootstrap/lib/stdlib/ebin/escript.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ets.beam b/bootstrap/lib/stdlib/ebin/ets.beam
index 642689d749..cc6521891b 100644
--- a/bootstrap/lib/stdlib/ebin/ets.beam
+++ b/bootstrap/lib/stdlib/ebin/ets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_server.beam b/bootstrap/lib/stdlib/ebin/gen_server.beam
index 3fa7d89db4..5d68dda4cf 100644
--- a/bootstrap/lib/stdlib/ebin/gen_server.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_server.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/lists.beam b/bootstrap/lib/stdlib/ebin/lists.beam
index 5e23551fb3..dfd689aa5a 100644
--- a/bootstrap/lib/stdlib/ebin/lists.beam
+++ b/bootstrap/lib/stdlib/ebin/lists.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/math.beam b/bootstrap/lib/stdlib/ebin/math.beam
index e97284ca1e..9b662405ea 100644
--- a/bootstrap/lib/stdlib/ebin/math.beam
+++ b/bootstrap/lib/stdlib/ebin/math.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/otp_internal.beam b/bootstrap/lib/stdlib/ebin/otp_internal.beam
index 1a9a55dc24..270d35ef33 100644
--- a/bootstrap/lib/stdlib/ebin/otp_internal.beam
+++ b/bootstrap/lib/stdlib/ebin/otp_internal.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/qlc_pt.beam b/bootstrap/lib/stdlib/ebin/qlc_pt.beam
index f51bc3ccbd..8a25d95384 100644
--- a/bootstrap/lib/stdlib/ebin/qlc_pt.beam
+++ b/bootstrap/lib/stdlib/ebin/qlc_pt.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/re.beam b/bootstrap/lib/stdlib/ebin/re.beam
index 4b9cbb7cfc..e20546d45d 100644
--- a/bootstrap/lib/stdlib/ebin/re.beam
+++ b/bootstrap/lib/stdlib/ebin/re.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/stdlib.app b/bootstrap/lib/stdlib/ebin/stdlib.app
new file mode 100644
index 0000000000..461531e1df
--- /dev/null
+++ b/bootstrap/lib/stdlib/ebin/stdlib.app
@@ -0,0 +1,105 @@
+%% This is an -*- erlang -*- file.
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2011. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+{application, stdlib,
+ [{description, "ERTS CXC 138 10"},
+ {vsn, "1.19"},
+ {modules, [array,
+ base64,
+ beam_lib,
+ binary,
+ c,
+ calendar,
+ dets,
+ dets_server,
+ dets_sup,
+ dets_utils,
+ dets_v8,
+ dets_v9,
+ dict,
+ digraph,
+ digraph_utils,
+ edlin,
+ edlin_expand,
+ epp,
+ eval_bits,
+ erl_bits,
+ erl_compile,
+ erl_eval,
+ erl_expand_records,
+ erl_internal,
+ erl_lint,
+ erl_parse,
+ erl_posix_msg,
+ erl_pp,
+ erl_scan,
+ erl_tar,
+ error_logger_file_h,
+ error_logger_tty_h,
+ escript,
+ ets,
+ file_sorter,
+ filelib,
+ filename,
+ gb_trees,
+ gb_sets,
+ gen,
+ gen_event,
+ gen_fsm,
+ gen_server,
+ io,
+ io_lib,
+ io_lib_format,
+ io_lib_fread,
+ io_lib_pretty,
+ lib,
+ lists,
+ log_mf_h,
+ math,
+ ms_transform,
+ orddict,
+ ordsets,
+ otp_internal,
+ pg,
+ pool,
+ proc_lib,
+ proplists,
+ qlc,
+ qlc_pt,
+ queue,
+ random,
+ re,
+ sets,
+ shell,
+ shell_default,
+ slave,
+ sofs,
+ string,
+ supervisor,
+ supervisor_bridge,
+ sys,
+ timer,
+ unicode,
+ win32reg,
+ zip]},
+ {registered,[timer_server,rsh_starter,take_over_monitor,pool_master,
+ dets]},
+ {applications, [kernel]},
+ {env, []}]}.
+
diff --git a/bootstrap/lib/stdlib/ebin/stdlib.appup b/bootstrap/lib/stdlib/ebin/stdlib.appup
new file mode 100644
index 0000000000..ff9b5387c9
--- /dev/null
+++ b/bootstrap/lib/stdlib/ebin/stdlib.appup
@@ -0,0 +1,27 @@
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2011. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+{"1.19",
+ %% Up from - max two major revisions back
+ [{<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R14
+ %% Down to - max two major revisions back
+ [{<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R14
+}.
diff --git a/bootstrap/lib/stdlib/ebin/string.beam b/bootstrap/lib/stdlib/ebin/string.beam
index 3ace8184d0..e014257a58 100644
--- a/bootstrap/lib/stdlib/ebin/string.beam
+++ b/bootstrap/lib/stdlib/ebin/string.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/unicode.beam b/bootstrap/lib/stdlib/ebin/unicode.beam
index 7dcf140730..074fe0534d 100644
--- a/bootstrap/lib/stdlib/ebin/unicode.beam
+++ b/bootstrap/lib/stdlib/ebin/unicode.beam
Binary files differ
diff --git a/erts/aclocal.m4 b/erts/aclocal.m4
index 339a15a2bb..b1cf1fe404 100644
--- a/erts/aclocal.m4
+++ b/erts/aclocal.m4
@@ -1,7 +1,7 @@
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1998-2011. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2012. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
@@ -59,6 +59,7 @@ AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only us
dnl Cross compilation variables
AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)])
@@ -606,6 +607,103 @@ ifelse([$5], , , [$5
fi
])
+dnl ----------------------------------------------------------------------
+dnl
+dnl AC_DOUBLE_MIDDLE_ENDIAN
+dnl
+dnl Checks whether doubles are represented in "middle-endian" format.
+dnl Sets ac_cv_double_middle_endian={no,yes,unknown} accordingly,
+dnl as well as DOUBLE_MIDDLE_ENDIAN.
+dnl
+dnl
+
+AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN],
+[AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian,
+[# It does not; compile a test program.
+AC_RUN_IFELSE(
+[AC_LANG_SOURCE([[#include <stdlib.h>
+
+int
+main(void)
+{
+ int i = 0;
+ int zero = 0;
+ int bigendian;
+ int zero_index = 0;
+
+ union
+ {
+ long int l;
+ char c[sizeof (long int)];
+ } u;
+
+ /* we'll use the one with 32-bit words */
+ union
+ {
+ double d;
+ unsigned int c[2];
+ } vint;
+
+ union
+ {
+ double d;
+ unsigned long c[2];
+ } vlong;
+
+ union
+ {
+ double d;
+ unsigned short c[2];
+ } vshort;
+
+
+ /* Are we little or big endian? From Harbison&Steele. */
+ u.l = 1;
+ bigendian = (u.c[sizeof (long int) - 1] == 1);
+
+ zero_index = bigendian ? 1 : 0;
+
+ vint.d = 1.0;
+ vlong.d = 1.0;
+ vshort.d = 1.0;
+
+ if (sizeof(unsigned int) == 4)
+ {
+ if (vint.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned long) == 4)
+ {
+ if (vlong.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned short) == 4)
+ {
+ if (vshort.c[zero_index] != 0)
+ zero = 1;
+ }
+
+ exit (zero);
+}
+]])],
+ [ac_cv_c_double_middle_endian=no],
+ [ac_cv_c_double_middle_endian=yes],
+ [ac_cv_c_double_middle=unknown])])
+case $ac_cv_c_double_middle_endian in
+ yes)
+ m4_default([$1],
+ [AC_DEFINE([DOUBLE_MIDDLE_ENDIAN], 1,
+ [Define to 1 if your processor stores the words in a double in
+ middle-endian format (like some ARMs).])]) ;;
+ no)
+ $2 ;;
+ *)
+ m4_default([$3],
+ [AC_MSG_WARN([unknown double endianness
+presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;;
+esac
+])# AC_C_DOUBLE_MIDDLE_ENDIAN
+
dnl ----------------------------------------------------------------------
dnl
@@ -1337,6 +1435,14 @@ if test "$ac_cv_c_bigendian" = "yes"; then
AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian])
fi
+case X$erl_xcomp_double_middle_endian in
+ X) ;;
+ Xyes|Xno|Xunknown) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;;
+ *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);;
+esac
+
+AC_C_DOUBLE_MIDDLE_ENDIAN
+
AC_ARG_ENABLE(native-ethr-impls,
AS_HELP_STRING([--disable-native-ethr-impls],
[disable native ethread implementations]),
diff --git a/erts/autoconf/vxworks/sed.general b/erts/autoconf/vxworks/sed.general
index ac792b44b5..162fd38c2b 100644
--- a/erts/autoconf/vxworks/sed.general
+++ b/erts/autoconf/vxworks/sed.general
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/configure.in b/erts/configure.in
index b3289bf84c..6ad1951a4e 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -1730,6 +1730,7 @@ case X$erl_xcomp_bigendian in
esac
AC_C_BIGENDIAN
+AC_C_DOUBLE_MIDDLE_ENDIAN
dnl fdatasync syscall (Unix only)
AC_CHECK_FUNCS([fdatasync])
@@ -1881,9 +1882,10 @@ AC_CHECK_FUNCS([ieee_handler fpsetmask finite isnan isinf res_gethostbyname dlop
pread pwrite writev memmove strerror strerror_r strncasecmp \
gethrtime localtime_r gmtime_r inet_pton mmap mremap memcpy mallopt \
sbrk _sbrk __sbrk brk _brk __brk \
- flockfile fstat strlcpy strlcat setsid posix2time setlocale nl_langinfo poll])
+ flockfile fstat strlcpy strlcat setsid posix2time time2posix \
+ setlocale nl_langinfo poll])
-AC_CHECK_DECLS([posix2time],,,[#include <time.h>])
+AC_CHECK_DECLS([posix2time, time2posix],,,[#include <time.h>])
disable_vfork=false
if test "x$EMU_THR_LIB_NAME" != "x"; then
@@ -1943,6 +1945,64 @@ AC_CHECK_FUNCS([openpty])
AC_CHECK_HEADERS(net/if_dl.h ifaddrs.h netpacket/packet.h)
AC_CHECK_FUNCS([getifaddrs])
+dnl Checks for variables in6addr_any and in6addr_loopback,
+dnl
+dnl They normally declared by netinet/in.h, according to POSIX,
+dnl but not on Windows 7 (Windows SDK 7.1). I would have liked
+dnl to just write AC_CHECK_DECL([in6addr_any], ...) but if doing so,
+dnl the configure check fails erroneously on Linux with the error
+dnl "cannot convert to a pointer type", on a line looking like
+dnl "char *p = (char *) in6addr_any;", so work around that
+dnl with some more code.
+AC_CACHE_CHECK(
+ [whether in6addr_any is declared],
+ [erts_cv_have_in6addr_any],
+ [AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ ]],
+ [[printf("%d", in6addr_any.s6_addr[16]);]]
+ )],
+ [erts_cv_have_in6addr_any=yes],
+ [erts_cv_have_in6addr_any=no]
+ )]
+)
+
+case "$erts_cv_have_in6addr_any" in
+ yes)
+ AC_DEFINE([HAVE_IN6ADDR_ANY], [1],
+ [Define to 1 if you have the variable in6addr_any declared.])
+esac
+
+AC_CACHE_CHECK(
+ [whether in6addr_loopback is declared],
+ [erts_cv_have_in6addr_loopback],
+ [AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ ]],
+ [[printf("%d", in6addr_loopback.s6_addr[16]);]]
+ )],
+ [erts_cv_have_in6addr_loopback=yes],
+ [erts_cv_have_in6addr_loopback=no]
+ )]
+)
+
+case "$erts_cv_have_in6addr_loopback" in
+ yes)
+ AC_DEFINE([HAVE_IN6ADDR_LOOPBACK], [1],
+ [Define to 1 if you have the variable in6addr_loopback declared.])
+esac
+
+AC_CHECK_DECLS([IN6ADDR_ANY_INIT, IN6ADDR_LOOPBACK_INIT], [], [],
+ [#include <netinet/in.h>])
+
dnl ----------------------------------------------------------------------
dnl Checks for features/quirks in the system that affects Erlang.
dnl ----------------------------------------------------------------------
@@ -3515,6 +3575,11 @@ case $host_os in
DED_LDFLAGS="-m32 $DED_LDFLAGS"
fi
;;
+ openbsd*)
+ DED_LD="$CC"
+ DED_LD_FLAG_RUNTIME_LIBRARY_PATH="$CFLAG_RUNTIME_LIBRARY_PATH"
+ DED_LDFLAGS="-shared"
+ ;;
osf*)
# NOTE! Whitespace after -rpath is important.
DED_LD_FLAG_RUNTIME_LIBRARY_PATH="-rpath "
diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile
index 13cf003aad..91e823fdb1 100644
--- a/erts/doc/src/Makefile
+++ b/erts/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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
@@ -55,7 +55,6 @@ XML_REF3_EFILES = \
XML_REF3_FILES = \
driver_entry.xml \
erl_nif.xml \
- erl_set_memory_block.xml \
erl_driver.xml \
erl_prim_loader.xml \
erlang.xml \
@@ -158,9 +157,6 @@ $(SPECDIR)/specs_driver_entry.xml:
$(SPECDIR)/specs_erl_nif.xml:
escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
-o$(dir $@) -module erl_nif
-$(SPECDIR)/specs_erl_set_memory_block.xml:
- escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
- -o$(dir $@) -module erl_set_memory_block
$(SPECDIR)/specs_erl_driver.xml:
escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
-o$(dir $@) -module erl_driver
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index f8a92e1ec4..93d1289e8d 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -533,7 +533,7 @@
<p>Force the <c>compressed</c> option on all ETS tables.
Only intended for test and evaluation.</p>
</item>
- <tag><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. This is default on operating systems that have transparent file naming, i.e. all Unixes except MacOSX.</p>
</item>
@@ -586,7 +586,9 @@
<item>
<p>Sets the maximum number of concurrent processes for this
system. <c><![CDATA[Number]]></c> must be in the range 16..134217727.
- Default is 32768.</p>
+ Default is 32768.</p>
+ <p><em>NOTE</em>: It is possible to choose any value in the range
+ but powers of 2 perform best.</p>
</item>
<tag><marker id="compat_rel"><c><![CDATA[+R ReleaseNumber]]></c></marker></tag>
<item>
diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml
index 4fd74b783e..187c263b60 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>2011</year>
+ <year>2001</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -1067,7 +1067,7 @@ typedef struct ErlIOVec {
</desc>
</func>
<func>
- <name><ret>ErlDrvBinary*</ret><nametext>driver_alloc_binary(ErlDrvSizeT size)</nametext></name>
+ <name><ret>ErlDrvBinary *</ret><nametext>driver_alloc_binary(ErlDrvSizeT size)</nametext></name>
<fsummary>Allocate a driver binary</fsummary>
<desc>
<marker id="driver_alloc_binary"></marker>
@@ -1087,7 +1087,7 @@ typedef struct ErlIOVec {
</desc>
</func>
<func>
- <name><ret>ErlDrvBinary*</ret><nametext>driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size)</nametext></name>
+ <name><ret>ErlDrvBinary *</ret><nametext>driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size)</nametext></name>
<fsummary>Resize a driver binary</fsummary>
<desc>
<marker id="driver_realloc_binary"></marker>
@@ -1277,7 +1277,7 @@ typedef struct ErlIOVec {
</desc>
</func>
<func>
- <name><ret>SysIOVec*</ret><nametext>driver_peekq(ErlDrvPort port, int *vlen)</nametext></name>
+ <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_peekq"></marker>
@@ -1481,7 +1481,7 @@ typedef struct ErlIOVec {
</desc>
</func>
<func>
- <name><ret>char*</ret><nametext>erl_errno_id(int error)</nametext></name>
+ <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_errno_id"></marker>
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 5fc6508aad..f484e9eaf7 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>2011</year>
+ <year>2001</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -472,7 +472,7 @@ typedef enum {
</section>
<funcs>
- <func><name><ret>void*</ret><nametext>enif_alloc(size_t size)</nametext></name>
+ <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>
@@ -489,7 +489,7 @@ typedef enum {
<p>Return true on success or false if allocation failed.</p>
</desc>
</func>
- <func><name><ret>ErlNifEnv*</ret><nametext>enif_alloc_env()</nametext></name>
+ <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
@@ -499,7 +499,7 @@ typedef enum {
<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>
+ <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>
@@ -522,7 +522,7 @@ typedef enum {
<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>
+ <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>
@@ -840,7 +840,7 @@ typedef enum {
<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>
+ <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
@@ -951,7 +951,7 @@ typedef enum {
<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>ErlNifMutex*</ret><nametext>enif_mutex_create(char *name)</nametext></name>
+ <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>
@@ -976,7 +976,7 @@ typedef enum {
<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,
+ <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>
@@ -1005,7 +1005,7 @@ typedef enum {
and <seealso marker="#upgrade">upgrade</seealso>.</p>
</desc>
</func>
- <func><name><ret>void*</ret><nametext>enif_priv_data(ErlNifEnv* env)</nametext></name>
+ <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>
@@ -1033,7 +1033,7 @@ typedef enum {
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>
+ <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>
@@ -1073,7 +1073,7 @@ typedef enum {
<desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_tryrwlock">erl_drv_rwlock_tryrwlock</seealso>.
</p></desc>
</func>
- <func><name><ret>ErlNifPid*</ret><nametext>enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)</nametext></name>
+ <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>
@@ -1129,7 +1129,7 @@ typedef enum {
<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>
+ <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>
@@ -1154,7 +1154,7 @@ typedef enum {
<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>
+ <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>
diff --git a/erts/doc/src/erl_set_memory_block.xml b/erts/doc/src/erl_set_memory_block.xml
deleted file mode 100644
index d77da56d95..0000000000
--- a/erts/doc/src/erl_set_memory_block.xml
+++ /dev/null
@@ -1,172 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE cref SYSTEM "cref.dtd">
-
-<cref>
- <header>
- <copyright>
- <year>1998</year><year>2009</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- 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
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>erl_set_memory_block</title>
- <prepared>Patrik Nyblom</prepared>
- <responsible></responsible>
- <docno></docno>
- <approved></approved>
- <checked></checked>
- <date>98-08-05</date>
- <rev>A</rev>
- <file>erl_set_memory_block.xml</file>
- </header>
- <lib>erl_set_memory_block</lib>
- <libsummary>Custom memory allocation for Erlang on VxWorks&reg;</libsummary>
- <description>
- <p>This documentation is specific to VxWorks.</p>
- <p>The <c><![CDATA[erl_set_memory_block]]></c> function/command initiates custom
- memory allocation for the Erlang emulator. It has to be called
- before the Erlang emulator is started and makes Erlang use one
- single large memory block for all memory allocation.</p>
- <p>The memory within the block can be utilized by other tasks than
- Erlang. This is accomplished by calling the functions
- <c><![CDATA[sys_alloc]]></c>, <c><![CDATA[sys_realloc]]></c> and <c><![CDATA[sys_free]]></c> instead
- of <c><![CDATA[malloc]]></c>, <c><![CDATA[realloc]]></c> and <c><![CDATA[free]]></c> respectively.</p>
- <p>The purpose of this is to avoid problems inherent in the
- VxWorks systems <c><![CDATA[malloc]]></c> library. The memory allocation within the
- large memory block avoids fragmentation by using an "address
- order first fit" algorithm. Another advantage of using a
- separate memory block is that resource reclamation can be made
- more easily when Erlang is stopped.</p>
- <p>The <c><![CDATA[erl_set_memory_block]]></c> function is callable from any C
- program as an ordinary 10 argument function as well as
- from the commandline.</p>
- </description>
- <funcs>
- <func>
- <name><ret>int</ret><nametext>erl_set_memory_block(size_t size, void *ptr, int warn_mixed_malloc, int realloc_always_moves, int use_reclaim, ...)</nametext></name>
- <fsummary>Specify parameters for Erlang internal memory allocation.</fsummary>
- <desc>
- <p>The function is called before Erlang is
- started to specify a large memory block where Erlang can
- maintain memory internally.</p>
- <p>Parameters:</p>
- <taglist>
- <tag>size_t size</tag>
- <item>The size in bytes of Erlang's internal memory block. Has to
- be specified. Note that the VxWorks system uses dynamic
- memory allocation heavily, so leave some memory to the system.</item>
- <tag>void *ptr</tag>
- <item>
- <p>A pointer to the actual memory block of size
- <c><![CDATA[size]]></c>. If this is specified as 0 (NULL), Erlang will
- allocate the memory when starting and will reclaim the
- memory block (as a whole) when stopped.</p>
- <p>If a memory block is allocated and provided here, the
- <c><![CDATA[sys_alloc]]></c> etc routines can still be used after
- the Erlang emulator is stopped. The Erlang emulator can
- also be restarted while other tasks using the memory
- block are running without destroying the memory. If
- Erlang is to be restarted, also set the
- <c><![CDATA[use_reclaim]]></c> flag.</p>
- <p>If 0 is specified here, the Erlang system should not
- be stopped while some other task uses the memory block
- (has called <c><![CDATA[sys_alloc]]></c>).</p>
- </item>
- <tag>int warn_mixed_malloc</tag>
- <item>
- <p>If this flag is set to true (anything else than 0), the
- system will write a warning message on the console if a
- program is mixing normal <c><![CDATA[malloc]]></c> with
- <c><![CDATA[sys_realloc]]></c> or <c><![CDATA[sys_free]]></c>.</p>
- </item>
- <tag>int realloc_always_moves</tag>
- <item>
- <p>If this flag is set to true (anything else than 0), all
- calls to <c><![CDATA[sys_realloc]]></c> result in a moved memory
- block. This can in certain conditions give less
- fragmentation. This flag may be removed in future releases.</p>
- </item>
- <tag>int use_reclaim</tag>
- <item>
- <p>If this flag is set to true (anything else than 0), all
- memory allocated with <c><![CDATA[sys_alloc]]></c> is automatically
- reclaimed as soon as a task exits. This is very useful
- to make writing port programs (and other programs as
- well) easier. Combine this with using the routines
- <c><![CDATA[save_open]]></c> etc. specified in the reclaim.h
- file delivered in the Erlang distribution.</p>
- </item>
- </taglist>
- <p>Return Value:</p>
- <p>Returns 0 (OK) on success, otherwise a value &lt;&gt; 0.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>erl_memory_show(...)</nametext></name>
- <fsummary>A utility similar to VxWorks <c><![CDATA[memShow]]></c>, but for the Erlang memory area.</fsummary>
- <desc>
- <p>Return Value:</p>
- <p>Returns 0 (OK) on success, otherwise a value &lt;&gt; 0.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>erl_mem_info_get(MEM_PART_STATS *stats)</nametext></name>
- <fsummary>A utility similar to VxWorks <c><![CDATA[memPartInfoGet]]></c>, but for the Erlang memory area.</fsummary>
- <desc>
- <p>Parameter:</p>
- <taglist>
- <tag>MEM_PART_STATS *stats</tag>
- <item>A pointer to a MEM_PART_STATS structure as defined in
- <c><![CDATA[<memLib.h>]]></c>. A successful call will fill in all
- fields of the structure, on error all fields are left untouched. </item>
- </taglist>
- <p>Return Value:</p>
- <p>Returns 0 (OK) on success, otherwise a value &lt;&gt; 0</p>
- </desc>
- </func>
- </funcs>
-
- <section>
- <title>NOTES</title>
- <p>The memory block used by Erlang actually does not need to be
- inside the area known to ordinary <c><![CDATA[malloc]]></c>. It is possible
- to set the <c><![CDATA[USER_RESERVED_MEM]]></c> preprocessor symbol when compiling
- the wind kernel and then use user reserved memory for
- Erlang. Erlang can therefor utilize memory above the 32 Mb limit
- of VxWorks on the PowerPC architecture.</p>
- <p>Example:</p>
- <p>In config.h for the wind kernel:</p>
- <code type="none"><![CDATA[
- #undef LOCAL_MEM_AUTOSIZE
- #undef LOCAL_MEM_SIZE
- #undef USER_RESERVED_MEM
-
- #define LOCAL_MEM_SIZE 0x05000000
- #define USER_RESERVED_MEM 0x03000000
- ]]></code>
- <p>In the start-up script/code for the VxWorks node:</p>
- <code type="none"><![CDATA[
-erl_set_memory_block(sysPhysMemTop()-sysMemTop(),sysMemTop(),0,0,1);
- ]]></code>
- <p>Setting the <c><![CDATA[use_reclaim]]></c> flag decreases performance of the
- system, but makes programming much easier. Other similar
- facilities are present in the Erlang system even without using a
- separate memory block. The routines called <c><![CDATA[save_malloc]]></c>,
- <c><![CDATA[save_realloc]]></c> and <c><![CDATA[save_free]]></c> provide the same
- facilities by using VxWorks own <c><![CDATA[malloc]]></c>. Similar routines
- exist for files, see the file <c><![CDATA[reclaim.h]]></c> in the distribution.</p>
- </section>
-</cref>
-
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index e4f245975b..ef0e6fea84 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -65,14 +65,14 @@
<funcs>
<func>
- <name>abs(Number) -> integer() | float()</name>
+ <name name="abs" arity="1" clause_i="1"/>
+ <name name="abs" arity="1" clause_i="2"/>
+ <type variable="Float" name_i="1"/>
+ <type variable="Int" name_i="2"/>
<fsummary>Arithmetical absolute value</fsummary>
- <type>
- <v>Number = number()</v>
- </type>
<desc>
<p>Returns an integer or float which is the arithmetical
- absolute value of <c>Number</c>.</p>
+ absolute value of <c><anno>Float</anno></c> or <c><anno>Int</anno></c>.</p>
<pre>
> <input>abs(-3.33).</input>
3.33
@@ -82,26 +82,19 @@
</desc>
</func>
<func>
- <name>erlang:adler32(Data) -> integer()</name>
+ <name name="adler32" arity="1"/>
<fsummary>Compute adler32 checksum</fsummary>
- <type>
- <v>Data = iodata()</v>
- </type>
<desc>
- <p>Computes and returns the adler32 checksum for <c>Data</c>.</p>
+ <p>Computes and returns the adler32 checksum for <c><anno>Data</anno></c>.</p>
</desc>
</func>
<func>
- <name>erlang:adler32(OldAdler, Data) -> integer()</name>
+ <name name="adler32" arity="2"/>
<fsummary>Compute adler32 checksum</fsummary>
- <type>
- <v>OldAdler = integer()</v>
- <v>Data = iodata()</v>
- </type>
<desc>
<p>Continue computing the adler32 checksum by combining
- the previous checksum, <c>OldAdler</c>, with the checksum of
- <c>Data</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),
@@ -114,12 +107,8 @@
</desc>
</func>
<func>
- <name>erlang:adler32_combine(FirstAdler, SecondAdler, SecondSize) -> integer()</name>
+ <name name="adler32_combine" arity="3"/>
<fsummary>Combine two adler32 checksums</fsummary>
- <type>
- <v>FirstAdler = SecondAdler = integer()</v>
- <v>SecondSize = integer()</v>
- </type>
<desc>
<p>Combines two previously computed adler32 checksums.
This computation requires the size of the data object for
@@ -138,18 +127,14 @@
</desc>
</func>
<func>
- <name>erlang:append_element(Tuple1, Term) -> Tuple2</name>
+ <name name="append_element" arity="2"/>
<fsummary>Append an extra element to a tuple</fsummary>
- <type>
- <v>Tuple1 = Tuple2 = tuple()</v>
- <v>Term = term()</v>
- </type>
<desc>
<p>Returns a new tuple which has one element more than
- <c>Tuple1</c>, and contains the elements in <c>Tuple1</c>
- followed by <c>Term</c> as the last element. Semantically
+ <c><anno>Tuple1</anno></c>, and contains the elements in <c><anno>Tuple1</anno></c>
+ followed by <c><anno>Term</anno></c> as the last element. Semantically
equivalent to
- <c>list_to_tuple(tuple_to_list(Tuple) ++ [Term])</c>, but much
+ <c>list_to_tuple(tuple_to_list(<anno>Tuple1</anno>) ++ [<anno>Term</anno>])</c>, but much
faster.</p>
<pre>
> <input>erlang:append_element({one, two}, three).</input>
@@ -204,27 +189,24 @@
</desc>
</func>
<func>
- <name>atom_to_binary(Atom, Encoding) -> binary()</name>
+ <name name="atom_to_binary" arity="2"/>
<fsummary>Return the binary representation of an atom</fsummary>
- <type>
- <v>Atom = atom()</v>
- <v>Encoding = latin1 | utf8 | unicode</v>
- </type>
<desc>
<p>Returns a binary which corresponds to the text
- representation of <c>Atom</c>. If <c>Encoding</c>
+ representation of <c><anno>Atom</anno></c>. If <c><anno>Encoding</anno></c>
is <c>latin1</c>, there will be one byte for each character
- in the text representation. If <c>Encoding</c> is <c>utf8</c> or
+ in the text representation. If <c><anno>Encoding</anno></c> is
+ <c>utf8</c> or
<c>unicode</c>, the characters will be encoded using UTF-8
(meaning that characters from 16#80 up to 0xFF will be
encoded in two bytes).</p>
- <note><p>Currently, <c>atom_to_binary(Atom, latin1)</c> can
+ <note><p>Currently, <c>atom_to_binary(<anno>Atom</anno>, latin1)</c> can
never fail because the text representation of an atom can only contain
characters from 0 to 16#FF. In a future release, the text representation
of atoms might be allowed to contain any Unicode character
- and <c>atom_to_binary(Atom, latin1)</c> will fail if the
- text representation for the <c>Atom</c> contains a Unicode
+ and <c>atom_to_binary(<anno>Atom</anno>, latin1)</c> will fail if the
+ text representation for the <c><anno>Atom</anno></c> contains a Unicode
character greater than 16#FF.</p></note>
<pre>
@@ -233,30 +215,21 @@
</desc>
</func>
<func>
- <name>atom_to_list(Atom) -> string()</name>
+ <name name="atom_to_list" arity="1"/>
<fsummary>Text representation of an atom</fsummary>
- <type>
- <v>Atom = atom()</v>
- </type>
<desc>
<p>Returns a string which corresponds to the text
- representation of <c>Atom</c>.</p>
+ representation of <c><anno>Atom</anno></c>.</p>
<pre>
> <input>atom_to_list('Erlang').</input>
"Erlang"</pre>
</desc>
</func>
<func>
- <name>binary_part(Subject, PosLen) -> binary()</name>
+ <name name="binary_part" arity="2"/>
<fsummary>Extracts a part of a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>PosLen = {Start,Length}</v>
- <v>Start = integer() >= 0</v>
- <v>Length = integer() >= 0</v>
- </type>
- <desc>
- <p>Extracts the part of the binary described by <c>PosLen</c>.</p>
+ <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:</p>
@@ -266,53 +239,44 @@
&lt;&lt;6,7,8,9,10&gt;&gt;
</code>
- <p>If <c>PosLen</c> in any way references outside the binary, a <c>badarg</c> exception is raised.</p>
+ <p>If <c><anno>PosLen</anno></c> in any way references outside the binary, a <c>badarg</c> exception is raised.</p>
- <p><c>Start</c> is zero-based, i.e.:</p>
+ <p><c><anno>Start</anno></c> is zero-based, i.e.:</p>
<code>
1> Bin = &lt;&lt;1,2,3&gt;&gt;
2> binary_part(Bin,{0,2}).
&lt;&lt;1,2&gt;&gt;
</code>
- <p>See the STDLIB module <c>binary</c> for details about the <c>PosLen</c> semantics.</p>
+ <p>See the STDLIB module <c>binary</c> for details about the <c><anno>PosLen</anno></c> semantics.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>binary_part(Subject, Start, Length) -> binary()</name>
+ <name name="binary_part" arity="3"/>
<fsummary>Extracts a part of a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Start = integer() >= 0</v>
- <v>Length = integer() >= 0</v>
- </type>
<desc>
- <p>The same as <c>binary_part(Subject, {Pos, Len})</c>.</p>
+ <p>The same as <c>binary_part(<anno>Subject</anno>, {<anno>Start</anno>, <anno>Length</anno>})</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>binary_to_atom(Binary, Encoding) -> atom()</name>
+ <name name="binary_to_atom" arity="2"/>
<fsummary>Convert from text representation to an atom</fsummary>
- <type>
- <v>Binary = binary()</v>
- <v>Encoding = latin1 | utf8 | unicode</v>
- </type>
<desc>
<p>Returns the atom whose text representation is
- <c>Binary</c>. If <c>Encoding</c> is <c>latin1</c>, no
- translation of bytes in the binary is done. If <c>Encoding</c>
+ <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; furthermore, only Unicode characters up
to 0xFF are allowed.</p>
- <note><p><c>binary_to_atom(Binary, utf8)</c> will fail if
+ <note><p><c>binary_to_atom(<anno>Binary</anno>, utf8)</c> will fail if
the binary contains Unicode characters greater than 16#FF.
In a future release, such Unicode characters might be allowed
- and <c>binary_to_atom(Binary, utf8)</c>
+ and <c>binary_to_atom(<anno>Binary</anno>, utf8)</c>
will not fail in that case.</p></note>
<pre>
@@ -325,12 +289,8 @@
</desc>
</func>
<func>
- <name>binary_to_existing_atom(Binary, Encoding) -> atom()</name>
+ <name name="binary_to_existing_atom" arity="2"/>
<fsummary>Convert from text representation to an atom</fsummary>
- <type>
- <v>Binary = binary()</v>
- <v>Encoding = latin1 | utf8 | unicode</v>
- </type>
<desc>
<p>Works like <seealso marker="#binary_to_atom/2">binary_to_atom/2</seealso>,
but the atom must already exist.</p>
@@ -338,27 +298,21 @@
</desc>
</func>
<func>
- <name>binary_to_list(Binary) -> [char()]</name>
+ <name name="binary_to_list" arity="1"/>
<fsummary>Convert a binary to a list</fsummary>
- <type>
- <v>Binary = binary()</v>
- </type>
<desc>
<p>Returns a list of integers which correspond to the bytes of
- <c>Binary</c>.</p>
+ <c><anno>Binary</anno></c>.</p>
</desc>
</func>
<func>
- <name>binary_to_list(Binary, Start, Stop) -> [char()]</name>
+ <name name="binary_to_list" arity="3"/>
<fsummary>Convert part of a binary to a list</fsummary>
- <type>
- <v>Binary = binary()</v>
- <v>Start = Stop = 1..byte_size(Binary)</v>
- </type>
+ <type_desc variable="Start">1..byte_size(<anno>Binary</anno>)</type_desc>
<desc>
<p>As <c>binary_to_list/1</c>, but returns a list of integers
- corresponding to the bytes from position <c>Start</c> to
- position <c>Stop</c> in <c>Binary</c>. Positions in the
+ corresponding to the bytes from position <c><anno>Start</anno></c> to
+ position <c><anno>Stop</anno></c> in <c><anno>Binary</anno></c>. Positions in the
binary are numbered starting from 1.</p>
<note><p>This function's indexing style of using one-based indices for
@@ -368,27 +322,21 @@
</desc>
</func>
<func>
- <name>bitstring_to_list(Bitstring) -> [char()|bitstring()]</name>
+ <name name="bitstring_to_list" arity="1"/>
<fsummary>Convert a bitstring to a list</fsummary>
- <type>
- <v>Bitstring = bitstring()</v>
- </type>
<desc>
<p>Returns a list of integers which correspond to the bytes of
- <c>Bitstring</c>. If the number of bits in the binary is not
+ <c><anno>Bitstring</anno></c>. If the number of bits in the binary is not
divisible by 8, the last element of the list will be a bitstring
containing the remaining bits (1 up to 7 bits).</p>
</desc>
</func>
<func>
- <name>binary_to_term(Binary) -> term()</name>
+ <name name="binary_to_term" arity="1"/>
<fsummary>Decode an Erlang external term format binary</fsummary>
- <type>
- <v>Binary = <seealso marker="#type-ext_binary">ext_binary()</seealso></v>
- </type>
<desc>
<p>Returns an Erlang term which is the result of decoding
- the binary object <c>Binary</c>, which must be encoded
+ the 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
@@ -401,12 +349,8 @@
</desc>
</func>
<func>
- <name>binary_to_term(Binary, Opts) -> term()</name>
+ <name name="binary_to_term" arity="2"/>
<fsummary>Decode an Erlang external term format binary</fsummary>
- <type>
- <v>Opts = [safe]</v>
- <v>Binary = <seealso marker="#type-ext_binary">ext_binary()</seealso></v>
- </type>
<desc>
<p>As <c>binary_to_term/1</c>, but takes options that affect decoding
of the binary.</p>
@@ -436,13 +380,10 @@
</desc>
</func>
<func>
- <name>bit_size(Bitstring) -> integer() >= 0</name>
+ <name name="bit_size" arity="1"/>
<fsummary>Return the size of a bitstring</fsummary>
- <type>
- <v>Bitstring = bitstring()</v>
- </type>
<desc>
- <p>Returns an integer which is the size in bits of <c>Bitstring</c>.</p>
+ <p>Returns an integer which is the size in bits of <c><anno>Bitstring</anno></c>.</p>
<pre>
> <input>bit_size(&lt;&lt;433:16,3:3&gt;&gt;).</input>
19
@@ -452,11 +393,8 @@
</desc>
</func>
<func>
- <name>erlang:bump_reductions(Reductions) -> void()</name>
+ <name name="bump_reductions" arity="1"/>
<fsummary>Increment the reduction counter</fsummary>
- <type>
- <v>Reductions = integer() >= 0</v>
- </type>
<desc>
<p>This implementation-dependent function increments
the reduction counter for the calling process. In the Beam
@@ -472,14 +410,11 @@
</desc>
</func>
<func>
- <name>byte_size(Bitstring) -> integer() >= 0</name>
+ <name name="byte_size" arity="1"/>
<fsummary>Return the size of a bitstring (or binary)</fsummary>
- <type>
- <v>Bitstring = bitstring()</v>
- </type>
<desc>
<p>Returns an integer which is the number of bytes needed to contain
- <c>Bitstring</c>. (That is, if the number of bits in <c>Bitstring</c> is not
+ <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 will be rounded <em>up</em>.)</p>
<pre>
> <input>byte_size(&lt;&lt;433:16,3:3&gt;&gt;).</input>
@@ -490,21 +425,17 @@
</desc>
</func>
<func>
- <name>erlang:cancel_timer(TimerRef) -> Time | false</name>
+ <name name="cancel_timer" arity="1"/>
<fsummary>Cancel a timer</fsummary>
- <type>
- <v>TimerRef = reference()</v>
- <v>Time = integer() >= 0</v>
- </type>
<desc>
- <p>Cancels a timer, where <c>TimerRef</c> was returned by
+ <p>Cancels a timer, where <c><anno>TimerRef</anno></c> was returned by
either
<seealso marker="#send_after/3">erlang:send_after/3</seealso>
or
<seealso marker="#start_timer/3">erlang:start_timer/3</seealso>.
If the timer is there to be removed, the function returns
the time in milliseconds left until the timer would have expired,
- otherwise <c>false</c> (which means that <c>TimerRef</c> was
+ otherwise <c>false</c> (which means that <c><anno>TimerRef</anno></c> was
never a timer, that it has already been cancelled, or that it
has already delivered its message).</p>
<p>See also
@@ -518,27 +449,20 @@
</func>
<func>
- <name>check_old_code(Module) -> boolean()</name>
+ <name name="check_old_code" arity="1"/>
<fsummary>Check if a module has old code</fsummary>
- <type>
- <v>Module = atom()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if the <c>Module</c> has old code,
+ <p>Returns <c>true</c> if the <c><anno>Module</anno></c> has old code,
and <c>false</c> otherwise.</p>
<p>See also <seealso marker="kernel:code">code(3)</seealso>.</p>
</desc>
</func>
<func>
- <name>check_process_code(Pid, Module) -> boolean()</name>
+ <name name="check_process_code" arity="2"/>
<fsummary>Check if a process is executing old code for a module</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Module = atom()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if the process <c>Pid</c> is executing
- old code for <c>Module</c>. That is, if the current call of
+ <p>Returns <c>true</c> if the process <c><anno>Pid</anno></c> is executing
+ old code for <c><anno>Module</anno></c>. That is, if the current call of
the process executes old code for this module, or if the
process has references to old code for this module, or if the
process contains funs that references old code for this
@@ -550,26 +474,19 @@ false</pre>
</desc>
</func>
<func>
- <name>erlang:crc32(Data) -> integer() >= 0</name>
+ <name name="crc32" arity="1"/>
<fsummary>Compute crc32 (IEEE 802.3) checksum</fsummary>
- <type>
- <v>Data = iodata()</v>
- </type>
<desc>
- <p>Computes and returns the crc32 (IEEE 802.3 style) checksum for <c>Data</c>.</p>
+ <p>Computes and returns the crc32 (IEEE 802.3 style) checksum for <c><anno>Data</anno></c>.</p>
</desc>
</func>
<func>
- <name>erlang:crc32(OldCrc, Data) -> integer() >= 0</name>
+ <name name="crc32" arity="2"/>
<fsummary>Compute crc32 (IEEE 802.3) checksum</fsummary>
- <type>
- <v>OldCrc = integer() >= 0</v>
- <v>Data = iodata()</v>
- </type>
<desc>
<p>Continue computing the crc32 checksum by combining
- the previous checksum, <c>OldCrc</c>, with the checksum of
- <c>Data</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),
@@ -582,12 +499,8 @@ false</pre>
</desc>
</func>
<func>
- <name>erlang:crc32_combine(FirstCrc, SecondCrc, SecondSize) -> integer() >= 0</name>
+ <name name="crc32_combine" arity="3"/>
<fsummary>Combine two crc32 (IEEE 802.3) checksums</fsummary>
- <type>
- <v>FirstCrc = SecondCrc = integer() >= 0</v>
- <v>SecondSize = integer() >= 0</v>
- </type>
<desc>
<p>Combines two previously computed crc32 checksums.
This computation requires the size of the data object for
@@ -606,11 +519,8 @@ false</pre>
</desc>
</func>
<func>
- <name>date() -> Date</name>
+ <name name="date" arity="0"/>
<fsummary>Current date</fsummary>
- <type>
- <v>Date = <seealso marker="calendar#type-date">calendar:date()</seealso></v>
- </type>
<desc>
<p>Returns the current date as <c>{Year, Month, Day}</c>.</p>
<p>The time zone and daylight saving time correction depend on
@@ -621,47 +531,24 @@ false</pre>
</desc>
</func>
<func>
- <name>erlang:decode_packet(Type,Bin,Options) -> {ok,Packet,Rest} | {more,Length} | {error,Reason}</name>
+ <name name="decode_packet" arity="3"/>
<fsummary>Extracts a protocol packet from a binary</fsummary>
- <type>
- <v>Bin = binary()</v>
- <v>Options = [Opt]</v>
- <v>Packet = binary() | HttpPacket</v>
- <v>Rest = binary()</v>
- <v>Length = integer() > 0 | undefined</v>
- <v>Reason = term()</v>
- <v>&nbsp;Type, Opt -- see below</v>
- <v></v>
- <v>HttpPacket = HttpRequest | HttpResponse | HttpHeader | http_eoh | HttpError</v>
- <v>HttpRequest = {http_request, HttpMethod, HttpUri, HttpVersion}</v>
- <v>HttpResponse = {http_response, HttpVersion, integer(), HttpString}</v>
- <v>HttpHeader = {http_header, integer(), HttpField, Reserved=term(), Value=HttpString}</v>
- <v>HttpError = {http_error, HttpString}</v>
- <v>HttpMethod = HttpMethodAtom | HttpString</v>
- <v>HttpMethodAtom = 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'TRACE'</v>
- <v>HttpUri = '*' | {absoluteURI, http|https, Host=HttpString, Port=integer()|undefined, Path=HttpString} |
- {scheme, Scheme=HttpString, HttpString} | {abs_path, HttpString} | HttpString</v>
- <v>HttpVersion = {Major=integer(), Minor=integer()}</v>
- <v>HttpString = string() | binary()</v>
- <v>HttpField = HttpFieldAtom | HttpString</v>
- <v>HttpFieldAtom = 'Cache-Control' | 'Connection' | 'Date' | 'Pragma' | 'Transfer-Encoding' | 'Upgrade' | 'Via' | 'Accept' | 'Accept-Charset' | 'Accept-Encoding' | 'Accept-Language' | 'Authorization' | 'From' | 'Host' | 'If-Modified-Since' | 'If-Match' | 'If-None-Match' | 'If-Range' | 'If-Unmodified-Since' | 'Max-Forwards' | 'Proxy-Authorization' | 'Range' | 'Referer' | 'User-Agent' | 'Age' | 'Location' | 'Proxy-Authenticate' | 'Public' | 'Retry-After' | 'Server' | 'Vary' | 'Warning' | 'Www-Authenticate' | 'Allow' | 'Content-Base' | 'Content-Encoding' | 'Content-Language' | 'Content-Length' | 'Content-Location' | 'Content-Md5' | 'Content-Range' | 'Content-Type' | 'Etag' | 'Expires' | 'Last-Modified' | 'Accept-Ranges' | 'Set-Cookie' | 'Set-Cookie2' | 'X-Forwarded-For' | 'Cookie' | 'Keep-Alive' | 'Proxy-Connection'</v>
- <v></v>
- </type>
- <desc>
- <p>Decodes the binary <c>Bin</c> according to the packet
- protocol specified by <c>Type</c>. Very similar to the packet
- handling done by sockets with the option {packet,Type}.</p>
- <p>If an entire packet is contained in <c>Bin</c> it is
+ <desc>
+
+ <p>Decodes the binary <c><anno>Bin</anno></c> according to the packet
+ protocol specified by <c><anno>Type</anno></c>. Very similar to the packet
+ handling done by sockets with the option {packet,<anno>Type</anno>}.</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,Packet,Rest}</c>.</p>
- <p>If <c>Bin</c> does not contain the entire packet,
- <c>{more,Length}</c> is returned. <c>Length</c> is either the
+ <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 not known. <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,Reason}</c> is returned.</p>
- <p>The following values of <c>Type</c> are valid:</p>
+ <c>{error,<anno>Reason</anno>}</c> is returned.</p>
+ <p>The following values of <c><anno>Type</anno></c> are valid:</p>
<taglist>
<tag><c>raw | 0</c></tag>
<item>
@@ -699,15 +586,15 @@ false</pre>
<item>
<p>The Hypertext Transfer Protocol. The packets
are returned with the format according to
- <c>HttpPacket</c> described above. A packet is either a
+ <c><anno>HttpPacket</anno></c> described above. A packet is either a
request, a response, a header or an end of header
- mark. Invalid lines are returned as <c>HttpError</c>.</p>
+ 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.</p>
<p>The protocol type <c>http</c> should only be used for
- the first line when a <c>HttpRequest</c> or a
- <c>HttpResponse</c> is expected. The following calls
- should use <c>httph</c> to get <c>HttpHeader</c>'s until
+ the first line when a <c><anno>HttpRequest</anno></c> or a
+ <c><anno>HttpResponse</anno></c> is expected. The following calls
+ should use <c>httph</c> to get <c><anno>HttpHeader</anno></c>'s until
<c>http_eoh</c> is returned that 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> will return
@@ -716,14 +603,14 @@ false</pre>
</taglist>
<p>The following options are available:</p>
<taglist>
- <tag><c>{packet_size, integer()}</c></tag>
+ <tag><c>{packet_size, integer() >= 0}</c></tag>
<item><p>Sets the max allowed size of the packet body. If
the packet header indicates that the length of the
packet is longer than the max allowed length, the packet
is considered invalid. Default is 0 which means no
size limit.</p>
</item>
- <tag><c>{line_length, integer()}</c></tag>
+ <tag><c>{line_length, integer() >= 0}</c></tag>
<item><p>For packet type <c>line</c>, truncate lines longer
than the indicated length.</p>
<p>Option <c>line_length</c> also applies to <c>http*</c>
@@ -740,13 +627,10 @@ false</pre>
</desc>
</func>
<func>
- <name>delete_module(Module) -> true | undefined</name>
+ <name name="delete_module" arity="1"/>
<fsummary>Make the current code for a module old</fsummary>
- <type>
- <v>Module = atom()</v>
- </type>
<desc>
- <p>Makes the current code for <c>Module</c> become old code, and
+ <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>
@@ -760,27 +644,24 @@ false</pre>
</desc>
</func>
<func>
- <name>demonitor(MonitorRef) -> true</name>
+ <name name="demonitor" arity="1"/>
<fsummary>Stop monitoring</fsummary>
- <type>
- <v>MonitorRef = reference()</v>
- </type>
<desc>
- <p>If <c>MonitorRef</c> is a reference which the calling process
+ <p>If <c><anno>MonitorRef</anno></c> is a reference which the calling process
obtained by calling
<seealso marker="#monitor/2">monitor/2</seealso>,
this monitoring is turned off. If the monitoring is already
turned off, nothing happens.</p>
- <p>Once <c>demonitor(MonitorRef)</c> has returned it is
- guaranteed that no <c>{'DOWN', MonitorRef, _, _, _}</c> message
+ <p>Once <c>demonitor(<anno>MonitorRef</anno>)</c> has returned it is
+ guaranteed that no <c>{'DOWN', <anno>MonitorRef</anno>, _, _, _}</c> message
due to the monitor will be placed in the caller's message queue
- in the future. A <c>{'DOWN', MonitorRef, _, _, _}</c> message
+ in the future. A <c>{'DOWN', <anno>MonitorRef</anno>, _, _, _}</c> message
might have been placed in the caller's message queue prior to
the call, though. Therefore, in most cases, it is advisable
to remove such a <c>'DOWN'</c> message from the message queue
after monitoring has been stopped.
- <seealso marker="#demonitor/2">demonitor(MonitorRef, [flush])</seealso> can be used instead of
- <c>demonitor(MonitorRef)</c> if this cleanup is wanted.</p>
+ <seealso marker="#demonitor/2">demonitor(<anno>MonitorRef</anno>, [flush])</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 asynchronous, i.e., the monitor was active
@@ -792,35 +673,30 @@ false</pre>
asynchronously send a "demonitor signal" to the monitored entity
and ignore any future results of the monitor. </p>
</note>
- <p>Failure: It is an error if <c>MonitorRef</c> refers to a
+ <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>MonitorRef</c> is a remote
+ <c>badarg</c> (for example if <c><anno>MonitorRef</anno></c> is a remote
reference).</p>
</desc>
</func>
<func>
- <name>demonitor(MonitorRef, OptionList) -> boolean()</name>
+ <name name="demonitor" arity="2"/>
<fsummary>Stop monitoring</fsummary>
- <type>
- <v>MonitorRef = reference()</v>
- <v>OptionList = [Option]</v>
- <v>&nbsp;Option = flush | info</v>
- </type>
<desc>
<p>The returned value is <c>true</c> unless <c>info</c> is part
- of <c>OptionList</c>.
+ of <c><anno>OptionList</anno></c>.
</p>
- <p><c>demonitor(MonitorRef, [])</c> is equivalent to
- <seealso marker="#demonitor/1">demonitor(MonitorRef)</seealso>.</p>
- <p>Currently the following <c>Option</c>s are valid:</p>
+ <p><c>demonitor(<anno>MonitorRef</anno>, [])</c> is equivalent to
+ <seealso marker="#demonitor/1">demonitor(<anno>MonitorRef</anno>)</seealso>.</p>
+ <p>Currently the following <c><anno>Option</anno></c>s are valid:</p>
<taglist>
<tag><c>flush</c></tag>
<item>
- <p>Remove (one) <c>{_, MonitorRef, _, _, _}</c> message,
+ <p>Remove (one) <c>{_, <anno>MonitorRef</anno>, _, _, _}</c> message,
if there is one, from the caller's message queue after
monitoring has been stopped.</p>
- <p>Calling <c>demonitor(MonitorRef, [flush])</c>
+ <p>Calling <c>demonitor(<anno>MonitorRef</anno>, [flush])</c>
is equivalent to the following, but more efficient:</p>
<code type="none">
@@ -860,8 +736,8 @@ false</pre>
<note>
<p>More options may be added in the future.</p>
</note>
- <p>Failure: <c>badarg</c> if <c>OptionList</c> is not a list, or
- if <c>Option</c> is not a valid option, or the same failure as for
+ <p>Failure: <c>badarg</c> if <c><anno>OptionList</anno></c> is not a list, or
+ if <c><anno>Option</anno></c> is not a valid option, or the same failure as for
<seealso marker="#demonitor/1">demonitor/1</seealso></p>
</desc>
</func>
@@ -878,13 +754,10 @@ false</pre>
</desc>
</func>
<func>
- <name>erlang:display(Term) -> true</name>
+ <name name="display" arity="1"/>
<fsummary>Print a term on standard output</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Prints a text representation of <c>Term</c> on the standard
+ <p>Prints a text representation of <c><anno>Term</anno></c> on the standard
output.</p>
<warning>
<p>This BIF is intended for debugging only.</p>
@@ -892,15 +765,12 @@ false</pre>
</desc>
</func>
<func>
- <name>element(N, Tuple) -> term()</name>
+ <name name="element" arity="2"/>
+ <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc>
<fsummary>Get Nth element of a tuple</fsummary>
- <type>
- <v>N = 1..tuple_size(Tuple)</v>
- <v>Tuple = tuple()</v>
- </type>
<desc>
- <p>Returns the <c>N</c>th element (numbering from 1) of
- <c>Tuple</c>.</p>
+ <p>Returns the <c><anno>N</anno></c>th element (numbering from 1) of
+ <c><anno>Tuple</anno></c>.</p>
<pre>
> <input>element(2, {a, b, c}).</input>
b</pre>
@@ -908,11 +778,8 @@ b</pre>
</desc>
</func>
<func>
- <name>erase() -> [{Key, Val}]</name>
+ <name name="erase" arity="0"/>
<fsummary>Return and delete the process dictionary</fsummary>
- <type>
- <v>Key = Val = term()</v>
- </type>
<desc>
<p>Returns the process dictionary and deletes it.</p>
<pre>
@@ -923,15 +790,12 @@ b</pre>
</desc>
</func>
<func>
- <name>erase(Key) -> Val | undefined</name>
+ <name name="erase" arity="1"/>
<fsummary>Return and delete a value from the process dictionary</fsummary>
- <type>
- <v>Key = Val = term()</v>
- </type>
<desc>
- <p>Returns the value <c>Val</c> associated with <c>Key</c> and
+ <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>Key</c>.</p>
+ <c>undefined</c> if no value is associated with <c><anno>Key</anno></c>.</p>
<pre>
> <input>put(key1, {merry, lambs, are, playing}),</input>
<input>X = erase(key1),</input>
@@ -940,15 +804,12 @@ b</pre>
</desc>
</func>
<func>
- <name>error(Reason)</name>
+ <name name="error" arity="1"/>
<fsummary>Stop execution with a given reason</fsummary>
- <type>
- <v>Reason = term()</v>
- </type>
<desc>
<p>Stops the execution of the calling process with the reason
- <c>Reason</c>, where <c>Reason</c> is any term. The actual
- exit reason will be <c>{Reason, Where}</c>, where <c>Where</c>
+ <c><anno>Reason</anno></c>, where <c><anno>Reason</anno></c> is any term. The actual
+ exit reason will be <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>
@@ -962,18 +823,14 @@ b</pre>
</desc>
</func>
<func>
- <name>error(Reason, Args)</name>
+ <name name="error" arity="2"/>
<fsummary>Stop execution with a given reason</fsummary>
- <type>
- <v>Reason = term()</v>
- <v>Args = [term()]</v>
- </type>
<desc>
<p>Stops the execution of the calling process with the reason
- <c>Reason</c>, where <c>Reason</c> is any term. The actual
- exit reason will be <c>{Reason, Where}</c>, where <c>Where</c>
+ <c><anno>Reason</anno></c>, where <c><anno>Reason</anno></c> is any term. The actual
+ exit reason will be <c>{<anno>Reason</anno>, Where}</c>, where <c>Where</c>
is a list of the functions most recently called (the current
- function first). <c>Args</c> is expected to be the list of
+ function first). <c><anno>Args</anno></c> is expected to be the list of
arguments for the current function; in Beam it will be used
to provide the actual arguments for the current function in
the <c>Where</c> term. Since evaluating this function causes
@@ -981,14 +838,11 @@ b</pre>
</desc>
</func>
<func>
- <name>exit(Reason)</name>
+ <name name="exit" arity="1"/>
<fsummary>Stop execution with a given reason</fsummary>
- <type>
- <v>Reason = term()</v>
- </type>
<desc>
<p>Stops the execution of the calling process with the exit
- reason <c>Reason</c>, where <c>Reason</c> is any term. Since
+ reason <c><anno>Reason</anno></c>, where <c><anno>Reason</anno></c> is any term. Since
evaluating this function causes the process to terminate, it
has no return value.</p>
<pre>
@@ -999,78 +853,67 @@ b</pre>
</desc>
</func>
<func>
- <name>exit(Pid, Reason) -> true</name>
+ <name name="exit" arity="2"/>
<fsummary>Send an exit signal to a process</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>Sends an exit signal with exit reason <c>Reason</c> to
- the process <c>Pid</c>.</p>
- <p>The following behavior apply if <c>Reason</c> is any term
+ <desc>
+ <p>Sends an exit signal with exit reason <c><anno>Reason</anno></c> to
+ the process <c><anno>Pid</anno></c>.</p>
+ <p>The following behavior apply if <c><anno>Reason</anno></c> is any term
except <c>normal</c> or <c>kill</c>:</p>
- <p>If <c>Pid</c> is not trapping exits, <c>Pid</c> itself will
- exit with exit reason <c>Reason</c>. If <c>Pid</c> is trapping
+ <p>If <c><anno>Pid</anno></c> is not trapping exits, <c><anno>Pid</anno></c> itself will
+ exit with exit reason <c><anno>Reason</anno></c>. If <c><anno>Pid</anno></c> is trapping
exits, the exit signal is transformed into a message
- <c>{'EXIT', From, Reason}</c> and delivered to the message
- queue of <c>Pid</c>. <c>From</c> is the pid of the process
+ <c>{'EXIT', From, <anno>Reason</anno>}</c> and delivered to the message
+ queue of <c><anno>Pid</anno></c>. <c>From</c> is the pid of the process
which sent the exit signal. See also
<seealso marker="#process_flag/2">process_flag/2</seealso>.</p>
- <p>If <c>Reason</c> is the atom <c>normal</c>, <c>Pid</c> will
+ <p>If <c><anno>Reason</anno></c> is the atom <c>normal</c>, <c><anno>Pid</anno></c> will
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>Reason</c> is the atom <c>kill</c>, that is if
- <c>exit(Pid, kill)</c> is called, an untrappable exit signal
- is sent to <c>Pid</c> which will unconditionally exit with
+ <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 will unconditionally exit with
exit reason <c>killed</c>.</p>
</desc>
</func>
<func>
- <name>erlang:external_size(Term) -> integer() >= 0</name>
+ <name name="external_size" arity="1"/>
<fsummary>Calculate the maximum size for a term encoded in the Erlang
external term format</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
<p>Calculates, without doing the encoding, the maximum byte size for
a term encoded in the Erlang external term format. The following
condition applies always:</p>
<p>
<pre>
-> <input>Size1 = byte_size(term_to_binary(Term)),</input>
-> <input>Size2 = erlang:external_size(Term),</input>
+> <input>Size1 = byte_size(term_to_binary(<anno>Term</anno>)),</input>
+> <input>Size2 = erlang:external_size(<anno>Term</anno>),</input>
> <input>true = Size1 =&lt; Size2.</input>
true
</pre>
</p>
- <p>This is equivalent to a call to: <code>erlang:external_size(Term, [])
+ <p>This is equivalent to a call to: <code>erlang:external_size(<anno>Term</anno>, [])
</code></p>
</desc>
</func>
<func>
- <name>erlang:external_size(Term, [Option]) -> integer() >= 0</name>
+ <name name="external_size" arity="2"/>
<fsummary>Calculate the maximum size for a term encoded in the Erlang
external term format</fsummary>
- <type>
- <v>Term = term()</v>
- <v>Option = {minor_version, Version}</v>
- </type>
<desc>
<p>Calculates, without doing the encoding, the maximum byte size for
a term encoded in the Erlang external term format. The following
condition applies always:</p>
<p>
<pre>
-> <input>Size1 = byte_size(term_to_binary(Term, Options)),</input>
-> <input>Size2 = erlang:external_size(Term, Options),</input>
+> <input>Size1 = byte_size(term_to_binary(<anno>Term</anno>, <anno>Options</anno>)),</input>
+> <input>Size2 = erlang:external_size(<anno>Term</anno>, <anno>Options</anno>),</input>
> <input>true = Size1 =&lt; Size2.</input>
true
</pre>
</p>
- <p>The option <c>{minor_version, Version}</c> specifies how floats
+ <p>The option <c>{minor_version, <anno>Version</anno>}</c> specifies how floats
are encoded. See
<seealso marker="#term_to_binary/2">term_to_binary/2</seealso> for
a more detailed description.
@@ -1078,13 +921,10 @@ true
</desc>
</func>
<func>
- <name>float(Number) -> float()</name>
+ <name name="float" arity="1"/>
<fsummary>Convert a number to a float</fsummary>
- <type>
- <v>Number = number()</v>
- </type>
<desc>
- <p>Returns a float by converting <c>Number</c> to a float.</p>
+ <p>Returns a float by converting <c><anno>Number</anno></c> to a float.</p>
<pre>
> <input>float(55).</input>
55.0</pre>
@@ -1101,14 +941,11 @@ true
</desc>
</func>
<func>
- <name>float_to_list(Float) -> string()</name>
+ <name name="float_to_list" arity="1"/>
<fsummary>Text representation of a float</fsummary>
- <type>
- <v>Float = float()</v>
- </type>
<desc>
<p>Returns a string which corresponds to the text
- representation of <c>Float</c>.</p>
+ representation of <c><anno>Float</anno></c>.</p>
<pre>
> <input>float_to_list(7.0).</input>
"7.00000000000000000000e+00"</pre>
@@ -1213,18 +1050,15 @@ true
</desc>
</func>
<func>
- <name>erlang:fun_info(Fun, Item) -> {Item, Info}</name>
+ <name name="fun_info" arity="2"/>
+ <type name="fun_info_item"/>
<fsummary>Information about a fun</fsummary>
- <type>
- <v>Fun = fun()</v>
- <v>Item, Info -- see below</v>
- </type>
- <desc>
- <p>Returns information about <c>Fun</c> as specified by
- <c>Item</c>, in the form <c>{Item,Info}</c>.</p>
- <p>For any fun, <c>Item</c> can be any of the atoms
+ <desc>
+ <p>Returns information about <c><anno>Fun</anno></c> as specified by
+ <c><anno>Item</anno></c>, in the form <c>{<anno>Item</anno>,<anno>Info</anno>}</c>.</p>
+ <p>For any fun, <c><anno>Item</anno></c> can be any of the atoms
<c>module</c>, <c>name</c>, <c>arity</c>, <c>env</c>, or <c>type</c>.</p>
- <p>For a local fun, <c>Item</c> can also be any of the atoms
+ <p>For a local fun, <c><anno>Item</anno></c> can also be any of the atoms
<c>index</c>, <c>new_index</c>, <c>new_uniq</c>,
<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>
@@ -1233,33 +1067,26 @@ true
</desc>
</func>
<func>
- <name>erlang:fun_to_list(Fun) -> string()</name>
+ <name name="fun_to_list" arity="1"/>
<fsummary>Text representation of a fun</fsummary>
- <type>
- <v>Fun = fun()</v>
- </type>
<desc>
<p>Returns a string which corresponds to the text
- representation of <c>Fun</c>.</p>
+ representation of <c><anno>Fun</anno></c>.</p>
</desc>
</func>
<func>
- <name>erlang:function_exported(Module, Function, Arity) -> boolean()</name>
+ <name name="function_exported" arity="3"/>
<fsummary>Check if a function is exported and loaded</fsummary>
- <type>
- <v>Module = Function = atom()</v>
- <v>Arity = arity()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if the module <c>Module</c> is loaded
- and contains an exported function <c>Function/Arity</c>;
+ <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>;
otherwise <c>false</c>.</p>
<p>Returns <c>false</c> for any BIF (functions implemented in C
rather than in Erlang).</p>
</desc>
</func>
<func>
- <name>garbage_collect() -> true</name>
+ <name name="garbage_collect" arity="0"/>
<fsummary>Force an immediate garbage collection of the calling process</fsummary>
<desc>
<p>Forces an immediate garbage collection of the currently
@@ -1276,26 +1103,20 @@ true
</desc>
</func>
<func>
- <name>garbage_collect(Pid) -> boolean()</name>
+ <name name="garbage_collect" arity="1"/>
<fsummary>Force an immediate garbage collection of a process</fsummary>
- <type>
- <v>Pid = pid()</v>
- </type>
<desc>
<p>Works like <c>erlang:garbage_collect()</c> but on any
process. The same caveats apply. Returns <c>false</c> if
- <c>Pid</c> refers to a dead process; <c>true</c> otherwise.</p>
+ <c><anno>Pid</anno></c> refers to a dead process; <c>true</c> otherwise.</p>
</desc>
</func>
<func>
- <name>get() -> [{Key, Val}]</name>
+ <name name="get" arity="0"/>
<fsummary>Return the process dictionary</fsummary>
- <type>
- <v>Key = Val = term()</v>
- </type>
<desc>
<p>Returns the process dictionary as a list of
- <c>{Key, Val}</c> tuples.</p>
+ <c>{<anno>Key</anno>, <anno>Val</anno>}</c> tuples.</p>
<pre>
> <input>put(key1, merry),</input>
<input>put(key2, lambs),</input>
@@ -1305,14 +1126,11 @@ true
</desc>
</func>
<func>
- <name>get(Key) -> Val | undefined</name>
+ <name name="get" arity="1"/>
<fsummary>Return a value from the process dictionary</fsummary>
- <type>
- <v>Key = Val = term()</v>
- </type>
<desc>
- <p>Returns the value <c>Val</c>associated with <c>Key</c> in
- the process dictionary, or <c>undefined</c> if <c>Key</c>
+ <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>
<pre>
> <input>put(key1, merry),</input>
@@ -1331,14 +1149,11 @@ true
</desc>
</func>
<func>
- <name>get_keys(Val) -> [Key]</name>
+ <name name="get_keys" arity="1"/>
<fsummary>Return a list of keys from the process dictionary</fsummary>
- <type>
- <v>Val = Key = term()</v>
- </type>
<desc>
<p>Returns a list of keys which are associated with the value
- <c>Val</c> in the process dictionary.</p>
+ <c><anno>Val</anno></c> in the process dictionary.</p>
<pre>
> <input>put(mary, {1, 2}),</input>
<input>put(had, {1, 2}),</input>
@@ -1351,28 +1166,23 @@ true
</desc>
</func>
<func>
- <name>erlang:get_stacktrace() -> [{Module, Function, Arity | Args, Location}]</name>
+ <name name="get_stacktrace" arity="0"/>
<fsummary>Get the call stack back-trace of the last exception</fsummary>
- <type>
- <v>Module = Function = atom()</v>
- <v>Arity = arity()</v>
- <v>Args = [term()]</v>
- <v>Location = [{atom(),term()}]</v>
- </type>
+ <type name="stack_item"/>
<desc>
<p>Get the call stack back-trace (<em>stacktrace</em>) of the last
exception in the calling process as a list of
- <c>{Module,Function,Arity,Location}</c> tuples.
- The <c>Arity</c> field in the first tuple may be the argument
+ <c>{<anno>Module</anno>,<anno>Function</anno>,<anno>Arity</anno>,<anno>Location</anno>}</c> tuples.
+ The <c><anno>Arity</anno></c> field in the first tuple may 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 []. After a code change for the process,
+ stacktrace is <c>[]</c>. After a code change for the process,
the stacktrace may also be reset to [].</p>
<p>The stacktrace is the same data as the <c>catch</c> operator
returns, for example:</p>
<p><c>{'EXIT',{badarg,Stacktrace}} = catch abs(x)</c></p>
- <p><c>Location</c> is a (possibly empty) list of two-tuples that
+ <p><c><anno>Location</anno></c> is a (possibly empty) list of two-tuples that
may indicate the location in the source code of the function.
The first element is an atom that describes the type of
information in the second element. Currently the following
@@ -1397,11 +1207,8 @@ true
</desc>
</func>
<func>
- <name>group_leader() -> GroupLeader</name>
+ <name name="group_leader" arity="0"/>
<fsummary>Get the group leader for the calling process</fsummary>
- <type>
- <v>GroupLeader = pid()</v>
- </type>
<desc>
<p>Returns the pid of the group leader for the process which
evaluates the function.</p>
@@ -1414,13 +1221,10 @@ true
</desc>
</func>
<func>
- <name>group_leader(GroupLeader, Pid) -> true</name>
+ <name name="group_leader" arity="2"/>
<fsummary>Set the group leader for a process</fsummary>
- <type>
- <v>GroupLeader = Pid = pid()</v>
- </type>
<desc>
- <p>Sets the group leader of <c>Pid</c> to <c>GroupLeader</c>.
+ <p>Sets the group leader of <c><anno>Pid</anno></c> to <c><anno>GroupLeader</anno></c>.
Typically, this is used when a processes started from a
certain shell should have another group leader than
<c>init</c>.</p>
@@ -1429,7 +1233,7 @@ true
</desc>
</func>
<func>
- <name>halt()</name>
+ <name name="halt" arity="0"/>
<fsummary>Halt the Erlang runtime system and indicate normal exit to the calling environment</fsummary>
<desc>
<p>The same as
@@ -1440,14 +1244,11 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>halt(Status)</name>
+ <name name="halt" arity="1"/>
<fsummary>Halt the Erlang runtime system</fsummary>
- <type>
- <v>Status = integer() >= 0 | string() | abort</v>
- </type>
<desc>
<p>The same as
- <seealso marker="#halt/2"><c>halt(Status, [])</c></seealso>.</p>
+ <seealso marker="#halt/2"><c>halt(<anno>Status</anno>, [])</c></seealso>.</p>
<pre>
> <input>halt(17).</input>
os_prompt% <input>echo $?</input>
@@ -1456,26 +1257,21 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>halt(Status, Options)</name>
+ <name name="halt" arity="2"/>
<fsummary>Halt the Erlang runtime system</fsummary>
- <type>
- <v>Status = integer() >= 0 | string() | abort</v>
- <v>Options = [Option]</v>
- <v>Option = {flush,boolean()} | term()</v>
- </type>
<desc>
- <p><c>Status</c> must be a non-negative integer, a string,
+ <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>Status</c>:
+ Depending on <c><anno>Status</anno></c>:
</p>
<taglist>
<tag>integer()</tag>
- <item>The runtime system exits with the integer value <c>Status</c>
+ <item>The runtime system exits with the integer value <c><anno>Status</anno></c>
as status code to the calling environment (operating system).
</item>
<tag>string()</tag>
- <item>An erlang crash dump is produced with <c>Status</c> as slogan,
+ <item>An erlang crash dump is produced with <c><anno>Status</anno></c> as slogan,
and then the runtime system exits with status code <c>1</c>.
</item>
<tag><c>abort</c></tag>
@@ -1487,10 +1283,10 @@ os_prompt% </pre>
<p>Note that on many platforms, only the status codes 0-255 are
supported by the operating system.
</p>
- <p>For integer <c>Status</c> the Erlang runtime system closes all ports
+ <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>Option</c> as <c>{flush,false}</c>.
+ <c><anno>Option</anno></c> as <c>{flush,false}</c>.
</p>
<p>For statuses <c>string()</c> and <c>abort</c> the <c>flush</c>
option is ignored and flushing is <em>not</em> done.
@@ -1498,11 +1294,11 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:hash(Term, Range) -> Hash</name>
+ <name name="hash" arity="2"/>
<fsummary>Hash function (deprecated)</fsummary>
<desc>
- <p>Returns a hash value for <c>Term</c> within the range
- <c>1..Range</c>. The allowed range is 1..2^27-1.</p>
+ <p>Returns a hash value for <c><anno>Term</anno></c> within the range
+ <c>1..<anno>Range</anno></c>. The allowed range is 1..2^27-1.</p>
<warning>
<p>This BIF is deprecated as the hash value may differ on
different architectures. Also the hash values for integer
@@ -1515,35 +1311,28 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>hd(List) -> term()</name>
+ <name name="hd" arity="1"/>
<fsummary>Head of a list</fsummary>
- <type>
- <v>List = [term()]</v>
- </type>
<desc>
- <p>Returns the head of <c>List</c>, that is, the first element.</p>
+ <p>Returns the head of <c><anno>List</anno></c>, that is, the first element.</p>
<pre>
> <input>hd([1,2,3,4,5]).</input>
1</pre>
<p>Allowed in guard tests.</p>
- <p>Failure: <c>badarg</c> if <c>List</c> is the empty list [].</p>
+ <p>Failure: <c>badarg</c> if <c><anno>List</anno></c> is the empty list [].</p>
</desc>
</func>
<func>
- <name>erlang:hibernate(Module, Function, Args)</name>
+ <name name="hibernate" arity="3"/>
<fsummary>Hibernate a process until a message is sent to it</fsummary>
- <type>
- <v>Module = Function = atom()</v>
- <v>Args = [term()]</v>
- </type>
<desc>
<p>Puts the calling process into a wait state where its memory
allocation has been reduced as much as possible, which is
useful if the process does not expect to receive any messages
in the near future.</p>
<p>The process will be awaken when a message is sent to it, and
- control will resume in <c>Module:Function</c> with
- the arguments given by <c>Args</c> with the call stack
+ control will resume in <c><anno>Module</anno>:<anno>Function</anno></c> with
+ the arguments given by <c><anno>Args</anno></c> with the call stack
emptied, meaning that the process will terminate when that
function returns. Thus <c>erlang:hibernate/3</c> will never
return to its caller.</p>
@@ -1573,14 +1362,11 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>integer_to_list(Integer) -> string()</name>
+ <name name="integer_to_list" arity="1"/>
<fsummary>Text representation of an integer</fsummary>
- <type>
- <v>Integer = integer()</v>
- </type>
<desc>
<p>Returns a string which corresponds to the text
- representation of <c>Integer</c>.</p>
+ representation of <c><anno>Integer</anno></c>.</p>
<pre>
> <input>integer_to_list(77).</input>
"77"</pre>
@@ -1598,14 +1384,11 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>iolist_to_binary(IoListOrBinary) -> binary()</name>
+ <name name="iolist_to_binary" arity="1"/>
<fsummary>Convert an iolist to a binary</fsummary>
- <type>
- <v>IoListOrBinary = iolist() | binary()</v>
- </type>
<desc>
<p>Returns a binary which is made from the integers and
- binaries in <c>IoListOrBinary</c>.</p>
+ binaries in <c><anno>IoListOrBinary</anno></c>.</p>
<pre>
> <input>Bin1 = &lt;&lt;1,2,3&gt;&gt;.</input>
&lt;&lt;1,2,3&gt;&gt;
@@ -1618,22 +1401,19 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>iolist_size(Item) -> integer() >= 0</name>
+ <name name="iolist_size" arity="1"/>
<fsummary>Size of an iolist</fsummary>
- <type>
- <v>Item = iolist() | binary()</v>
- </type>
<desc>
<p>Returns an integer which is the size in bytes
of the binary that would be the result of
- <c>iolist_to_binary(Item)</c>.</p>
+ <c>iolist_to_binary(<anno>Item</anno>)</c>.</p>
<pre>
> <input>iolist_size([1,2|&lt;&lt;3,4>>]).</input>
4</pre>
</desc>
</func>
<func>
- <name>is_alive() -> boolean()</name>
+ <name name="is_alive" arity="0"/>
<fsummary>Check whether the local node is alive</fsummary>
<desc>
<p>Returns <c>true</c> if the local node is alive; that is, if
@@ -1642,25 +1422,19 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>is_atom(Term) -> boolean()</name>
+ <name name="is_atom" arity="1"/>
<fsummary>Check whether a term is an atom</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is an atom;
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is an atom;
otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_binary(Term) -> boolean()</name>
+ <name name="is_binary" arity="1"/>
<fsummary>Check whether a term is a binary</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a binary;
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a binary;
otherwise returns <c>false</c>.</p>
<p>A binary always contains a complete number of bytes.</p>
@@ -1669,78 +1443,58 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>is_bitstring(Term) -> boolean()</name>
+ <name name="is_bitstring" arity="1"/>
<fsummary>Check whether a term is a bitstring</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a bitstring (including a binary);
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a bitstring (including a binary);
otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_boolean(Term) -> boolean()</name>
+ <name name="is_boolean" arity="1"/>
<fsummary>Check whether a term is a boolean</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is
either the atom <c>true</c> or the atom <c>false</c>
(i.e. a boolean); otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>erlang:is_builtin(Module, Function, Arity) -> boolean()</name>
+ <name name="is_builtin" arity="3"/>
<fsummary>Check if a function is a BIF implemented in C</fsummary>
- <type>
- <v>Module = Function = atom()</v>
- <v>Arity = arity()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Module:Function/Arity</c> is
+ <p>Returns <c>true</c> if <c><anno>Module</anno>:<anno>Function</anno>/<anno>Arity</anno></c> is
a BIF implemented in C; otherwise returns <c>false</c>.
This BIF is useful for builders of cross reference tools.</p>
</desc>
</func>
<func>
- <name>is_float(Term) -> boolean()</name>
+ <name name="is_float" arity="1"/>
<fsummary>Check whether a term is a float</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a floating point
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a floating point
number; otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_function(Term) -> boolean()</name>
+ <name name="is_function" arity="1"/>
<fsummary>Check whether a term is a fun</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a fun; otherwise
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a fun; otherwise
returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_function(Term, Arity) -> boolean()</name>
+ <name name="is_function" arity="2"/>
<fsummary>Check whether a term is a fun with a given arity</fsummary>
- <type>
- <v>Term = term()</v>
- <v>Arity = arity()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a fun that can be
- applied with <c>Arity</c> number of arguments; otherwise
+ <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
returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
<warning>
@@ -1753,74 +1507,56 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>is_integer(Term) -> boolean()</name>
+ <name name="is_integer" arity="1"/>
<fsummary>Check whether a term is an integer</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is an integer;
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is an integer;
otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_list(Term) -> boolean()</name>
+ <name name="is_list" arity="1"/>
<fsummary>Check whether a term is a list</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a list with
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a list with
zero or more elements; otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_number(Term) -> boolean()</name>
+ <name name="is_number" arity="1"/>
<fsummary>Check whether a term is a number</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is either an integer or a
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is either an integer or a
floating point number; otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_pid(Term) -> boolean()</name>
+ <name name="is_pid" arity="1"/>
<fsummary>Check whether a term is a pid</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a pid (process
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a pid (process
identifier); otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_port(Term) -> boolean()</name>
+ <name name="is_port" arity="1"/>
<fsummary>Check whether a term is a port</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a port identifier;
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a port identifier;
otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_process_alive(Pid) -> boolean()</name>
+ <name name="is_process_alive" arity="1"/>
<fsummary>Check whether a process is alive</fsummary>
- <type>
- <v>Pid = pid()</v>
- </type>
<desc>
<p>
- <c>Pid</c> must refer to a process at the local node.
+ <c><anno>Pid</anno></c> must refer to a process at the local node.
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>.
@@ -1828,41 +1564,32 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>is_record(Term, RecordTag) -> boolean()</name>
+ <name name="is_record" arity="2"/>
<fsummary>Check whether a term appears to be a record</fsummary>
- <type>
- <v>Term = term()</v>
- <v>RecordTag = atom()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a tuple and its first
- element is <c>RecordTag</c>. Otherwise, returns <c>false</c>.</p>
+ <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>Term</c> is a
- tuple, that its first element is <c>RecordTag</c>, and that
- the size is correct. However, if the <c>RecordTag</c> is
+ specially. 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 the <c><anno>RecordTag</anno></c> is
not a literal atom, the <c>is_record/2</c> BIF will be
called instead and the size of the tuple will not be
verified.</p>
</note>
- <p>Allowed in guard tests, if <c>RecordTag</c> is a literal
+ <p>Allowed in guard tests, if <c><anno>RecordTag</anno></c> is a literal
atom.</p>
</desc>
</func>
<func>
- <name>is_record(Term, RecordTag, Size) -> boolean()</name>
+ <name name="is_record" arity="3"/>
<fsummary>Check whether a term appears to be a record</fsummary>
- <type>
- <v>Term = term()</v>
- <v>RecordTag = atom()</v>
- <v>Size = integer()</v>
- </type>
- <desc>
- <p><c>RecordTag</c> must be an atom. Returns <c>true</c> if
- <c>Term</c> is a tuple, its first element is <c>RecordTag</c>,
- and its size is <c>Size</c>. Otherwise, returns <c>false</c>.</p>
- <p>Allowed in guard tests, provided that <c>RecordTag</c> is
+ <desc>
+ <p><c><anno>RecordTag</anno></c> must be an atom. Returns <c>true</c> if
+ <c><anno>Term</anno></c> is a tuple, its first element is <c><anno>RecordTag</anno></c>,
+ and its size is <c><anno>Size</anno></c>. Otherwise, returns <c>false</c>.</p>
+ <p>Allowed in guard tests, provided that <c><anno>RecordTag</anno></c> is
a literal atom and <c>Size</c> is a literal integer.</p>
<note>
<p>This BIF is documented for completeness. In most cases
@@ -1871,37 +1598,28 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>is_reference(Term) -> boolean()</name>
+ <name name="is_reference" arity="1"/>
<fsummary>Check whether a term is a reference</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a reference;
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a reference;
otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_tuple(Term) -> boolean()</name>
+ <name name="is_tuple" arity="1"/>
<fsummary>Check whether a term is a tuple</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a tuple;
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a tuple;
otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>length(List) -> integer() >= 0</name>
+ <name name="length" arity="1"/>
<fsummary>Length of a list</fsummary>
- <type>
- <v>List = [term()]</v>
- </type>
<desc>
- <p>Returns the length of <c>List</c>.</p>
+ <p>Returns the length of <c><anno>List</anno></c>.</p>
<pre>
> <input>length([1,2,3,4,5,6,7,8,9]).</input>
9</pre>
@@ -1909,52 +1627,47 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>link(Pid) -> true</name>
+ <name name="link" arity="1"/>
<fsummary>Create a link to another process (or port)</fsummary>
- <type>
- <v>Pid = pid() | port()</v>
- </type>
<desc>
<p>Creates a link between the calling process and another
- process (or port) <c>Pid</c>, if there is not such a link
+ process (or port) <c><anno>PidOrPort</anno></c>, if there is not such a link
already. If a process attempts to create a link to itself,
nothing is done. Returns <c>true</c>.</p>
- <p>If <c>Pid</c> does not exist, the behavior of the BIF depends
+ <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>
<list type="bulleted">
<item>If the calling process is not trapping exits, and
- checking <c>Pid</c> is cheap -- that is, if <c>Pid</c> is
+ 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>Pid</c> is remote, <c>link/1</c> returns
+ 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>
</list>
</desc>
</func>
<func>
- <name>list_to_atom(String) -> atom()</name>
+ <name name="list_to_atom" arity="1"/>
<fsummary>Convert from text representation to an atom</fsummary>
- <type>
- <v>String = string()</v>
- </type>
<desc>
- <p>Returns the atom whose text representation is <c>String</c>.</p>
+ <p>Returns the atom whose text representation is <c><anno>String</anno></c>.</p>
+ <p><c><anno>String</anno></c> may only contain ISO-latin-1
+ characterns (i.e. numbers below 256) as the current
+ implementation does not allow unicode characters >= 256 in
+ atoms.</p>
<pre>
> <input>list_to_atom("Erlang").</input>
'Erlang'</pre>
</desc>
</func>
<func>
- <name>list_to_binary(IoList) -> binary()</name>
+ <name name="list_to_binary" arity="1"/>
<fsummary>Convert a list to a binary</fsummary>
- <type>
- <v>IoList = iolist()</v>
- </type>
<desc>
<p>Returns a binary which is made from the integers and
- binaries in <c>IoList</c>.</p>
+ binaries in <c><anno>IoList</anno></c>.</p>
<pre>
> <input>Bin1 = &lt;&lt;1,2,3&gt;&gt;.</input>
&lt;&lt;1,2,3&gt;&gt;
@@ -1967,14 +1680,12 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>list_to_bitstring(BitstringList) -> bitstring()</name>
+ <name name="list_to_bitstring" arity="1"/>
+ <type name="bitstring_list"/>
<fsummary>Convert a list to a bitstring</fsummary>
- <type>
- <v>BitstringList = [BitstringList | bitstring() | char()]</v>
- </type>
<desc>
<p>Returns a bitstring which is made from the integers and
- bitstrings in <c>BitstringList</c>. (The last tail in <c>BitstringList</c>
+ bitstrings in <c><anno>BitstringList</anno></c>. (The last tail in <c><anno>BitstringList</anno></c>
is allowed to be a bitstring.)</p>
<pre>
> <input>Bin1 = &lt;&lt;1,2,3&gt;&gt;.</input>
@@ -1983,51 +1694,42 @@ os_prompt% </pre>
&lt;&lt;4,5&gt;&gt;
> <input>Bin3 = &lt;&lt;6,7:4,&gt;&gt;.</input>
&lt;&lt;6&gt;&gt;
-> <input>list_to_binary([Bin1,1,[2,3,Bin2],4|Bin3]).</input>
+> <input>list_to_bitstring([Bin1,1,[2,3,Bin2],4|Bin3]).</input>
&lt;&lt;1,2,3,1,2,3,4,5,4,6,7:46&gt;&gt;</pre>
</desc>
</func>
<func>
- <name>list_to_existing_atom(String) -> atom()</name>
+ <name name="list_to_existing_atom" arity="1"/>
<fsummary>Convert from text representation to an atom</fsummary>
- <type>
- <v>String = string()</v>
- </type>
<desc>
- <p>Returns the atom whose text representation is <c>String</c>,
+ <p>Returns the atom whose text representation is <c><anno>String</anno></c>,
but only if there already exists such atom.</p>
<p>Failure: <c>badarg</c> if there does not already exist an atom
- whose text representation is <c>String</c>.</p>
+ whose text representation is <c><anno>String</anno></c>.</p>
</desc>
</func>
<func>
- <name>list_to_float(String) -> float()</name>
+ <name name="list_to_float" arity="1"/>
<fsummary>Convert from text representation to a float</fsummary>
- <type>
- <v>String = string()</v>
- </type>
<desc>
- <p>Returns the float whose text representation is <c>String</c>.</p>
+ <p>Returns the float whose text representation is <c><anno>String</anno></c>.</p>
<pre>
> <input>list_to_float("2.2017764e+0").</input>
2.2017764</pre>
- <p>Failure: <c>badarg</c> if <c>String</c> contains a bad
+ <p>Failure: <c>badarg</c> if <c><anno>String</anno></c> contains a bad
representation of a float.</p>
</desc>
</func>
<func>
- <name>list_to_integer(String) -> integer()</name>
+ <name name="list_to_integer" arity="1"/>
<fsummary>Convert from text representation to an integer</fsummary>
- <type>
- <v>String = string()</v>
- </type>
<desc>
<p>Returns an integer whose text representation is
- <c>String</c>.</p>
+ <c><anno>String</anno></c>.</p>
<pre>
> <input>list_to_integer("123").</input>
123</pre>
- <p>Failure: <c>badarg</c> if <c>String</c> contains a bad
+ <p>Failure: <c>badarg</c> if <c><anno>String</anno></c> contains a bad
representation of an integer.</p>
</desc>
</func>
@@ -2045,13 +1747,10 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>list_to_pid(String) -> pid()</name>
+ <name name="list_to_pid" arity="1"/>
<fsummary>Convert from text representation to a pid</fsummary>
- <type>
- <v>String = string()</v>
- </type>
<desc>
- <p>Returns a pid whose text representation is <c>String</c>.</p>
+ <p>Returns a pid whose text representation is <c><anno>String</anno></c>.</p>
<warning>
<p>This BIF is intended for debugging and for use in
the Erlang operating system. It should not be used in
@@ -2060,18 +1759,15 @@ os_prompt% </pre>
<pre>
> <input>list_to_pid("&lt;0.4.1>").</input>
&lt;0.4.1></pre>
- <p>Failure: <c>badarg</c> if <c>String</c> contains a bad
+ <p>Failure: <c>badarg</c> if <c><anno>String</anno></c> contains a bad
representation of a pid.</p>
</desc>
</func>
<func>
- <name>list_to_tuple(List) -> tuple()</name>
+ <name name="list_to_tuple" arity="1"/>
<fsummary>Convert a list to a tuple</fsummary>
- <type>
- <v>List = [term()]</v>
- </type>
<desc>
- <p>Returns a tuple which corresponds to <c>List</c>. <c>List</c>
+ <p>Returns a tuple which corresponds to <c><anno>List</anno></c>. <c><anno>List</anno></c>
can contain any Erlang terms.</p>
<pre>
> <input>list_to_tuple([share, ['Ericsson_B', 163]]).</input>
@@ -2079,38 +1775,30 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>load_module(Module, Binary) -> {module, Module} | {error, Reason}</name>
+ <name name="load_module" arity="2"/>
<fsummary>Load object code for a module</fsummary>
- <type>
- <v>Module = atom()</v>
- <v>Binary = binary()</v>
- <v>Reason = badfile | not_purged | badfile</v>
- </type>
- <desc>
- <p>If <c>Binary</c> contains the object code for the module
- <c>Module</c>, this BIF loads that object code. Also, if
- the code for the module <c>Module</c> already exists, all
+ <desc>
+ <p>If <c><anno>Binary</anno></c> contains the object code for the module
+ <c><anno>Module</anno></c>, this BIF loads that object code. Also, if
+ the code for the module <c><anno>Module</anno></c> already exists, all
export references are replaced so they point to the newly
loaded code. The previously loaded code is kept in the system
as old code, as there may still be processes which are
executing that code. It returns either
- <c>{module, Module}</c>, or <c>{error, Reason}</c> if loading
- fails. <c>Reason</c> is one of the following:</p>
+ <c>{module, <anno>Module</anno>}</c>, or <c>{error, <anno>Reason</anno>}</c> if loading
+ fails. <c><anno>Reason</anno></c> is one of the following:</p>
<taglist>
<tag><c>badfile</c></tag>
<item>
- <p>The object code in <c>Binary</c> has an incorrect format.</p>
+ <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>
<tag><c>not_purged</c></tag>
<item>
- <p><c>Binary</c> contains a module which cannot be loaded
+ <p><c><anno>Binary</anno></c> contains a module which cannot be loaded
because old code for this module already exists.</p>
</item>
- <tag><c>badfile</c></tag>
- <item>
- <p>The object code contains code for another module than
- <c>Module</c></p>
- </item>
</taglist>
<warning>
<p>This BIF is intended for the code server (see
@@ -2120,15 +1808,8 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:load_nif(Path, LoadInfo) -> ok | {error, {Reason, Text}}</name>
+ <name name="load_nif" arity="2"/>
<fsummary>Load NIF library</fsummary>
- <type>
- <v>Path = string()</v>
- <v>LoadInfo = term()</v>
- <v>Reason = load_failed | bad_lib | load | reload |
- upgrade | old_code</v>
- <v>Text = string()</v>
- </type>
<desc>
<note>
<p>In releases older than OTP R14B, NIFs were an
@@ -2139,21 +1820,21 @@ os_prompt% </pre>
<c>{error,Reason,Text}</c>.</p>
</note>
<p>Loads and links a dynamic library containing native
- implemented functions (NIFs) for a module. <c>Path</c> is a
+ implemented functions (NIFs) for a module. <c><anno>Path</anno></c> is a
file path to the sharable object/dynamic library file minus
the OS-dependent file extension (.so for Unix and .dll for
Windows). See <seealso marker="erl_nif">erl_nif</seealso>
on how to implement a NIF library.</p>
- <p><c>LoadInfo</c> can be any term. It will be passed on to
+ <p><c><anno>LoadInfo</anno></c> can be any term. It will be 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>
<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.</p>
- <p>It returns either <c>ok</c>, or <c>{error,{Reason,Text}}</c>
- if loading fails. <c>Reason</c> is one of the atoms below,
- while <c>Text</c> is a human readable string that may give
+ <p>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 atoms below,
+ while <c><anno>Text</anno></c> is a human readable string that may give
some more information about the failure.</p>
<taglist>
<tag><c>load_failed</c></tag>
@@ -2179,11 +1860,8 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:loaded() -> [Module]</name>
+ <name name="loaded" arity="0"/>
<fsummary>List of all loaded modules</fsummary>
- <type>
- <v>Module = atom()</v>
- </type>
<desc>
<p>Returns a list of all loaded Erlang modules (current and/or
old code), including preloaded modules.</p>
@@ -2191,11 +1869,8 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:localtime() -> DateTime</name>
+ <name name="localtime" arity="0"/>
<fsummary>Current local date and time</fsummary>
- <type>
- <v>DateTime = <seealso marker="calendar#type-datetime">calendar:datetime()</seealso></v>
- </type>
<desc>
<p>Returns the current local date and time
<c>{{Year, Month, Day}, {Hour, Minute, Second}}</c>.</p>
@@ -2212,32 +1887,27 @@ os_prompt% </pre>
<desc>
<p>Converts local date and time to Universal Time Coordinated
(UTC), if this is supported by the underlying OS. Otherwise,
- no conversion is done and <c>{<anno>Date1</anno>, <anno>Time1</anno>}</c> is returned.</p>
+ no conversion is done and <c><anno>Localtime</anno></c> is returned.</p>
<pre>
> <input>erlang:localtime_to_universaltime({{1996,11,6},{14,45,17}}).</input>
{{1996,11,6},{13,45,17}}</pre>
- <p>Failure: <c>badarg</c> if <c>Date1</c> or <c>Time1</c> do
- not denote a valid date or time.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Localtime</anno></c> does not denote
+ a valid date and time.</p>
</desc>
</func>
<func>
- <name>erlang:localtime_to_universaltime({Date1, Time1}, IsDst) -> {Date2, Time2}</name>
+ <name name="localtime_to_universaltime" arity="2"/>
<fsummary>Convert from local to Universal Time Coordinated (UTC) date and time</fsummary>
- <type>
- <v>Date1 = Date2 = <seealso marker="calendar#type-date">calendar:date()</seealso></v>
- <v>Time1 = Time2 = <seealso marker="calendar#type-time">calendar:time()</seealso></v>
- <v>IsDst = true | false | undefined</v>
- </type>
<desc>
<p>Converts local date and time to Universal Time Coordinated
(UTC) just like <c>erlang:localtime_to_universaltime/1</c>,
but the caller decides if daylight saving time is active or
not.</p>
- <p>If <c>IsDst == true</c> the <c>{Date1, Time1}</c> is during
- daylight saving time, if <c>IsDst == false</c> it is not,
- and if <c>IsDst == undefined</c> the underlying OS may
+ <p>If <c><anno>IsDst</anno> == true</c> the <c><anno>Localtime</anno></c> is during
+ daylight saving time, if <c><anno>IsDst</anno> == false</c> it is not,
+ and if <c><anno>IsDst</anno> == undefined</c> the underlying OS may
guess, which is the same as calling
- <c>erlang:localtime_to_universaltime({Date1, Time1})</c>.</p>
+ <c>erlang:localtime_to_universaltime(<anno>Localtime</anno>)</c>.</p>
<pre>
> <input>erlang:localtime_to_universaltime({{1996,11,6},{14,45,17}}, true).</input>
{{1996,11,6},{12,45,17}}
@@ -2245,12 +1915,12 @@ os_prompt% </pre>
{{1996,11,6},{13,45,17}}
> <input>erlang:localtime_to_universaltime({{1996,11,6},{14,45,17}}, undefined).</input>
{{1996,11,6},{13,45,17}}</pre>
- <p>Failure: <c>badarg</c> if <c>Date1</c> or <c>Time1</c> do
- not denote a valid date or time.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Localtime</anno></c> does not denote
+ a valid date and time.</p>
</desc>
</func>
<func>
- <name>make_ref() -> reference()</name>
+ <name name="make_ref" arity="0"/>
<fsummary>Return an almost unique reference</fsummary>
<desc>
<p>Returns an almost unique reference.</p>
@@ -2262,33 +1932,23 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:make_tuple(Arity, InitialValue) -> tuple()</name>
+ <name name="make_tuple" arity="2"/>
<fsummary>Create a new tuple of a given arity</fsummary>
- <type>
- <v>Arity = arity()</v>
- <v>InitialValue = term()</v>
- </type>
<desc>
- <p>Returns a new tuple of the given <c>Arity</c>, where all
- elements are <c>InitialValue</c>.</p>
+ <p>Returns a new tuple of the given <c><anno>Arity</anno></c>, where all
+ elements are <c><anno>InitialValue</anno></c>.</p>
<pre>
> <input>erlang:make_tuple(4, []).</input>
{[],[],[],[]}</pre>
</desc>
</func>
<func>
- <name>erlang:make_tuple(Arity, Default, InitList) -> tuple()</name>
+ <name name="make_tuple" arity="3"/>
<fsummary>Create a new tuple with given arity and contents</fsummary>
- <type>
- <v>Arity = arity()</v>
- <v>Default = term()</v>
- <v>InitList = [{Position,term()}]</v>
- <v>Position = integer()</v>
- </type>
- <desc>
- <p><c>erlang:make_tuple</c> first creates a tuple of size <c>Arity</c>
- where each element has the value <c>Default</c>. It then fills
- in values from <c>InitList</c>. Each list element in <c>InitList</c>
+ <desc>
+ <p><c>erlang:make_tuple</c> first creates a tuple of size <c><anno>Arity</anno></c>
+ where each element has the value <c><anno>DefaultValue</anno></c>. It then fills
+ in 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
@@ -2307,15 +1967,11 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:md5(Data) -> Digest</name>
+ <name name="md5" arity="1"/>
<fsummary>Compute an MD5 message digest</fsummary>
- <type>
- <v>Data = iodata()</v>
- <v>Digest = binary()</v>
- </type>
<desc>
- <p>Computes an <c>MD5</c> message digest from <c>Data</c>, where
- the length of the digest is 128 bits (16 bytes). <c>Data</c>
+ <p>Computes an <c>MD5</c> 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>See The MD5 Message Digest Algorithm (RFC 1321) for more
information about MD5.</p>
@@ -2324,51 +1980,39 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:md5_final(Context) -> Digest</name>
+ <name name="md5_final" arity="1"/>
<fsummary>Finish the update of an MD5 context and return the computed MD5 message digest</fsummary>
- <type>
- <v>Context = Digest = binary()</v>
- </type>
<desc>
- <p>Finishes the update of an MD5 <c>Context</c> and returns
+ <p>Finishes the update of an MD5 <c><anno>Context</anno></c> and returns
the computed <c>MD5</c> message digest.</p>
</desc>
</func>
<func>
- <name>erlang:md5_init() -> Context</name>
+ <name name="md5_init" arity="0"/>
<fsummary>Create an MD5 context</fsummary>
- <type>
- <v>Context = binary()</v>
- </type>
<desc>
<p>Creates an MD5 context, to be used in subsequent calls to
<c>md5_update/2</c>.</p>
</desc>
</func>
<func>
- <name>erlang:md5_update(Context, Data) -> NewContext</name>
+ <name name="md5_update" arity="2"/>
<fsummary>Update an MD5 context with data, and return a new context</fsummary>
- <type>
- <v>Data = iodata()</v>
- <v>Context = NewContext = binary()</v>
- </type>
<desc>
- <p>Updates an MD5 <c>Context</c> with <c>Data</c>, and returns
- a <c>NewContext</c>.</p>
+ <p>Updates an MD5 <c><anno>Context</anno></c> with <c><anno>Data</anno></c>, and returns
+ a <c><anno>NewContext</anno></c>.</p>
</desc>
</func>
<func>
- <name>erlang:memory() -> [{Type, Size}]</name>
+ <name name="memory" arity="0"/>
+ <type name="memory_type"/>
<fsummary>Information about dynamically allocated memory</fsummary>
- <type>
- <v>Type, Size -- see below</v>
- </type>
<desc>
<p>Returns a list containing information about memory
dynamically allocated by the Erlang emulator. Each element of
the list is a tuple <c>{Type, Size}</c>. The first element
- <c>Type</c>is an atom describing memory type. The second
- element <c>Size</c>is memory size in bytes. A description of
+ <c><anno>Type</anno></c>is an atom describing memory type. The second
+ element <c><anno>Size</anno></c>is memory size in bytes. A description of
each memory type follows:</p>
<taglist>
<tag><c>total</c></tag>
@@ -2430,6 +2074,14 @@ os_prompt% </pre>
<p>This memory is part of the memory presented as
<c>system</c> memory.</p>
</item>
+ <tag><c>low</c></tag>
+ <item>
+ <p>Only on 64-bit halfword emulator.</p>
+ <p>The total amount of memory allocated in low memory areas
+ that are restricted to less than 4 Gb even though
+ the system may have more physical memory.</p>
+ <p>May be removed in future releases of halfword emulator.</p>
+ </item>
<tag><c>maximum</c></tag>
<item>
<p>The maximum total amount of memory allocated since
@@ -2441,14 +2093,6 @@ os_prompt% </pre>
<seealso marker="tools:instrument">instrument(3)</seealso>
and/or <seealso marker="erts:erl">erl(1)</seealso>.</p>
</item>
- <tag><c>low</c></tag>
- <item>
- <p>Only on 64-bit halfword emulator.</p>
- <p>The total amount of memory allocated in low memory areas
- that are restricted to less than 4 Gb even though
- the system may have more physical memory.</p>
- <p>May be removed in future releases of halfword emulator.</p>
- </item>
</taglist>
<note>
<p>The <c>system</c> value is not complete. Some allocated
@@ -2512,16 +2156,15 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:memory(Type | [Type]) -> Size | [{Type, Size}]</name>
+ <name name="memory" arity="1" clause_i="1"/>
+ <name name="memory" arity="1" clause_i="2"/>
+ <type name="memory_type"/>
<fsummary>Information about dynamically allocated memory</fsummary>
- <type>
- <v>Type, Size -- see below</v>
- </type>
<desc>
<p>Returns the memory size in bytes allocated for memory of
- type <c>Type</c>. The argument can also be given as a list
- of <c>Type</c> atoms, in which case a corresponding list of
- <c>{Type, Size}</c> tuples is returned.</p>
+ type <c><anno>Type</anno></c>. The argument can also be given 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>
Since erts version 5.6.4 <c>erlang:memory/1</c> requires that
@@ -2533,13 +2176,13 @@ os_prompt% </pre>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c>Type</c> is not one of the memory types listed in the
+ If <c><anno>Type</anno></c> is not one of the memory types listed in the
documentation of
<seealso marker="#memory/0">erlang:memory/0</seealso>.
</item>
<tag><c>badarg</c></tag>
<item>
- If <c>maximum</c> is passed as <c>Type</c> and the emulator
+ If <c>maximum</c> is passed as <c><anno>Type</anno></c> and the emulator
is not run in instrumented mode.
</item>
<tag><c>notsup</c></tag>
@@ -2561,13 +2204,10 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>module_loaded(Module) -> boolean()</name>
+ <name name="module_loaded" arity="1"/>
<fsummary>Check if a module is loaded</fsummary>
- <type>
- <v>Module = atom()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if the module <c>Module</c> is loaded,
+ <p>Returns <c>true</c> if the module <c><anno>Module</anno></c> is loaded,
otherwise returns <c>false</c>. It does not attempt to load
the module.</p>
<warning>
@@ -2578,22 +2218,15 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>monitor(Type, Item) -> MonitorRef</name>
+ <name name="monitor" arity="2"/>
<fsummary>Start monitoring</fsummary>
- <type>
- <v>Type = process</v>
- <v>Item = pid() | {RegName, Node} | RegName</v>
- <v>&nbsp;RegName = atom()</v>
- <v>&nbsp;Node = node()</v>
- <v>MonitorRef = reference()</v>
- </type>
- <desc>
- <p>The calling process starts monitoring <c>Item</c> which is
- an object of type <c>Type</c>.</p>
+ <desc>
+ <p>The calling process starts monitoring <c><anno>Item</anno></c> which is
+ an object of type <c><anno>Type</anno></c>.</p>
<p>Currently only processes can be monitored, i.e. the only
- allowed <c>Type</c> is <c>process</c>, but other types may be
+ allowed <c><anno>Type</anno></c> is <c>process</c>, but other types may be
allowed in the future.</p>
- <p><c>Item</c> can be:</p>
+ <p><c><anno>Item</anno></c> can be:</p>
<taglist>
<tag><c>pid()</c></tag>
<item>
@@ -2619,8 +2252,8 @@ os_prompt% </pre>
unregistered.</p>
</note>
<p>A <c>'DOWN'</c> message will be sent to the monitoring
- process if <c>Item</c> dies, if <c>Item</c> does not exist,
- or if the connection is lost to the node which <c>Item</c>
+ process if <c><anno>Item</anno></c> dies, if <c><anno>Item</anno></c> does not exist,
+ or if the connection is lost to the node which <c><anno>Item</anno></c>
resides on. A <c>'DOWN'</c> message has the following pattern:</p>
<code type="none">
{'DOWN', MonitorRef, Type, Object, Info}</code>
@@ -2631,11 +2264,11 @@ os_prompt% </pre>
<item>
<p>A reference to the monitored object:</p>
<list type="bulleted">
- <item>the pid of the monitored process, if <c>Item</c> was
+ <item>the pid of the monitored process, if <c><anno>Item</anno></c> was
specified as a pid.</item>
- <item><c>{RegName, Node}</c>, if <c>Item</c> was specified as
+ <item><c>{RegName, Node}</c>, if <c><anno>Item</anno></c> was specified as
<c>{RegName, Node}</c>.</item>
- <item><c>{RegName, Node}</c>, if <c>Item</c> was specified as
+ <item><c>{RegName, Node}</c>, if <c><anno>Item</anno></c> was specified as
<c>RegName</c>. <c>Node</c> will in this case be the
name of the local node (<c>node()</c>).</item>
</list>
@@ -2644,7 +2277,7 @@ os_prompt% </pre>
<item>
<p>Either the exit reason of the process, <c>noproc</c>
(non-existing process), or <c>noconnection</c> (no
- connection to <c>Node</c>).</p>
+ connection to <c><anno>Node</anno></c>).</p>
</item>
</taglist>
<note>
@@ -2662,7 +2295,7 @@ os_prompt% </pre>
where remote process monitoring by registered name is not
implemented), the call fails with <c>badarg</c>.</p>
<p>Making several calls to <c>monitor/2</c> for the same
- <c>Item</c> is not an error; it results in as many, completely
+ <c><anno>Item</anno></c> is not an error; it results in as many, completely
independent, monitorings.</p>
<note>
<p>The format of the <c>'DOWN'</c> message changed in the 5.2
@@ -2680,25 +2313,21 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>monitor_node(Node, Flag) -> true</name>
+ <name name="monitor_node" arity="2"/>
<fsummary>Monitor the status of a node</fsummary>
- <type>
- <v>Node = node()</v>
- <v>Flag = boolean()</v>
- </type>
<desc>
- <p>Monitors the status of the node <c>Node</c>. If <c>Flag</c>
- is <c>true</c>, monitoring is turned on; if <c>Flag</c> is
+ <p>Monitors 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>
<p>Making several calls to <c>monitor_node(Node, true)</c> for
- the same <c>Node</c> is not an error; it results in as many,
+ the same <c><anno>Node</anno></c> is not an error; it results in as many,
completely independent, monitorings.</p>
- <p>If <c>Node</c> fails or does not exist, the message
+ <p>If <c><anno>Node</anno></c> fails or does not exist, the message
<c>{nodedown, Node}</c> is delivered to the process. If a
process has made two calls to <c>monitor_node(Node, true)</c>
- and <c>Node</c> terminates, two <c>nodedown</c> messages are
+ and <c><anno>Node</anno></c> terminates, two <c>nodedown</c> messages are
delivered to the process. If there is no connection to
- <c>Node</c>, there will be an attempt to create one. If this
+ <c><anno>Node</anno></c>, there will be an attempt to create one. If this
fails, a <c>nodedown</c> message is delivered.</p>
<p>Nodes connected through hidden connections can be monitored
as any other node.</p>
@@ -2706,14 +2335,8 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:monitor_node(Node, Flag, Options) -> true</name>
+ <name name="monitor_node" arity="3"/>
<fsummary>Monitor the status of a node</fsummary>
- <type>
- <v>Node = node()</v>
- <v>Flag = boolean()</v>
- <v>Options = [Option]</v>
- <v>Option = allow_passive_connect</v>
- </type>
<desc>
<p>Behaves as <c>monitor_node/2</c> except that it allows an
extra option to be given, namely <c>allow_passive_connect</c>.
@@ -2736,11 +2359,8 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:nif_error(Reason)</name>
+ <name name="nif_error" arity="1"/>
<fsummary>Stop execution with a given reason</fsummary>
- <type>
- <v>Reason = term()</v>
- </type>
<desc>
<p>Works exactly like
<seealso marker="#error/1">erlang:error/1</seealso>,
@@ -2751,12 +2371,8 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:nif_error(Reason, Args)</name>
+ <name name="nif_error" arity="2"/>
<fsummary>Stop execution with a given reason</fsummary>
- <type>
- <v>Reason = term()</v>
- <v>Args = [term()]</v>
- </type>
<desc>
<p>Works exactly like
<seealso marker="#error/2">erlang:error/2</seealso>,
@@ -2767,11 +2383,8 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>node() -> Node</name>
+ <name name="node" arity="0"/>
<fsummary>Name of the local node</fsummary>
- <type>
- <v>Node = node()</v>
- </type>
<desc>
<p>Returns the name of the local node. If the node is not alive,
<c>nonode@nohost</c> is returned instead.</p>
@@ -2779,14 +2392,10 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>node(Arg) -> Node</name>
+ <name name="node" arity="1"/>
<fsummary>At which node is a pid, port or reference located</fsummary>
- <type>
- <v>Arg = pid() | port() | reference()</v>
- <v>Node = node()</v>
- </type>
<desc>
- <p>Returns the node where <c>Arg</c> is located. <c>Arg</c> can
+ <p>Returns the node where <c><anno>Arg</anno></c> is located. <c><anno>Arg</anno></c> can
be a pid, a reference, or a port. If the local node is not
alive, <c>nonode@nohost</c> is returned.</p>
<p>Allowed in guard tests.</p>
@@ -2801,17 +2410,13 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>nodes(Arg | [Arg]) -> Nodes</name>
+ <name name="nodes" arity="1"/>
<fsummary>All nodes of a certain type in the system</fsummary>
- <type>
- <v>Arg = visible | hidden | connected | this | known</v>
- <v>Nodes = [node()]</v>
- </type>
<desc>
<p>Returns a list of nodes according to argument given.
The result returned when the argument is a list, is the list
of nodes satisfying the disjunction(s) of the list elements.</p>
- <p><c>Arg</c> can be any of the following:</p>
+ <p><c><anno>NodeType</anno></c> can be any of the following:</p>
<taglist>
<tag><c>visible</c></tag>
<item>
@@ -2840,15 +2445,12 @@ os_prompt% </pre>
<c>nodes() = nodes(visible)</c>.</p>
<p>If the local node is not alive,
<c>nodes(this) == nodes(known) == [nonode@nohost]</c>, for
- any other <c>Arg</c> the empty list [] is returned.</p>
+ any other <c><anno>Arg</anno></c> the empty list [] is returned.</p>
</desc>
</func>
<func>
- <name>now() -> timestamp()</name>
- <type>
- <v>timestamp() = {MegaSecs, Secs, MicroSecs}</v>
- <v>MegaSecs = Secs = MicroSecs = integer() >= 0</v>
- </type>
+ <name name="now" arity="0"/>
+ <type name="timestamp"/>
<fsummary>Elapsed time since 00:00 GMT</fsummary>
<desc>
<p>Returns the tuple <c>{MegaSecs, Secs, MicroSecs}</c> which is
@@ -2863,38 +2465,26 @@ os_prompt% </pre>
<p>It can only be used to check the local time of day if
the time-zone info of the underlying operating system is
properly configured.</p>
+ <p>If you do not need the return value to be unique and
+ monotonically increasing, use
+ <seealso marker="kernel:os#timestamp/0">os:timestamp/0</seealso>
+ instead to avoid some overhead.</p>
</desc>
</func>
<func>
- <name>open_port(PortName, PortSettings) -> port()</name>
+ <name name="open_port" arity="2"/>
<fsummary>Open a port</fsummary>
- <type>
- <v>PortName = {spawn, Command} | {spawn_driver, Command} | {spawn_executable, FileName} | {fd, In, Out}</v>
- <v>&nbsp;Command = string()</v>
- <v>&nbsp;FileName = [ FileNameChar ] | binary()</v>
- <v>&nbsp;FileNameChar = integer() (1..255 or any Unicode codepoint, see description)</v>
- <v>&nbsp;In = Out = integer()</v>
- <v>PortSettings = [Opt]</v>
- <v>&nbsp;Opt = {packet, N} | stream | {line, L} | {cd, Dir} | {env, Env} | {args, [ ArgString ]} | {arg0, ArgString} | exit_status | use_stdio | nouse_stdio | stderr_to_stdout | in | out | binary | eof</v>
- <v>&nbsp;&nbsp;N = 1 | 2 | 4</v>
- <v>&nbsp;&nbsp;L = integer()</v>
- <v>&nbsp;&nbsp;Dir = string()</v>
- <v>&nbsp;&nbsp;ArgString = [ FileNameChar ] | binary()</v>
- <v>&nbsp;&nbsp;Env = [{Name, Val}]</v>
- <v>&nbsp;&nbsp;&nbsp;Name = string()</v>
- <v>&nbsp;&nbsp;&nbsp;Val = string() | false</v>
- </type>
<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. <c>PortName</c> is one of the following:</p>
+ process. <c><anno>PortName</anno></c> is one of the following:</p>
<taglist>
- <tag><c>{spawn, Command}</c></tag>
+ <tag><c>{spawn, <anno>Command</anno>}</c></tag>
<item>
- <p>Starts an external program. <c>Command</c> is the name
- of the external program which will be run. <c>Command</c>
+ <p>Starts an external program. <c><anno>Command</anno></c> is the name
+ of the external program which will be run. <c><anno>Command</anno></c>
runs outside the Erlang work space unless an Erlang
- driver with the name <c>Command</c> is found. If found,
+ driver with the name <c><anno>Command</anno></c> is found. If found,
that driver will be started. A driver runs in the Erlang
workspace, which means that it is linked with the Erlang
runtime system.</p>
@@ -2914,24 +2504,24 @@ os_prompt% </pre>
name of the executable (or driver). This (among other
things) makes this option unsuitable for running
programs having spaces in file or directory names. Use
- {spawn_executable, Command} instead if spaces in executable
+ {spawn_executable, <anno>Command</anno>} instead if spaces in executable
file names is desired.</p>
</item>
- <tag><c>{spawn_driver, Command}</c></tag>
+ <tag><c>{spawn_driver, <anno>Command</anno>}</c></tag>
<item>
- <p>Works like <c>{spawn, Command}</c>, but demands the
+ <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, Command}</c></tag>
+ <tag><c>{spawn_executable, <anno>FileName</anno>}</c></tag>
<item>
- <p>Works like <c>{spawn, Command}</c>, but only runs
- external executables. The <c>Command</c> in its whole
+ <p>Works like <c>{spawn, <anno>FileName</anno>}</c>, but only runs
+ external executables. The <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>args</c> and <c>arg0</c> <c>PortSettings</c> can be used.</p>
+ <c>args</c> and <c>arg0</c> <c><anno>PortSettings</anno></c> can be used.</p>
<p>The shell is not usually invoked to start the
program, it's executed directly. Neither is the
@@ -2962,7 +2552,7 @@ os_prompt% </pre>
of the executable is limited to the ISO-latin-1
character set.</p></note>
- <p>If the <c>Command</c> cannot be run, an error
+ <p>If the <c><anno>FileName</anno></c> cannot be run, an error
exception, with the posix error code as the reason, is
raised. The error reason may differ between operating
systems. Typically the error <c>enoent</c> is raised
@@ -2970,23 +2560,23 @@ os_prompt% </pre>
<c>eaccess</c> is raised when the given file is not
executable.</p>
</item>
- <tag><c>{fd, In, Out}</c></tag>
+ <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>In</c> can be used for standard input, and the file
- descriptor <c>Out</c> for standard output. It is only
+ <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 operating system
(<c>shell</c> and <c>user</c>). Hence, its use is very
limited.</p>
</item>
</taglist>
- <p><c>PortSettings</c> is a list of settings for the port.
+ <p><c><anno>PortSettings</anno></c> is a list of settings for the port.
Valid settings are:</p>
<taglist>
- <tag><c>{packet, N}</c></tag>
+ <tag><c>{packet, <anno>N</anno>}</c></tag>
<item>
- <p>Messages are preceded by their length, sent in <c>N</c>
+ <p>Messages are preceded by their length, sent in <c><anno>N</anno></c>
bytes, with the most significant byte first. Valid values
for <c>N</c> are 1, 2, or 4.</p>
</item>
@@ -2996,7 +2586,7 @@ os_prompt% </pre>
user-defined protocol must be used between the Erlang
process and the external object.</p>
</item>
- <tag><c>{line, L}</c></tag>
+ <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 newline sequence) is
@@ -3004,7 +2594,7 @@ os_prompt% </pre>
is <c>{Flag, Line}</c>, where <c>Flag</c> is either
<c>eol</c> or <c>noeol</c> and <c>Line</c> is the actual
data delivered (without the newline sequence).</p>
- <p><c>L</c> specifies the maximum line length in bytes.
+ <p><c><anno>L</anno></c> specifies the maximum line length in bytes.
Lines longer than this will be delivered in more than one
message, with the <c>Flag</c> set to <c>noeol</c> for all
but the last message. If end of file is encountered
@@ -3012,36 +2602,40 @@ os_prompt% </pre>
sequence, the last line will also be delivered with
the <c>Flag</c> set to <c>noeol</c>. In all other cases,
lines are delivered with <c>Flag</c> set to <c>eol</c>.</p>
- <p>The <c>{packet, N}</c> and <c>{line, L}</c> settings are
+ <p>The <c>{packet, <anno>N</anno>}</c> and <c>{line, <anno>L</anno>}</c> settings are
mutually exclusive.</p>
</item>
- <tag><c>{cd, Dir}</c></tag>
+ <tag><c>{cd, <anno>Dir</anno>}</c></tag>
<item>
- <p>This is only valid for <c>{spawn, Command}</c> and
- <c>{spawn_executable, Command}</c>.
- The external program starts using <c>Dir</c> as its
- working directory. <c>Dir</c> must be a string. Not
- available on VxWorks.</p>
+ <p>This is only valid for <c>{spawn, <anno>Command</anno>}</c> and
+ <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, Env}</c></tag>
+ <tag><c>{env, <anno>Env</anno>}</c></tag>
<item>
- <p>This is only valid for <c>{spawn, Command}</c> and
- <c>{spawn_executable, Command}</c>.
+ <p>This is 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>Env</c>.</p>
- <p><c>Env</c> should be a list of tuples <c>{Name, Val}</c>,
- where <c>Name</c> is the name of an environment variable,
- and <c>Val</c> is the value it is to have in the spawned
- port process. Both <c>Name</c> and <c>Val</c> must be
- strings. The one exception is <c>Val</c> being the atom
+ the environment specifications in <c><anno>Env</anno></c>.</p>
+ <p><c><anno>Env</anno></c> should be a list of tuples <c>{<anno>Name</anno>, <anno>Val</anno>}</c>,
+ where <c><anno>Name</anno></c> is the name of an environment variable,
+ and <c><anno>Val</anno></c> is the value it is to have in the spawned
+ 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. Not available on
- VxWorks.</p>
+ removes the environment variable.
+ </p>
+ <p>If Unicode filename encoding is in effect (see the <seealso
+ marker="erts:erl#file_name_encoding">erl manual
+ page</seealso>), the strings (both <c>Name</c> and
+ <c>Value</c>) may contain characters with codepoints > 255.</p>
</item>
- <tag><c>{args, [ string() ]}</c></tag>
+ <tag><c>{args, [ string() | binary() ]}</c></tag>
<item>
- <p>This option is only valid for <c>{spawn_executable, Command}</c>
+ <p>This option is 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
@@ -3084,10 +2678,10 @@ os_prompt% </pre>
option can be used.</p>
</item>
- <tag><c>{arg0, string()}</c></tag>
+ <tag><c>{arg0, string() | binary()}</c></tag>
<item>
- <p>This option is only valid for <c>{spawn_executable, Command}</c>
+ <p>This option is only valid for <c>{spawn_executable, <anno>FileName</anno>}</c>
and explicitly specifies the program name argument when
running an executable. This might in some circumstances,
on some operating systems, be desirable. How the program
@@ -3101,9 +2695,9 @@ os_prompt% </pre>
<tag><c>exit_status</c></tag>
<item>
- <p>This is only valid for <c>{spawn, Command}</c> where
- <c>Command</c> refers to an external program, and for
- <c>{spawn_executable, Command}</c>.</p>
+ <p>This is 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>
<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
@@ -3118,8 +2712,8 @@ os_prompt% </pre>
</item>
<tag><c>use_stdio</c></tag>
<item>
- <p>This is only valid for <c>{spawn, Command}</c> and
- <c>{spawn_executable, Command}</c>. It
+ <p>This is only valid for <c>{spawn, <anno>Command</anno>}</c> and
+ <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>
@@ -3216,7 +2810,7 @@ os_prompt% </pre>
</item>
<tag><c>enoent</c></tag>
<item>
- <p>The <c>Command</c> given in <c>{spawn_executable, Command}</c> does not point out an existing file.</p>
+ <p>The <c><anno>FileName</anno></c> given in <c>{spawn_executable, <anno>FileName</anno>}</c> does not point out an existing file.</p>
</item>
</taglist>
<p>During use of a port opened using <c>{spawn, Name}</c>,
@@ -3232,56 +2826,47 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:phash(Term, Range) -> Hash</name>
+ <name name="phash" arity="2"/>
+ <type_desc variable="Range">Range = 1..2^32, Hash = 1..Range</type_desc>
<fsummary>Portable hash function</fsummary>
- <type>
- <v>Term = term()</v>
- <v>Range = 1..2^32</v>
- <v>Hash = 1..Range</v>
- </type>
<desc>
<p>Portable hash function that will give the same hash for
the same Erlang term regardless of machine architecture and
ERTS version (the BIF was introduced in ERTS 4.9.1.1). Range
can be between 1 and 2^32, the function returns a hash value
- for <c>Term</c> within the range <c>1..Range</c>.</p>
+ for <c><anno>Term</anno></c> within the range <c>1..<anno>Range</anno></c>.</p>
<p>This BIF could be used instead of the old deprecated
<c>erlang:hash/2</c> BIF, as it calculates better hashes for
all data-types, but consider using <c>phash2/1,2</c> instead.</p>
</desc>
</func>
<func>
- <name>erlang:phash2(Term [, Range]) -> Hash</name>
+ <name name="phash2" arity="1"/>
+ <name name="phash2" arity="2"/>
+ <type_desc variable="Range">1..2^32</type_desc>
+ <type_desc variable="Hash">0..Range-1</type_desc>
<fsummary>Portable hash function</fsummary>
- <type>
- <v>Term = term()</v>
- <v>Range = 1..2^32</v>
- <v>Hash = 0..Range-1</v>
- </type>
<desc>
<p>Portable hash function that will give the same hash for
the same Erlang term regardless of machine architecture and
ERTS version (the BIF was introduced in ERTS 5.2). Range can
be between 1 and 2^32, the function returns a hash value for
- <c>Term</c> within the range <c>0..Range-1</c>. When called
- without the <c>Range</c> argument, a value in the range
+ <c><anno>Term</anno></c> within the range <c>0..<anno>Range</anno>-1</c>. When called
+ without the <c><anno>Range</anno></c> argument, a value in the range
<c>0..2^27-1</c> is returned.</p>
<p>This BIF should always be used for hashing terms. It
distributes small integers better than <c>phash/2</c>, and
it is faster for bignums and binaries.</p>
- <p>Note that the range <c>0..Range-1</c> is different from
- the range of <c>phash/2</c> (<c>1..Range</c>).</p>
+ <p>Note that the range <c>0..<anno>Range</anno>-1</c> is different from
+ the range of <c>phash/2</c> (<c>1..<anno>Range</anno></c>).</p>
</desc>
</func>
<func>
- <name>pid_to_list(Pid) -> string()</name>
+ <name name="pid_to_list" arity="1"/>
<fsummary>Text representation of a pid</fsummary>
- <type>
- <v>Pid = pid()</v>
- </type>
<desc>
<p>Returns a string which corresponds to the text
- representation of <c>Pid</c>.</p>
+ representation of <c><anno>Pid</anno></c>.</p>
<warning>
<p>This BIF is intended for debugging and for use in
the Erlang operating system. It should not be used in
@@ -3290,88 +2875,74 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>port_close(Port) -> true</name>
+ <name name="port_close" arity="1"/>
<fsummary>Close an open port</fsummary>
- <type>
- <v>Port = port() | atom()</v>
- </type>
<desc>
<p>Closes an open port. Roughly the same as
- <c>Port ! {self(), close}</c> except for the error behaviour
+ <c><anno>Port</anno> ! {self(), close}</c> except for the error behaviour
(see below), and that the port does <em>not</em> reply with
<c>{Port, closed}</c>. Any process may close a port with
<c>port_close/1</c>, not only the port owner (the connected
process).</p>
- <p>For comparison: <c>Port ! {self(), close}</c> fails with
- <c>badarg</c> if <c>Port</c> cannot be sent to (i.e.,
- <c>Port</c> refers neither to a port nor to a process). If
- <c>Port</c> is a closed port nothing happens. If <c>Port</c>
+ <p>For comparison: <c><anno>Port</anno> ! {self(), close}</c> fails with
+ <c>badarg</c> if <c><anno>Port</anno></c> cannot be sent to (i.e.,
+ <c><anno>Port</anno></c> refers neither to a port nor to 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, but if
the calling process is not the port owner the <em>port owner</em> fails with <c>badsig</c>.</p>
<p>Note that any process can close a port using
- <c>Port ! {PortOwner, close}</c> just as if it itself was
+ <c><anno>Port</anno> ! {PortOwner, close}</c> just as if it itself was
the port owner, but the reply always goes to the port owner.</p>
<p>In short: <c>port_close(Port)</c> has a cleaner and more
- logical behaviour than <c>Port ! {self(), close}</c>.</p>
- <p>Failure: <c>badarg</c> if <c>Port</c> is not an open port or
+ logical behaviour than <c><anno>Port</anno> ! {self(), close}</c>.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not an open port or
the registered name of an open port.</p>
</desc>
</func>
<func>
- <name>port_command(Port, Data) -> true</name>
+ <name name="port_command" arity="2"/>
<fsummary>Send data to a port</fsummary>
- <type>
- <v>Port = port() | atom()</v>
- <v>Data = iodata()</v>
- </type>
<desc>
<p>Sends data to a port. Same as
- <c>Port ! {self(), {command, Data}}</c> except for the error
+ <c><anno>Port</anno> ! {self(), {command, Data}}</c> except for the error
behaviour (see below). Any process may send data to a port
with <c>port_command/2</c>, not only the port owner
(the connected process).</p>
- <p>For comparison: <c>Port ! {self(), {command, Data}}</c>
- fails with <c>badarg</c> if <c>Port</c> cannot be sent to
- (i.e., <c>Port</c> refers neither to a port nor to a process).
- If <c>Port</c> is a closed port the data message disappears
- without a sound. If <c>Port</c> is open and the calling
+ <p>For comparison: <c><anno>Port</anno> ! {self(), {command, Data}}</c>
+ fails with <c>badarg</c> if <c><anno>Port</anno></c> cannot be sent to
+ (i.e., <c><anno>Port</anno></c> refers neither to a port nor to 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>
- also if <c>Data</c> is not a valid IO list.</p>
+ also if <c><anno>Data</anno></c> is not a valid IO list.</p>
<p>Note that any process can send to a port using
- <c>Port ! {PortOwner, {command, Data}}</c> just as if it
+ <c><anno>Port</anno> ! {PortOwner, {command, <anno>Data</anno>}}</c> just as if it
itself was the port owner.</p>
- <p>In short: <c>port_command(Port, Data)</c> has a cleaner and
+ <p>In short: <c>port_command(<anno>Port</anno>, <anno>Data</anno>)</c> has a cleaner and
more logical behaviour than
- <c>Port ! {self(), {command, Data}}</c>.</p>
+ <c><anno>Port</anno> ! {self(), {command, Data}}</c>.</p>
<p>If the port is busy, the calling process will be suspended
until the port is not busy anymore.</p>
<p>Failures:</p>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c>Port</c> is not an open port or the registered name
+ 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>Data</c> is not a valid io list.
+ If <c><anno>Data</anno></c> is not a valid io list.
</item>
</taglist>
</desc>
</func>
<func>
- <name>port_command(Port, Data, OptionList) -> boolean()</name>
+ <name name="port_command" arity="3"/>
<fsummary>Send data to a port</fsummary>
- <type>
- <v>Port = port() | atom()</v>
- <v>Data = iodata()</v>
- <v>OptionList = [Option]</v>
- <v>Option = force</v>
- <v>Option = nosuspend</v>
- </type>
<desc>
<p>Sends data to a port. <c>port_command(Port, Data, [])</c>
equals <c>port_command(Port, Data)</c>.</p>
@@ -3379,7 +2950,7 @@ os_prompt% </pre>
otherwise, <c>true</c> is returned.</p>
<p>If the port is busy, the calling process will be suspended
until the port is not busy anymore.</p>
- <p>Currently the following <c>Option</c>s are valid:</p>
+ <p>Currently the following <c><anno>Option</anno></c>s are valid:</p>
<taglist>
<tag><c>force</c></tag>
<item>The calling process will not be suspended if the port is
@@ -3403,16 +2974,16 @@ os_prompt% </pre>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c>Port</c> is not an open port or the registered name
+ 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>Data</c> is not a valid io list.
+ If <c><anno>Data</anno></c> is not a valid io list.
</item>
<tag><c>badarg</c></tag>
<item>
- If <c>OptionList</c> is not a valid option list.
+ If <c><anno>OptionList</anno></c> is not a valid option list.
</item>
<tag><c>notsup</c></tag>
<item>
@@ -3424,15 +2995,11 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>port_connect(Port, Pid) -> true</name>
+ <name name="port_connect" arity="2"/>
<fsummary>Set the owner of a port</fsummary>
- <type>
- <v>Port = port() | atom()</v>
- <v>Pid = pid()</v>
- </type>
<desc>
- <p>Sets the port owner (the connected port) to <c>Pid</c>.
- Roughly the same as <c>Port ! {self(), {connect, Pid}}</c>
+ <p>Sets the port owner (the connected port) to <c><anno>Pid</anno></c>.
+ Roughly the same as <c><anno>Port</anno> ! {self(), {connect, <anno>Pid</anno>}}</c>
except for the following:</p>
<list type="bulleted">
<item>
@@ -3450,164 +3017,146 @@ os_prompt% </pre>
<c>unlink(Port)</c> if this is not desired. Any process may
set the port owner to be any process with
<c>port_connect/2</c>.</p>
- <p>For comparison: <c>Port ! {self(), {connect, Pid}}</c> fails
- with <c>badarg</c> if <c>Port</c> cannot be sent to (i.e.,
- <c>Port</c> refers neither to a port nor to a process). If
- <c>Port</c> is a closed port nothing happens. If <c>Port</c>
+ <p>For comparison: <c><anno>Port</anno> ! {self(), {connect, <anno>Pid</anno>}}</c> fails
+ with <c>badarg</c> if <c><anno>Port</anno></c> cannot be sent to (i.e.,
+ <c><anno>Port</anno></c> refers neither to a port nor to 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, connected}</c> to the old
port owner. Note that the old port owner is still linked to
- the port, and that the new is not. If <c>Port</c> is an open
+ the port, and that 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>Pid</c> is not an
+ owner fails with <c>badsig</c> also if <c><anno>Pid</anno></c> is not an
existing local pid.</p>
<p>Note that any process can set the port owner using
- <c>Port ! {PortOwner, {connect, Pid}}</c> just as if it
+ <c><anno>Port</anno> ! {PortOwner, {connect, <anno>Pid</anno>}}</c> just as if it
itself was the port owner, but the reply always goes to
the port owner.</p>
- <p>In short: <c>port_connect(Port, Pid)</c> has a cleaner and
+ <p>In short: <c>port_connect(<anno>Port</anno>, <anno>Pid</anno>)</c> has a cleaner and
more logical behaviour than
- <c>Port ! {self(),{connect,Pid}}</c>.</p>
- <p>Failure: <c>badarg</c> if <c>Port</c> is not an open port
- or the registered name of an open port, or if <c>Pid</c> is
+ <c><anno>Port</anno> ! {self(),{connect,<anno>Pid</anno>}}</c>.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not an open port
+ or the registered name of an open port, or if <c><anno>Pid</anno></c> is
not an existing local pid.</p>
</desc>
</func>
<func>
- <name>port_control(Port, Operation, Data) -> Res</name>
+ <name name="port_control" arity="3"/>
<fsummary>Perform a synchronous control operation on a port</fsummary>
- <type>
- <v>Port = port() | atom()</v>
- <v>Operation = integer()</v>
- <v>Data = Res = iodata()</v>
- </type>
<desc>
<p>Performs a synchronous control operation on a port.
- The meaning of <c>Operation</c> and <c>Data</c> depends on
+ The meaning of <c><anno>Operation</anno></c> and <c><anno>Data</anno></c> depends on
the port, i.e., on the port driver. Not all port drivers
support this control feature.</p>
<p>Returns: a list of integers in the range 0 through 255, or a
binary, depending on the port driver. The meaning of
the returned data also depends on the port driver.</p>
- <p>Failure: <c>badarg</c> if <c>Port</c> is not an open port or
- the registered name of an open port, if <c>Operation</c>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not an open port or
+ the registered name of an open port, if <c><anno>Operation</anno></c>
cannot fit in a 32-bit integer, if the port driver does not
support synchronous control operations, or if the port driver
so decides for any reason (probably something wrong with
- <c>Operation</c> or <c>Data</c>).</p>
+ <c><anno>Operation</anno></c> or <c><anno>Data</anno></c>).</p>
</desc>
</func>
<func>
- <name>erlang:port_call(Port, Operation, Data) -> term()</name>
+ <name name="port_call" arity="3"/>
<fsummary>Synchronous call to a port with term data</fsummary>
- <type>
- <v>Port = port() | atom()</v>
- <v>Operation = integer()</v>
- <v>Data = term()</v>
- </type>
<desc>
<p>Performs a synchronous call to a port. The meaning of
- <c>Operation</c> and <c>Data</c> depends on the port, i.e.,
+ <c><anno>Operation</anno></c> and <c><anno>Data</anno></c> depends on the port, i.e.,
on the port driver. Not all port drivers support this feature.</p>
- <p><c>Port</c> is a port identifier, referring to a driver.</p>
- <p><c>Operation</c> is an integer, which is passed on to
+ <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>Data</c> is any Erlang term. This data is converted to
+ <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>Failure: <c>badarg</c> if <c>Port</c> is not an open port or
- the registered name of an open port, if <c>Operation</c>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not an open port or
+ the registered name of an open port, if <c><anno>Operation</anno></c>
cannot fit in a 32-bit integer, if the port driver does not
support synchronous control operations, or if the port driver
so decides for any reason (probably something wrong with
- <c>Operation</c> or <c>Data</c>).</p>
+ <c><anno>Operation</anno></c> or <c><anno>Data</anno></c>).</p>
</desc>
</func>
<func>
- <name>erlang:port_info(Port) -> [{Item, Info}] | undefined</name>
+ <name name="port_info" arity="1"/>
+ <type name="port_info_result_item"/>
<fsummary>Information about a port</fsummary>
- <type>
- <v>Port = port() | atom()</v>
- <v>Item, Info -- see below</v>
- </type>
<desc>
<p>Returns a list containing tuples with information about
- the <c>Port</c>, or <c>undefined</c> if the port is not open.
+ the <c><anno>Port</anno></c>, or <c>undefined</c> if the port is not open.
The order of the tuples is not defined, nor are all the
tuples mandatory.</p>
<taglist>
- <tag><c>{registered_name, RegName}</c></tag>
+ <tag><c>{registered_name, <anno>RegName</anno>}</c></tag>
<item>
- <p><c>RegName</c> (an atom) is the registered name of
+ <p><c><anno>RegName</anno></c> (an atom) is the registered name of
the port. If the port has no registered name, this tuple
is not present in the list.</p>
</item>
- <tag><c>{id, Index}</c></tag>
+ <tag><c>{id, <anno>Index</anno>}</c></tag>
<item>
- <p><c>Index</c> (an integer) is the internal index of the
+ <p><c><anno>Index</anno></c> (an integer) is the internal index of the
port. This index may be used to separate ports.</p>
</item>
- <tag><c>{connected, Pid}</c></tag>
+ <tag><c>{connected, <anno>Pid</anno>}</c></tag>
<item>
- <p><c>Pid</c> is the process connected to the port.</p>
+ <p><c><anno>Pid</anno></c> is the process connected to the port.</p>
</item>
- <tag><c>{links, Pids}</c></tag>
+ <tag><c>{links, <anno>Pids</anno>}</c></tag>
<item>
- <p><c>Pids</c> is a list of pids to which processes the
+ <p><c><anno>Pids</anno></c> is a list of pids to which processes the
port is linked.</p>
</item>
- <tag><c>{name, String}</c></tag>
+ <tag><c>{name, <anno>String</anno>}</c></tag>
<item>
- <p><c>String</c> is the command name set by
+ <p><c><anno>String</anno></c> is the command name set by
<c>open_port</c>.</p>
</item>
- <tag><c>{input, Bytes}</c></tag>
+ <tag><c>{input, <anno>Bytes</anno>}</c></tag>
<item>
- <p><c>Bytes</c> is the total number of bytes read from
+ <p><c><anno>Bytes</anno></c> is the total number of bytes read from
the port.</p>
</item>
- <tag><c>{output, Bytes}</c></tag>
+ <tag><c>{output, <anno>Bytes</anno>}</c></tag>
<item>
- <p><c>Bytes</c> is the total number of bytes written to
+ <p><c><anno>Bytes</anno></c> is the total number of bytes written to
the port.</p>
</item>
- <tag><c>{os_pid, Integer | undefined}</c></tag>
+ <tag><c>{os_pid, <anno>OsPid</anno> | undefined}</c></tag>
<item>
- <p><c>Integer</c> is the process identifier (or equivalent) of an OS process created with <c>open_port({spawn | spawn_executable, Command}, Options)</c>. If the port is not the result of spawning an OS process, the value is <c>undefined</c>.</p>
+ <p><c> <anno>OsPid</anno></c> is the process identifier (or equivalent) of an OS process created with <c>open_port({spawn | spawn_executable, Command}, Options)</c>. If the port is not the result of spawning an OS process, the value is <c>undefined</c>.</p>
</item>
</taglist>
- <p>Failure: <c>badarg</c> if <c>Port</c> is not a local port.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local port.</p>
</desc>
</func>
<func>
- <name>erlang:port_info(Port, Item) -> {Item, Info} | undefined | []</name>
+ <name name="port_info" arity="2"/>
+ <type name="port_info_item"/>
+ <type name="port_info_result_item"/>
<fsummary>Information about a port</fsummary>
- <type>
- <v>Port = port() | atom()</v>
- <v>Item, Info -- see below</v>
- </type>
- <desc>
- <p>Returns information about <c>Port</c> as specified
- by <c>Item</c>, or <c>undefined</c> if the port is not open.
- Also, if <c>Item == registered_name</c> and the port has no
- registered name, [] is returned.</p>
- <p>For valid values of <c>Item</c>, and corresponding
- values of <c>Info</c>, see
+ <desc>
+ <p>Returns information about <c><anno>Port</anno></c> as specified
+ by <c><anno>Item</anno></c>, or <c>undefined</c> if the port is not open.
+ Also, if <c>Item =:= registered_name</c> and the port has no
+ registered name, <c>[]</c> is returned.</p>
+ <p>For valid values of <c><anno>Item</anno></c>, and corresponding
+ values of <c><anno>Result</anno></c>, see
<seealso marker="#port_info/1">erlang:port_info/1</seealso>.</p>
- <p>Failure: <c>badarg</c> if <c>Port</c> is not a local port.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local port.</p>
</desc>
</func>
<func>
- <name>erlang:port_to_list(Port) -> string()</name>
+ <name name="port_to_list" arity="1"/>
<fsummary>Text representation of a port identifier</fsummary>
- <type>
- <v>Port = port()</v>
- </type>
<desc>
<p>Returns a string which corresponds to the text
- representation of the port identifier <c>Port</c>.</p>
+ representation of the port identifier <c><anno>Port</anno></c>.</p>
<warning>
<p>This BIF is intended for debugging and for use in
the Erlang operating system. It should not be used in
@@ -3616,18 +3165,15 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:ports() -> [port()]</name>
+ <name name="ports" arity="0"/>
<fsummary>All open ports</fsummary>
<desc>
<p>Returns a list of all ports on the local node.</p>
</desc>
</func>
<func>
- <name>pre_loaded() -> [Module]</name>
+ <name name="pre_loaded" arity="0"/>
<fsummary>List of all pre-loaded modules</fsummary>
- <type>
- <v>Module = atom()</v>
- </type>
<desc>
<p>Returns a list of Erlang modules which are pre-loaded in
the system. As all loading of code is done through the file
@@ -3636,220 +3182,222 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:process_display(Pid, Type) -> void()</name>
+ <name name="process_display" arity="2"/>
<fsummary>Write information about a local process on standard error</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Type = backtrace</v>
- </type>
<desc>
- <p>Writes information about the local process <c>Pid</c> on
+ <p>Writes information about the local process <c><anno>Pid</anno></c> on
standard error. The currently allowed value for the atom
- <c>Type</c> is <c>backtrace</c>, which shows the contents of
+ <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>
</func>
<func>
- <name>process_flag(Flag, Value) -> OldValue</name>
- <fsummary>Set process flags for the calling process</fsummary>
- <type>
- <v>Flag, Value, OldValue -- see below</v>
- </type>
+ <name name="process_flag" arity="2" clause_i="1"/>
+ <fsummary>Set process flag trap_exit for the calling process</fsummary>
<desc>
- <p>Sets certain flags for the process which calls this
- function. Returns the old value of the flag.</p>
- <taglist>
- <tag><c>process_flag(trap_exit, Boolean)</c></tag>
- <item>
- <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> messages, which can be received as ordinary
- messages. If <c>trap_exit</c> is set to <c>false</c>, the
- process exits if it receives an exit signal other than
- <c>normal</c> and the exit signal is propagated to its
- linked processes. Application processes should normally
- not trap exits.</p>
- <p>See also <seealso marker="#exit/2">exit/2</seealso>.</p>
- </item>
- <tag><c>process_flag(error_handler, Module)</c></tag>
- <item>
- <p>This is used by a process to redefine the error handler
- for undefined function calls and undefined registered
- processes. Inexperienced users should not use this flag
- since code auto-loading is dependent on the correct
- operation of the error handling module.</p>
- </item>
- <tag><c>process_flag(min_heap_size, MinHeapSize)</c></tag>
- <item>
- <p>This changes the minimum heap size for the calling
- process.</p>
- </item>
- <tag><c>process_flag(min_bin_vheap_size, MinBinVHeapSize)</c></tag>
- <item>
- <p>This changes the minimum binary virtual heap size for the calling
- process.</p>
- </item>
- <tag><marker id="process_flag_priority"><c>process_flag(priority, Level)</c></marker></tag>
- <item>
- <p>This sets the process priority. <c>Level</c> is an atom.
- There are currently four priority levels: <c>low</c>,
- <c>normal</c>, <c>high</c>, and <c>max</c>. The default
- priority level is <c>normal</c>. <em>NOTE</em>: The
- <c>max</c> priority level is reserved for internal use in
- the Erlang runtime system, and should <em>not</em> be used
- by others.
- </p>
- <p>Internally in each priority level processes are scheduled
- in a round robin fashion.
- </p>
- <p>Execution of processes on priority <c>normal</c> and
- priority <c>low</c> will be interleaved. Processes on
- priority <c>low</c> will be selected for execution less
- frequently than processes on priority <c>normal</c>.
- </p>
- <p>When there are runnable processes on priority <c>high</c>
- no processes on priority <c>low</c>, or <c>normal</c> will
- be selected for execution. Note, however, that this does
- <em>not</em> mean that no processes on priority <c>low</c>,
- or <c>normal</c> will be able to run when there are
- processes on priority <c>high</c> running. On the runtime
- system with SMP support there might be more processes running
- in parallel than processes on priority <c>high</c>, i.e.,
- a <c>low</c>, and a <c>high</c> priority process might
- execute at the same time.
- </p>
- <p>When there are runnable processes on priority <c>max</c>
- no processes on priority <c>low</c>, <c>normal</c>, or
- <c>high</c> will be selected for execution. As with the
- <c>high</c> priority, processes on lower priorities might
- 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 amount
- of reductions since the last time it was selected for
- execution.
- </p>
- <p><em>NOTE</em>: You should not depend on the scheduling
- to remain exactly as it is today. Scheduling, at least on
- the runtime system with SMP support, is very likely to be
- modified in the future in order to better utilize available
- processor cores.
- </p>
- <p>There is currently <em>no</em> automatic mechanism for
- avoiding priority inversion, such as priority inheritance,
- or priority ceilings. When using priorities you have
- to 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 don't have control over may cause the <c>high</c>
- priority process to wait for a processes with lower
- priority, i.e., effectively decreasing the priority of the
- <c>high</c> priority process during the call. Even if this
- isn't the case with one version of the code that you don't
- have under your control, it might be the case in a future
- version of it. This might, for example, happen if a
- <c>high</c> priority process triggers code loading, since
- the code server runs on priority <c>normal</c>.
- </p>
- <p>Other priorities than <c>normal</c> are normally not needed.
- When other priorities are used, they need to be used
- with care, especially the <c>high</c> priority <em>must</em>
- be used with care. A process on <c>high</c> priority should
- only perform work for short periods of time. Busy looping for
- long periods of time in a <c>high</c> priority process will
- most likely cause problems, since there are important servers
- in OTP running on priority <c>normal</c>.
- </p>
- </item>
-
- <tag><c>process_flag(save_calls, N)</c></tag>
- <item>
- <p><c>N</c> must be an integer in the interval 0..10000.
- If <c>N</c> &gt; 0, call saving is made active for the
- process, which means that information about the <c>N</c>
- most recent global function calls, BIF calls, sends and
- receives made by the process are saved in a list, which
- can be retrieved with
- <c>process_info(Pid, last_calls)</c>. A global function
- call is one in which the module of the function is
- explicitly mentioned. Only a fixed amount of information
- is saved: a tuple <c>{Module, Function, Arity}</c> for
- function calls, and the mere 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). If <c>N</c> = 0,
- call saving is disabled for the process, which is the
- default. Whenever the size of the call saving list is set,
- its contents are reset.</p>
- </item>
- <tag><c>process_flag(sensitive, Boolean)</c></tag>
- <item>
- <p>Set or clear the <c>sensitive</c> flag for the current process.
- When a process has been marked as sensitive by calling
- <c>process_flag(sensitive, true)</c>, features in the run-time
- system that can be used for examining the data and/or inner working
- of the process are silently disabled.</p>
- <p>Features that are disabled include (but are not limited to)
- the following:</p>
- <p>Tracing: Trace flags can still be set for the process, but no
- trace messages of any kind will be generated.
- (If the <c>sensitive</c> flag is turned off, trace messages will
- again be generated if there are any trace flags set.)</p>
- <p>Sequential tracing: The sequential trace token will be propagated
- as usual, but no sequential trace messages will be generated.</p>
- <p><c>process_info/1,2</c> cannot be used to read out the message
- queue or the process dictionary (both will be returned as empty lists).</p>
- <p>Stack back-traces cannot be displayed for the process.</p>
- <p>In crash dumps, the stack, messages, and the process dictionary
- will be omitted.</p>
- <p>If <c>{save_calls,N}</c> has been set for the process, no
- function calls will be saved to the call saving list.
- (The call saving list will not be cleared; furthermore, send, receive,
- and timeout events will still be added to the list.)</p>
- </item>
- </taglist>
+ <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> messages, which can be received as ordinary
+ messages. If <c>trap_exit</c> is set to <c>false</c>, the
+ process exits if it receives an exit signal other than
+ <c>normal</c> and the exit signal is propagated to its
+ linked processes. Application processes should normally
+ not trap exits.</p>
+ <p>Returns the old value of the flag.</p>
+ <p>See also <seealso marker="#exit/2">exit/2</seealso>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="process_flag" arity="2" clause_i="2"/>
+ <fsummary>Set process flag error_handler for the calling process</fsummary>
+ <desc>
+ <p>This is used by a process to redefine the error handler
+ for undefined function calls and undefined registered
+ processes. Inexperienced users should not use this flag
+ since code auto-loading is dependent on the correct
+ operation of the error handling module.</p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="process_flag" arity="2" clause_i="3"/>
+ <fsummary>Set process flag min_heap_size for the calling process</fsummary>
+ <desc>
+ <p>This changes the minimum heap size for the calling
+ process.</p>
+ <p>Returns the old value of the flag.</p>
</desc>
</func>
<func>
- <name>process_flag(Pid, Flag, Value) -> OldValue</name>
+ <name name="process_flag" arity="2" clause_i="4"/>
+ <fsummary>Set process flag min_bin_vheap_size for the calling process</fsummary>
+ <desc>
+ <p>This changes the minimum binary virtual heap size for the calling
+ process.</p>
+ <p>Returns the old value of the flag.</p> </desc>
+ </func>
+ <func>
+ <name name="process_flag" arity="2" clause_i="5"/>
+ <type name="priority_level"/>
+ <fsummary>Set process flag priority for the calling process</fsummary>
+ <desc>
+ <p><marker id="process_flag_priority"></marker>
+ This sets the process priority. <c><anno>Level</anno></c> is an atom.
+ There are currently four priority levels: <c>low</c>,
+ <c>normal</c>, <c>high</c>, and <c>max</c>. The default
+ priority level is <c>normal</c>. <em>NOTE</em>: The
+ <c>max</c> priority level is reserved for internal use in
+ the Erlang runtime system, and should <em>not</em> be used
+ by others.
+ </p>
+ <p>Internally in each priority level processes are scheduled
+ in a round robin fashion.
+ </p>
+ <p>Execution of processes on priority <c>normal</c> and
+ priority <c>low</c> will be interleaved. Processes on
+ priority <c>low</c> will be selected for execution less
+ frequently than processes on priority <c>normal</c>.
+ </p>
+ <p>When there are runnable processes on priority <c>high</c>
+ no processes on priority <c>low</c>, or <c>normal</c> will
+ be selected for execution. Note, however, that this does
+ <em>not</em> mean that no processes on priority <c>low</c>,
+ or <c>normal</c> will be able to run when there are
+ processes on priority <c>high</c> running. On the runtime
+ system with SMP support there might be more processes running
+ in parallel than processes on priority <c>high</c>, i.e.,
+ a <c>low</c>, and a <c>high</c> priority process might
+ execute at the same time.
+ </p>
+ <p>When there are runnable processes on priority <c>max</c>
+ no processes on priority <c>low</c>, <c>normal</c>, or
+ <c>high</c> will be selected for execution. As with the
+ <c>high</c> priority, processes on lower priorities might
+ 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 amount
+ of reductions since the last time it was selected for
+ execution.
+ </p>
+ <p><em>NOTE</em>: You should not depend on the scheduling
+ to remain exactly as it is today. Scheduling, at least on
+ the runtime system with SMP support, is very likely to be
+ modified in the future in order to better utilize available
+ processor cores.
+ </p>
+ <p>There is currently <em>no</em> automatic mechanism for
+ avoiding priority inversion, such as priority inheritance,
+ or priority ceilings. When using priorities you have
+ to 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 don't have control over may cause the <c>high</c>
+ priority process to wait for a processes with lower
+ priority, i.e., effectively decreasing the priority of the
+ <c>high</c> priority process during the call. Even if this
+ isn't the case with one version of the code that you don't
+ have under your control, it might be the case in a future
+ version of it. This might, for example, happen if a
+ <c>high</c> priority process triggers code loading, since
+ the code server runs on priority <c>normal</c>.
+ </p>
+ <p>Other priorities than <c>normal</c> are normally not needed.
+ When other priorities are used, they need to be used
+ with care, especially the <c>high</c> priority <em>must</em>
+ be used with care. A process on <c>high</c> priority should
+ only perform work for short periods of time. Busy looping for
+ long periods of time in a <c>high</c> priority process will
+ most likely cause problems, since there are important servers
+ in OTP running 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>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> &gt; 0, call saving is made active for the
+ process, which means that information about the <c><anno>N</anno></c>
+ most recent global function calls, BIF calls, sends and
+ receives made by the process are saved in a list, which
+ can be retrieved with
+ <c>process_info(Pid, last_calls)</c>. A global function
+ call is one in which the module of the function is
+ explicitly mentioned. Only a fixed amount of information
+ is saved: a tuple <c>{Module, Function, Arity}</c> for
+ function calls, and the mere 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). If <c>N</c> = 0,
+ call saving is disabled for the process, which is the
+ default. Whenever the size of the call saving list is set,
+ its contents are reset.</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 sensitive for the calling process</fsummary>
+ <desc>
+ <p>Set or clear the <c>sensitive</c> flag for the current process.
+ When a process has been marked as sensitive by calling
+ <c>process_flag(sensitive, true)</c>, features in the run-time
+ system that can be used for examining the data and/or inner working
+ of the process are silently disabled.</p>
+ <p>Features that are disabled include (but are not limited to)
+ the following:</p>
+ <p>Tracing: Trace flags can still be set for the process, but no
+ trace messages of any kind will be generated.
+ (If the <c>sensitive</c> flag is turned off, trace messages will
+ again be generated if there are any trace flags set.)</p>
+ <p>Sequential tracing: The sequential trace token will be propagated
+ as usual, but no sequential trace messages will be generated.</p>
+ <p><c>process_info/1,2</c> cannot be used to read out the message
+ queue or the process dictionary (both will be returned as empty lists).</p>
+ <p>Stack back-traces cannot be displayed for the process.</p>
+ <p>In crash dumps, the stack, messages, and the process dictionary
+ will be omitted.</p>
+ <p>If <c>{save_calls,N}</c> has been set for the process, no
+ function calls will be saved to the call saving list.
+ (The call saving list will not be cleared; furthermore, send, receive,
+ and timeout events will still be added to the list.)</p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="process_flag" arity="3"/>
<fsummary>Set process flags for a process</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Flag, Value, OldValue -- see below</v>
- </type>
<desc>
- <p>Sets certain flags for the process <c>Pid</c>, in the same
+ <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>.
Returns the old value of the flag. The allowed values for
- <c>Flag</c> are only a subset of those allowed in
+ <c><anno>Flag</anno></c> are only a subset of those allowed in
<c>process_flag/2</c>, namely: <c>save_calls</c>.</p>
- <p>Failure: <c>badarg</c> if <c>Pid</c> is not a local process.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Pid</anno></c> is not a local process.</p>
</desc>
</func>
<func>
- <name>process_info(Pid) -> InfoResult</name>
+ <name name="process_info" arity="1"/>
+ <type name="process_info_result_item"/>
+ <type name="priority_level"/>
+ <type name="stack_item"/>
<fsummary>Information about a process</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Item = atom()</v>
- <v>Info = term()</v>
- <v>InfoTuple = {Item, Info}</v>
- <v>InfoTupleList = [InfoTuple]</v>
- <v>InfoResult = InfoTupleList | undefined</v>
- </type>
- <desc>
- <p>Returns a list containing <c>InfoTuple</c>s with
+ <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>InfoTuple</c>s is not defined, nor
- are all the <c>InfoTuple</c>s mandatory. The <c>InfoTuple</c>s
+ The order of the <c><anno>InfoTuple</anno></c>s is not defined, nor
+ are all the <c><anno>InfoTuple</anno></c>s mandatory. The <c><anno>InfoTuple</anno></c>s
part of the result may be changed without prior notice.
- Currently <c>InfoTuple</c>s with the following <c>Item</c>s
+ Currently <c><anno>InfoTuple</anno></c>s with the following items
are part of the result:
<c>current_function</c>, <c>initial_call</c>, <c>status</c>,
<c>message_queue_len</c>, <c>messages</c>, <c>links</c>,
@@ -3857,12 +3405,12 @@ os_prompt% </pre>
<c>priority</c>, <c>group_leader</c>, <c>total_heap_size</c>,
<c>heap_size</c>, <c>stack_size</c>, <c>reductions</c>, and
<c>garbage_collection</c>.
- If the process identified by <c>Pid</c> has a registered name
- also an <c>InfoTuple</c> with <c>Item == registered_name</c>
+ If the process identified by <c><anno>Pid</anno></c> has a registered name
+ also an <c><anno>InfoTuple</anno></c> with the item <c>registered_name</c>
will appear.
</p>
<p>See <seealso marker="#process_info/2">process_info/2</seealso>
- for information about specific <c>InfoTuple</c>s.</p>
+ for information about specific <c><anno>InfoTuple</anno></c>s.</p>
<warning>
<p>This BIF is intended for <em>debugging only</em>, use
<seealso marker="#process_info/2">process_info/2</seealso>
@@ -3873,113 +3421,108 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>process_info(Pid, ItemSpec) -> InfoResult</name>
+ <name name="process_info" arity="2" clause_i="1"/>
+ <name name="process_info" arity="2" clause_i="2"/>
+ <type name="process_info_item"/>
+ <type name="process_info_result_item"/>
+ <type name="stack_item"/>
+ <type name="priority_level"/>
<fsummary>Information about a process</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Item = atom()</v>
- <v>Info = term()</v>
- <v>ItemList = [Item]</v>
- <v>ItemSpec = Item | ItemList</v>
- <v>InfoTuple = {Item, Info}</v>
- <v>InfoTupleList = [InfoTuple]</v>
- <v>InfoResult = InfoTuple | InfoTupleList | undefined | []</v>
- </type>
- <desc>
- <p>Returns information about the process identified by <c>Pid</c>
- as specified by the <c>ItemSpec</c>, or <c>undefined</c> if the
+ <desc>
+ <p>Returns information about the process identified by <c><anno>Pid</anno></c>
+ as specified by the <c><anno>Item</anno></c> or the <c><anno>ItemList</anno></c>, or <c>undefined</c> if the
process is not alive.
</p>
- <p>If the process is alive and <c>ItemSpec</c> is a single
- <c>Item</c>, the returned value is the corresponding
- <c>InfoTuple</c> unless <c>ItemSpec == registered_name</c>
+ <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 due to
historical reasons, and is kept for backward compatibility.
</p>
- <p>If <c>ItemSpec</c> is an <c>ItemList</c>, the result is an
- <c>InfoTupleList</c>. The <c>InfoTuple</c>s in the
- <c>InfoTupleList</c> will appear with the corresponding
- <c>Item</c>s in the same order as the <c>Item</c>s appeared
- in the <c>ItemList</c>. Valid <c>Item</c>s may appear multiple
- times in the <c>ItemList</c>.
+ <p>If an <c>ItemList</c> is given, the result is an
+ <c><anno>InfoTupleList</anno></c>. The <c><anno>InfoTuple</anno></c>s in the
+ <c><anno>InfoTupleList</anno></c> will appear with the corresponding
+ <c><anno>Item</anno></c>s in the same order as the <c><anno>Item</anno></c>s appeared
+ in the <c><anno>ItemList</anno></c>. Valid <c><anno>Item</anno></c>s may appear multiple
+ times in the <c><anno>ItemList</anno></c>.
</p>
- <note><p>If <c>registered_name</c> is part of an <c>ItemList</c>
+ <note><p>If <c>registered_name</c> is part of an <c><anno>ItemList</anno></c>
and the process has no name registered a
- <c>{registered_name, []}</c> <c>InfoTuple</c> <em>will</em>
- appear in the resulting <c>InfoTupleList</c>. This
- behavior is different than when
- <c>ItemSpec == registered_name</c>, and than when
+ <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 than when a single
+ <c>Item =:= registered_name</c> is given, and than when
<c>process_info/1</c> is used.
</p></note>
- <p>Currently the following <c>InfoTuple</c>s with corresponding
- <c>Item</c>s are valid:</p>
+ <p>Currently the following <c><anno>InfoTuple</anno></c>s with corresponding
+ <c><anno>Item</anno></c>s are valid:</p>
<taglist>
- <tag><c>{backtrace, Bin}</c></tag>
+ <tag><c>{backtrace, <anno>Bin</anno>}</c></tag>
<item>
- <p>The binary <c>Bin</c> contains the same information as
+ <p>The binary <c><anno>Bin</anno></c> contains the same information as
the output from
- <c>erlang:process_display(Pid, backtrace)</c>. Use
+ <c>erlang:process_display(<anno>Pid</anno>, backtrace)</c>. Use
<c>binary_to_list/1</c> to obtain the string of characters
from the binary.</p>
</item>
- <tag><c>{binary, BinInfo}</c></tag>
+ <tag><c>{binary, <anno>BinInfo</anno>}</c></tag>
<item>
- <p><c>BinInfo</c> is a list containing miscellaneous information
+ <p><c><anno>BinInfo</anno></c> is a list containing miscellaneous information
about binaries currently being referred to by this process.
- This <c>InfoTuple</c> may be changed or removed without prior
+ This <c><anno>InfoTuple</anno></c> may be changed or removed without prior
notice.</p>
</item>
- <tag><c>{catchlevel, CatchLevel}</c></tag>
+ <tag><c>{catchlevel, <anno>CatchLevel</anno>}</c></tag>
<item>
- <p><c>CatchLevel</c> is the number of currently active
- catches in this process. This <c>InfoTuple</c> may be
+ <p><c><anno>CatchLevel</anno></c> is the number of currently active
+ catches in this process. This <c><anno>InfoTuple</anno></c> may be
changed or removed without prior notice.</p>
</item>
- <tag><c>{current_function, {Module, Function, Arity}}</c></tag>
+ <tag><c>{current_function, {<anno>Module</anno>, <anno>Function</anno>, <anno>Arity</anno>}}</c></tag>
<item>
- <p><c>Module</c>, <c>Function</c>, <c>Arity</c> is
+ <p><c><anno>Module</anno></c>, <c><anno>Function</anno></c>, <c><anno>Arity</anno></c> is
the current function call of the process.</p>
</item>
- <tag><c>{current_location, {Module, Function, Arity, Location}}</c></tag>
+ <tag><c>{current_location, {<anno>Module</anno>, <anno>Function</anno>, <anno>Arity</anno>, <anno>Location</anno>}}</c></tag>
<item>
- <p><c>Module</c>, <c>Function</c>, <c>Arity</c> is
+ <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>Location</c> is a list of two-tuples that describes the
+ <c><anno>Location</anno></c> is a list of two-tuples that describes the
location in the source code.
</p>
</item>
- <tag><c>{current_stacktrace, Stack}</c></tag>
+ <tag><c>{current_stacktrace, <anno>Stack</anno>}</c></tag>
<item>
<p>Return 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>
</item>
- <tag><c>{dictionary, Dictionary}</c></tag>
+ <tag><c>{dictionary, <anno>Dictionary</anno>}</c></tag>
<item>
- <p><c>Dictionary</c> is the dictionary of the process.</p>
+ <p><c><anno>Dictionary</anno></c> is the dictionary of the process.</p>
</item>
- <tag><c>{error_handler, Module}</c></tag>
+ <tag><c>{error_handler, <anno>Module</anno>}</c></tag>
<item>
- <p><c>Module</c> is the error handler module used by
+ <p><c><anno>Module</anno></c> is the error handler module used by
the process (for undefined function calls, for example).</p>
</item>
- <tag><c>{garbage_collection, GCInfo}</c></tag>
+ <tag><c>{garbage_collection, <anno>GCInfo</anno>}</c></tag>
<item>
- <p><c>GCInfo</c> is a list which contains miscellaneous
+ <p><c><anno>GCInfo</anno></c> is a list which contains miscellaneous
information about garbage collection for this process.
- The content of <c>GCInfo</c> may be changed without
+ The content of <c><anno>GCInfo</anno></c> may be changed without
prior notice.</p>
</item>
- <tag><c>{group_leader, GroupLeader}</c></tag>
+ <tag><c>{group_leader, <anno>GroupLeader</anno>}</c></tag>
<item>
- <p><c>GroupLeader</c> is group leader for the IO of
+ <p><c><anno>GroupLeader</anno></c> is group leader for the IO of
the process.</p>
</item>
- <tag><c>{heap_size, Size}</c></tag>
+ <tag><c>{heap_size, <anno>Size</anno>}</c></tag>
<item>
- <p><c>Size</c> is the size in words of youngest heap generation
+ <p><c><anno>Size</anno></c> is the size in words of youngest heap generation
of the process. This generation currently include the stack
of the process. This information is highly implementation
dependent, and may change if the implementation change.
@@ -3991,9 +3534,9 @@ os_prompt% </pre>
the initial function call with which the process was
spawned.</p>
</item>
- <tag><c>{links, Pids}</c></tag>
+ <tag><c>{links, <anno>Pids</anno>}</c></tag>
<item>
- <p><c>Pids</c> is a list of pids, with processes to
+ <p><c><anno>Pids</anno></c> is a list of pids, with processes to
which the process has a link.</p>
</item>
<tag><c>{last_calls, false|Calls}</c></tag>
@@ -4004,131 +3547,131 @@ os_prompt% </pre>
If call saving is active, a list is returned, in which
the last element is the most recent called.</p>
</item>
- <tag><c>{memory, Size}</c></tag>
+ <tag><c>{memory, <anno>Size</anno>}</c></tag>
<item>
- <p><c>Size</c> is the size in bytes of the process. This
+ <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, MessageQueueLen}</c></tag>
+ <tag><c>{message_queue_len, <anno>MessageQueueLen</anno>}</c></tag>
<item>
- <p><c>MessageQueueLen</c> is the number of messages
+ <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>MessageQueue</c> returned as
+ the length of the list <c><anno>MessageQueue</anno></c> returned as
the info item <c>messages</c> (see below).</p>
</item>
- <tag><c>{messages, MessageQueue}</c></tag>
+ <tag><c>{messages, <anno>MessageQueue</anno>}</c></tag>
<item>
- <p><c>MessageQueue</c> is a list of the messages to
+ <p><c><anno>MessageQueue</anno></c> is a list of the messages to
the process, which have not yet been processed.</p>
</item>
- <tag><c>{min_heap_size, MinHeapSize}</c></tag>
+ <tag><c>{min_heap_size, <anno>MinHeapSize</anno>}</c></tag>
<item>
- <p><c>MinHeapSize</c> is the minimum heap size for the process.</p>
+ <p><c><anno>MinHeapSize</anno></c> is the minimum heap size for the process.</p>
</item>
- <tag><c>{min_bin_vheap_size, MinBinVHeapSize}</c></tag>
+ <tag><c>{min_bin_vheap_size, <anno>MinBinVHeapSize</anno>}</c></tag>
<item>
- <p><c>MinBinVHeapSize</c> is the minimum binary virtual heap size for the process.</p>
+ <p><c><anno>MinBinVHeapSize</anno></c> is the minimum binary virtual heap size for the process.</p>
</item>
- <tag><c>{monitored_by, Pids}</c></tag>
+ <tag><c>{monitored_by, <anno>Pids</anno>}</c></tag>
<item>
<p>A list of pids that are monitoring the process (with
<c>monitor/2</c>).</p>
</item>
- <tag><c>{monitors, Monitors}</c></tag>
+ <tag><c>{monitors, <anno>Monitors</anno>}</c></tag>
<item>
<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 pid, the list item
- is <c>{process, Pid}</c>, and for a remote process
+ is <c>{process, <anno>Pid</anno>}</c>, and for a remote process
monitor by name, the list item is
- <c>{process, {RegName, Node}}</c>.</p>
+ <c>{process, {<anno>RegName</anno>, <anno>Node</anno>}}</c>.</p>
</item>
<tag><c>{priority, Level}</c></tag>
<item>
- <p><c>Level</c> is the current priority level for
+ <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>
</item>
- <tag><c>{reductions, Number}</c></tag>
+ <tag><c>{reductions, <anno>Number</anno>}</c></tag>
<item>
- <p><c>Number</c> is the number of reductions executed by
+ <p><c><anno>Number</anno></c> is the number of reductions executed by
the process.</p>
</item>
- <tag><c>{registered_name, Atom}</c></tag>
+ <tag><c>{registered_name, <anno>Atom</anno>}</c></tag>
<item>
- <p><c>Atom</c> is the registered name of the process. If
+ <p><c><anno>Atom</anno></c> is the registered name of the process. If
the process has no registered name, this tuple is not
present in the list.</p>
</item>
- <tag><c>{sequential_trace_token, [] | SequentialTraceToken}</c></tag>
+ <tag><c>{sequential_trace_token, [] | <anno>SequentialTraceToken</anno>}</c></tag>
<item>
- <p><c>SequentialTraceToken</c> the sequential trace token for
- the process. This <c>InfoTuple</c> may be changed or removed
+ <p><c><anno>SequentialTraceToken</anno></c> the sequential trace token for
+ the process. This <c><anno>InfoTuple</anno></c> may be changed or removed
without prior notice.</p>
</item>
- <tag><c>{stack_size, Size}</c></tag>
+ <tag><c>{stack_size, <anno>Size</anno>}</c></tag>
<item>
- <p><c>Size</c> is the stack size of the process in words.</p>
+ <p><c><anno>Size</anno></c> is the stack size of the process in words.</p>
</item>
- <tag><c>{status, Status}</c></tag>
+ <tag><c>{status, <anno>Status</anno>}</c></tag>
<item>
- <p><c>Status</c> is the status of the process. <c>Status</c>
+ <p><c><anno>Status</anno></c> is the status of the process. <c><anno>Status</anno></c>
is <c>exiting</c>, <c>garbage_collecting</c>,
<c>waiting</c> (for a message), <c>running</c>,
<c>runnable</c> (ready to run, but another process is
running), or <c>suspended</c> (suspended on a "busy" port
or by the <c>erlang:suspend_process/[1,2]</c> BIF).</p>
</item>
- <tag><c>{suspending, SuspendeeList}</c></tag>
+ <tag><c>{suspending, <anno>SuspendeeList</anno>}</c></tag>
<item>
- <p><c>SuspendeeList</c> is a list of <c>{Suspendee,
- ActiveSuspendCount, OutstandingSuspendCount}</c> tuples.
- <c>Suspendee</c> is the pid of a process that have been or is to
- be suspended by the process identified by <c>Pid</c> via the
+ <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 pid of a process that have been or is to
+ be suspended by the process identified by <c><anno>Pid</anno></c> via the
<seealso marker="#suspend_process/2">erlang:suspend_process/2</seealso>
BIF, or the
<seealso marker="#suspend_process/1">erlang:suspend_process/1</seealso>
- BIF. <c>ActiveSuspendCount</c> is the number of times the
- <c>Suspendee</c> has been suspended by <c>Pid</c>.
- <c>OutstandingSuspendCount</c> is the number of not yet
- completed suspend requests sent by <c>Pid</c>. That is,
- if <c>ActiveSuspendCount /= 0</c>, <c>Suspendee</c> is
+ BIF. <c><anno>ActiveSuspendCount</anno></c> is the number of times the
+ <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,
+ if <c><anno>ActiveSuspendCount</anno> =/= 0</c>, <c><anno>Suspendee</anno></c> is
currently in the suspended state, and if
- <c>OutstandingSuspendCount /= 0</c> the <c>asynchronous</c>
+ <c><anno>OutstandingSuspendCount</anno> =/= 0</c> the <c>asynchronous</c>
option of <c>erlang:suspend_process/2</c> has been used and
- the suspendee has not yet been suspended by <c>Pid</c>.
- Note that the <c>ActiveSuspendCount</c> and
- <c>OutstandingSuspendCount</c> are not the total suspend count
- on <c>Suspendee</c>, only the parts contributed by <c>Pid</c>.
+ the suspendee has not yet been suspended by <c><anno>Pid</anno></c>.
+ Note that the <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>Pid</c>.
</p>
</item>
- <tag><c>{total_heap_size, Size}</c></tag>
+ <tag><c>{total_heap_size, <anno>Size</anno>}</c></tag>
<item>
- <p><c>Size</c> is the total size in words of all heap
+ <p><c><anno>Size</anno></c> is the total size in words of all heap
fragments of the process. This currently include the stack
of the process.
</p>
</item>
- <tag><c>{trace, InternalTraceFlags}</c></tag>
+ <tag><c>{trace, <anno>InternalTraceFlags</anno>}</c></tag>
<item>
- <p><c>InternalTraceFlags</c> is an integer representing
- internal trace flag for this process. This <c>InfoTuple</c>
+ <p><c><anno>InternalTraceFlags</anno></c> is an integer representing
+ internal trace flag for this process. This <c><anno>InfoTuple</anno></c>
may be changed or removed without prior notice.</p>
</item>
- <tag><c>{trap_exit, Boolean}</c></tag>
+ <tag><c>{trap_exit, <anno>Boolean</anno>}</c></tag>
<item>
- <p><c>Boolean</c> is <c>true</c> if the process is trapping
+ <p><c><anno>Boolean</anno></c> is <c>true</c> if the process is trapping
exits, otherwise it is <c>false</c>.</p>
</item>
</taglist>
<p>Note however, that not all implementations support every one
- of the above <c>Items</c>.</p>
- <p>Failure: <c>badarg</c> if <c>Pid</c> is not a local process,
- or if <c>Item</c> is not a valid <c>Item</c>.</p>
+ of the above <c><anno>Item</anno></c>s.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Pid</anno></c> is not a local process,
+ or if <c><anno>Item</anno></c> is not a valid <c><anno>Item</anno></c>.</p>
</desc>
</func>
<func>
- <name>processes() -> [pid()]</name>
+ <name name="processes" arity="0"/>
<fsummary>All processes</fsummary>
<desc>
<p>Returns a list of process identifiers corresponding to
@@ -4145,13 +3688,10 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>purge_module(Module) -> void()</name>
+ <name name="purge_module" arity="1"/>
<fsummary>Remove old code for a module</fsummary>
- <type>
- <v>Module = atom()</v>
- </type>
<desc>
- <p>Removes old code for <c>Module</c>. Before this BIF is used,
+ <p>Removes old code for <c><anno>Module</anno></c>. Before this BIF is used,
<c>erlang:check_process_code/2</c> should be called to check
that no processes are executing old code in the module.</p>
<warning>
@@ -4160,20 +3700,17 @@ os_prompt% </pre>
used elsewhere.</p>
</warning>
<p>Failure: <c>badarg</c> if there is no old code for
- <c>Module</c>.</p>
+ <c><anno>Module</anno></c>.</p>
</desc>
</func>
<func>
- <name>put(Key, Val) -> OldVal | undefined</name>
+ <name name="put" arity="2"/>
<fsummary>Add a new value to the process dictionary</fsummary>
- <type>
- <v>Key = Val = OldVal = term()</v>
- </type>
- <desc>
- <p>Adds a new <c>Key</c> to the process dictionary, associated
- with the value <c>Val</c>, and returns <c>undefined</c>. If
- <c>Key</c> already exists, the old value is deleted and
- replaced by <c>Val</c> and the function returns the old value.</p>
+ <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> already exists, the old value is deleted and
+ replaced by <c><anno>Val</anno></c> and the function returns the old value.</p>
<note>
<p>The values stored when <c>put</c> is evaluated within
the scope of a <c>catch</c> will not be retracted if a
@@ -4187,17 +3724,9 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:raise(Class, Reason, Stacktrace)</name>
+ <name name="raise" arity="3"/>
+ <type name="raise_stacktrace"/>
<fsummary>Stop execution with an exception of given class, reason and call stack backtrace</fsummary>
- <type>
- <v>Class = error | exit | throw</v>
- <v>Reason = term()</v>
- <v>Stacktrace = [{Module, Function, Arity | Args} | {Fun, Args}]</v>
- <v>&nbsp;Module = Function = atom()</v>
- <v>&nbsp;Arity = arity()</v>
- <v>&nbsp;Args = [term()]</v>
- <v>&nbsp;Fun = [fun()]</v>
- </type>
<desc>
<p>Stops the execution of the calling process with an
exception of given class, reason and call stack backtrace
@@ -4208,11 +3737,11 @@ os_prompt% </pre>
be avoided in applications, unless you know
very well what you are doing.</p>
</warning>
- <p><c>Class</c> is one of <c>error</c>, <c>exit</c> or
+ <p><c><anno>Class</anno></c> is one of <c>error</c>, <c>exit</c> or
<c>throw</c>, so if it were not for the stacktrace
- <c>erlang:raise(Class, Reason, Stacktrace)</c> is
- equivalent to <c>erlang:Class(Reason)</c>.
- <c>Reason</c> is any term and <c>Stacktrace</c> is a list as
+ <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>.
+ <c><anno>Reason</anno></c> is any term and <c><anno>Stacktrace</anno></c> is a list as
returned from <c>get_stacktrace()</c>, that is a list of
4-tuples <c>{Module, Function, Arity | Args,
Location}</c> where <c>Module</c> and <c>Function</c>
@@ -4229,24 +3758,21 @@ os_prompt% </pre>
terminate, it has no return value - unless the arguments are
invalid, in which case the function <em>returns the error reason</em>, that is <c>badarg</c>. If you want to be
really sure not to return you can call
- <c>error(erlang:raise(Class, Reason, Stacktrace))</c>
+ <c>error(erlang:raise(<anno>Class</anno>, <anno>Reason</anno>, <anno>Stacktrace</anno>))</c>
and hope to distinguish exceptions later.</p>
</desc>
</func>
<func>
- <name>erlang:read_timer(TimerRef) -> integer() >= 0 | false</name>
+ <name name="read_timer" arity="1"/>
<fsummary>Number of milliseconds remaining for a timer</fsummary>
- <type>
- <v>TimerRef = reference()</v>
- </type>
<desc>
- <p><c>TimerRef</c> is a timer reference returned by
+ <p><c><anno>TimerRef</anno></c> is a timer reference returned by
<seealso marker="#send_after/3">erlang:send_after/3</seealso>
or
<seealso marker="#start_timer/3">erlang:start_timer/3</seealso>.
If the timer is active, the function returns the time in
milliseconds left until the timer will expire, otherwise
- <c>false</c> (which means that <c>TimerRef</c> was never a
+ <c>false</c> (which means that <c><anno>TimerRef</anno></c> was never a
timer, that it has been cancelled, or that it has already
delivered its message).</p>
<p>See also
@@ -4257,14 +3783,11 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:ref_to_list(Ref) -> string()</name>
+ <name name="ref_to_list" arity="1"/>
<fsummary>Text representation of a reference</fsummary>
- <type>
- <v>Ref = reference()</v>
- </type>
<desc>
<p>Returns a string which corresponds to the text
- representation of <c>Ref</c>.</p>
+ representation of <c><anno>Ref</anno></c>.</p>
<warning>
<p>This BIF is intended for debugging and for use in
the Erlang operating system. It should not be used in
@@ -4273,33 +3796,25 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>register(RegName, Pid | Port) -> true</name>
+ <name name="register" arity="2"/>
<fsummary>Register a name for a pid (or port)</fsummary>
- <type>
- <v>RegName = atom()</v>
- <v>Pid = pid()</v>
- <v>Port = port()</v>
- </type>
- <desc>
- <p>Associates the name <c>RegName</c> with a pid or a port
- identifier. <c>RegName</c>, which must be an atom, can be used
+ <desc>
+ <p>Associates the name <c><anno>RegName</anno></c> with a pid or a port
+ identifier. <c><anno>RegName</anno></c>, which must be an atom, can be used
instead of the pid / port identifier in the send operator
- (<c>RegName ! Message</c>).</p>
+ (<c><anno>RegName</anno> ! Message</c>).</p>
<pre>
> <input>register(db, Pid).</input>
true</pre>
- <p>Failure: <c>badarg</c> if <c>Pid</c> is not an existing,
- local process or port, if <c>RegName</c> is already in use,
+ <p>Failure: <c>badarg</c> if <c><anno>PidOrPort</anno></c> is not an existing,
+ local process or port, if <c><anno>RegName</anno></c> is already in use,
if the process or port is already registered (already has a
- name), or if <c>RegName</c> is the atom <c>undefined</c>.</p>
+ name), or if <c><anno>RegName</anno></c> is the atom <c>undefined</c>.</p>
</desc>
</func>
<func>
- <name>registered() -> [RegName]</name>
+ <name name="registered" arity="0"/>
<fsummary>All registered names</fsummary>
- <type>
- <v>RegName = atom()</v>
- </type>
<desc>
<p>Returns a list of names which have been registered using
<seealso marker="#register/2">register/2</seealso>.</p>
@@ -4309,22 +3824,19 @@ true</pre>
</desc>
</func>
<func>
- <name>erlang:resume_process(Suspendee) -> true</name>
+ <name name="resume_process" arity="1"/>
<fsummary>Resume a suspended process</fsummary>
- <type>
- <v>Suspendee = pid()</v>
- </type>
<desc>
<p>Decreases the suspend count on the process identified by
- <c>Suspendee</c>. <c>Suspendee</c> should previously have been
+ <c><anno>Suspendee</anno></c>. <c><anno>Suspendee</anno></c> should previously have been
suspended via
<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(Suspendee)</c>. When
- the suspend count on <c>Suspendee</c> reach zero, <c>Suspendee</c>
+ by the process calling <c>erlang:resume_process(<anno>Suspendee</anno>)</c>. When
+ the suspend count on <c><anno>Suspendee</anno></c> reach zero, <c><anno>Suspendee</anno></c>
will be resumed, i.e., the state of the <c>Suspendee</c> is changed
- from suspended into the state <c>Suspendee</c> was in before it was
+ from suspended into the state <c><anno>Suspendee</anno></c> was in before it was
suspended.
</p>
<warning>
@@ -4334,29 +3846,26 @@ true</pre>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c>Suspendee</c> isn't a process identifier.
+ If <c><anno>Suspendee</anno></c> isn't 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>Suspendee</c>.
+ identified by <c><anno>Suspendee</anno></c>.
</item>
<tag><c>badarg</c></tag>
<item>
- If the process identified by <c>Suspendee</c> is not alive.
+ If the process identified by <c><anno>Suspendee</anno></c> is not alive.
</item>
</taglist>
</desc>
</func>
<func>
- <name>round(Number) -> integer()</name>
+ <name name="round" arity="1"/>
<fsummary>Return an integer by rounding a number</fsummary>
- <type>
- <v>Number = number()</v>
- </type>
<desc>
- <p>Returns an integer by rounding <c>Number</c>.</p>
+ <p>Returns an integer by rounding <c><anno>Number</anno></c>.</p>
<pre>
> <input>round(5.5).</input>
6</pre>
@@ -4364,7 +3873,7 @@ true</pre>
</desc>
</func>
<func>
- <name>self() -> pid()</name>
+ <name name="self" arity="0"/>
<fsummary>Pid of the calling process</fsummary>
<desc>
<p>Returns the pid (process identifier) of the calling process.</p>
@@ -4375,33 +3884,21 @@ true</pre>
</desc>
</func>
<func>
- <name>erlang:send(Dest, Msg) -> Msg</name>
+ <name name="send" arity="2"/>
<fsummary>Send a message</fsummary>
- <type>
- <v>Dest = pid() | port() | RegName | {RegName, Node}</v>
- <v>Msg = term()</v>
- <v>&nbsp;RegName = atom()</v>
- <v>&nbsp;Node = node()</v>
- </type>
- <desc>
- <p>Sends a message and returns <c>Msg</c>. This is the same as
- <c>Dest ! Msg</c>.</p>
- <p><c>Dest</c> may be a remote or local pid, a (local) port, a
- locally registered name, or a tuple <c>{RegName, Node}</c>
+ <type name="dst"/>
+ <desc>
+ <p>Sends a message and returns <c><anno>Msg</anno></c>. This is the same as
+ <c><anno>Dest</anno> ! <anno>Msg</anno></c>.</p>
+ <p><c><anno>Dest</anno></c> may be a remote or local pid, a (local) port, a
+ locally registered name, or a tuple <c>{<anno>RegName</anno>, <anno>Node</anno>}</c>
for a registered name at another node.</p>
</desc>
</func>
<func>
- <name>erlang:send(Dest, Msg, [Option]) -> Res</name>
+ <name name="send" arity="3"/>
+ <type name="dst"/>
<fsummary>Send a message conditionally</fsummary>
- <type>
- <v>Dest = pid() | port() | RegName | {RegName, Node}</v>
- <v>&nbsp;RegName = atom()</v>
- <v>&nbsp;Node = node()</v>
- <v>Msg = term()</v>
- <v>Option = nosuspend | noconnect</v>
- <v>Res = ok | nosuspend | noconnect</v>
- </type>
<desc>
<p>Sends a message and returns <c>ok</c>, or does not send
the message but returns something else (see below). Otherwise
@@ -4431,28 +3928,24 @@ true</pre>
</desc>
</func>
<func>
- <name>erlang:send_after(Time, Dest, Msg) -> TimerRef</name>
+ <name name="send_after" arity="3"/>
+ <type_desc variable="Time">0 &lt;= Time &lt;= 4294967295</type_desc>
<fsummary>Start a timer</fsummary>
- <type>
- <v>Time = integer() >= 0</v>
- <v>&nbsp;0 &lt;= Time &lt;= 4294967295</v>
- <v>Dest = pid() | RegName </v>
- <v>&nbsp;LocalPid = pid() (of a process, alive or dead, on the local node)</v>
- <v>Msg = term()</v>
- <v>TimerRef = reference()</v>
- </type>
<desc>
<p>Starts a timer which will send the message <c>Msg</c>
- to <c>Dest</c> after <c>Time</c> milliseconds.</p>
- <p>If <c>Dest</c> is an atom, it is supposed to be the name of
+ to <c><anno>Dest</anno></c> after <c><anno>Time</anno></c> milliseconds.</p>
+ <p>If <c><anno>Dest</anno></c> is a <c>pid()</c> it has to be a <c>pid()</c> of a local process, dead or alive.</p>
+ <p>The <c><anno>Time</anno></c> value can, in the current implementation, not be greater than 4294967295.</p>
+ <p>If <c><anno>Dest</anno></c> is an <c>atom()</c>, it is supposed to be the name of
a registered process. The process referred to by the name is
looked up at the time of delivery. No error is given if
the name does not refer to a process.</p>
- <p>If <c>Dest</c> is a pid, the timer will be automatically
- canceled if the process referred to by the pid is not alive,
+
+ <p>If <c><anno>Dest</anno></c> is a <c>pid()</c>, the timer will be automatically
+ canceled if the process referred to by the <c>pid()</c> is not alive,
or when the process exits. This feature was introduced in
erts version 5.4.11. Note that timers will not be
- automatically canceled when <c>Dest</c> is an atom.</p>
+ automatically canceled when <c><anno>Dest</anno></c> is an <c>atom</c>.</p>
<p>See also
<seealso marker="#start_timer/3">erlang:start_timer/3</seealso>,
<seealso marker="#cancel_timer/1">erlang:cancel_timer/1</seealso>,
@@ -4552,32 +4045,25 @@ true</pre>
</desc>
</func>
<func>
- <name>setelement(Index, Tuple1, Value) -> Tuple2</name>
+ <name name="setelement" arity="3"/>
+ <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno>)</type_desc>
<fsummary>Set Nth element of a tuple</fsummary>
- <type>
- <v>Index = 1..tuple_size(Tuple1)</v>
- <v>Tuple1 = Tuple2 = tuple()</v>
- <v>Value = term()</v>
- </type>
- <desc>
- <p>Returns a tuple which is a copy of the argument <c>Tuple1</c>
- with the element given by the integer argument <c>Index</c>
+ <desc>
+ <p>Returns a tuple which is a copy of the argument <c><anno>Tuple1</anno></c>
+ with the element given by the integer argument <c><anno>Index</anno></c>
(the first element is the element with index 1) replaced by
- the argument <c>Value</c>.</p>
+ the argument <c><anno>Value</anno></c>.</p>
<pre>
> <input>setelement(2, {10, green, bottles}, red).</input>
{10,red,bottles}</pre>
</desc>
</func>
<func>
- <name>size(Item) -> integer() >= 0</name>
+ <name name="size" arity="1"/>
<fsummary>Size of a tuple or binary</fsummary>
- <type>
- <v>Item = tuple() | binary()</v>
- </type>
<desc>
<p>Returns an integer which is the size of the argument
- <c>Item</c>, which must be either a tuple or a binary.</p>
+ <c><anno>Item</anno></c>, which must be either a tuple or a binary.</p>
<pre>
> <input>size({morni, mulle, bwange}).</input>
3</pre>
@@ -4605,20 +4091,16 @@ true</pre>
</desc>
</func>
<func>
- <name>spawn(Module, Function, Args) -> pid()</name>
+ <name name="spawn" arity="3"/>
<fsummary>Create a new process with a function as entry point</fsummary>
- <type>
- <v>Module = Function = atom()</v>
- <v>Args = [term()]</v>
- </type>
<desc>
<p>Returns the pid of a new process started by the application
- of <c>Module:Function</c> to <c>Args</c>. The new process
+ of <c><anno>Module</anno>:<anno>Function</anno></c> to <c><anno>Args</anno></c>. The new process
created will be placed in the system scheduler queue and be
run some time later.</p>
- <p><c>error_handler:undefined_function(Module, Function, Args)</c> is evaluated by the new process if
- <c>Module:Function/Arity</c> does not exist (where
- <c>Arity</c> is the length of <c>Args</c>). The error handler
+ <p><c>error_handler:undefined_function(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> is evaluated by the new process if
+ <c><anno>Module</anno>:<anno>Function</anno>/Arity</c> 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>).
If <c>error_handler</c> is undefined, or the user has
@@ -4665,15 +4147,11 @@ true</pre>
</desc>
</func>
<func>
- <name>spawn_link(Module, Function, Args) -> pid()</name>
+ <name name="spawn_link" arity="3"/>
<fsummary>Create and link to a new process with a function as entry point</fsummary>
- <type>
- <v>Module = Function = atom()</v>
- <v>Args = [term()]</v>
- </type>
<desc>
<p>Returns the pid of a new process started by the application
- of <c>Module:Function</c> to <c>Args</c>. A link is created
+ 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>
@@ -4852,15 +4330,12 @@ true</pre>
</desc>
</func>
<func>
- <name>split_binary(Bin, Pos) -> {Bin1, Bin2}</name>
+ <name name="split_binary" arity="2"/>
+ <type_desc variable="Pos">0..byte_size(Bin)</type_desc>
<fsummary>Split a binary into two</fsummary>
- <type>
- <v>Bin = Bin1 = Bin2 = binary()</v>
- <v>Pos = 0..byte_size(Bin)</v>
- </type>
<desc>
<p>Returns a tuple containing the binaries which are the result
- of splitting <c>Bin</c> into two parts at position <c>Pos</c>.
+ 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 will be three binaries altogether.</p>
<pre>
@@ -4877,30 +4352,24 @@ true</pre>
</desc>
</func>
<func>
- <name>erlang:start_timer(Time, Dest, Msg) -> TimerRef</name>
+ <name name="start_timer" arity="3"/>
+ <type_desc variable="Time">0 &lt;= Time &lt;= 4294967295</type_desc>
<fsummary>Start a timer</fsummary>
- <type>
- <v>Time = integer() >= 0</v>
- <v>&nbsp;0 &lt;= Time &lt;= 4294967295</v>
- <v>Dest = LocalPid | RegName </v>
- <v>&nbsp;LocalPid = pid() (of a process, alive or dead, on the local node)</v>
- <v>&nbsp;RegName = atom()</v>
- <v>Msg = term()</v>
- <v>TimerRef = reference()</v>
- </type>
<desc>
<p>Starts a timer which will send the message
- <c>{timeout, TimerRef, Msg}</c> to <c>Dest</c>
- after <c>Time</c> milliseconds.</p>
- <p>If <c>Dest</c> is an atom, it is supposed to be the name of
+ <c>{timeout, <anno>TimerRef</anno>, <anno>Msg</anno>}</c> to <c><anno>Dest</anno></c>
+ after <c><anno>Time</anno></c> milliseconds.</p>
+ <p>If <c><anno>Dest</anno></c> is a <c>pid()</c> it has to be a <c>pid()</c> of a local process, dead or alive.</p>
+ <p>The <c><anno>Time</anno></c> value can, in the current implementation, not be greater than 4294967295.</p>
+ <p>If <c><anno>Dest</anno></c> is an <c>atom()</c>, it is supposed to be the name of
a registered process. The process referred to by the name is
looked up at the time of delivery. No error is given if
the name does not refer to a process.</p>
- <p>If <c>Dest</c> is a pid, the timer will be automatically
- canceled if the process referred to by the pid is not alive,
+ <p>If <c><anno>Dest</anno></c> is a <c>pid()</c>, the timer will be automatically
+ canceled if the process referred to by the <c>pid()</c> is not alive,
or when the process exits. This feature was introduced in
erts version 5.4.11. Note that timers will not be
- automatically canceled when <c>Dest</c> is an atom.</p>
+ automatically canceled when <c><anno>Dest</anno></c> is an <c>atom()</c>.</p>
<p>See also
<seealso marker="#send_after/3">erlang:send_after/3</seealso>,
<seealso marker="#cancel_timer/1">erlang:cancel_timer/1</seealso>,
@@ -4911,54 +4380,52 @@ true</pre>
</desc>
</func>
<func>
- <name>statistics(Type) -> Res</name>
- <fsummary>Information about the system</fsummary>
- <type>
- <v>Type, Res -- see below</v>
- </type>
+ <name name="statistics" arity="1" clause_i="1"/>
+ <fsummary>Information about context switches</fsummary>
<desc>
- <p>All times are in milliseconds unless otherwise specified.</p>
- <p>Returns information about the system as specified by
- <c>Type</c>:</p>
- <taglist>
- <tag><c>context_switches</c></tag>
- <item>
- <p>Returns <c>{ContextSwitches, 0}</c>, where
- <c>ContextSwitches</c> is the total number of context
- switches since the system started.</p>
- </item>
- <tag><marker id="statistics_exact_reductions"><c>exact_reductions</c></marker></tag>
- <item>
- <p>Returns
- <c>{Total_Exact_Reductions, Exact_Reductions_Since_Last_Call}</c>.</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>
- </item>
- <tag><c>garbage_collection</c></tag>
- <item>
- <p>Returns <c>{Number_of_GCs, Words_Reclaimed, 0}</c>. This
- information may not be valid for all implementations.</p>
- <pre>
+ <p><c><anno>ContextSwitches</anno></c> is the total number of context
+ switches since the system started.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="statistics" arity="1" clause_i="2"/>
+ <fsummary>Information about exact reductions</fsummary>
+ <desc>
+ <marker id="statistics_exact_reductions"></marker>
+ <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>
+ <func>
+ <name name="statistics" arity="1" clause_i="3"/>
+ <fsummary>Information about garbage collection</fsummary>
+ <desc>
+ <p>This information may not be valid for all implementations.</p>
+ <pre>
> <input>statistics(garbage_collection).</input>
{85,23961,0}
</pre>
- </item>
- <tag><c>io</c></tag>
- <item>
- <p>Returns <c>{{input, Input}, {output, Output}}</c>,
- where <c>Input</c> is the total number of bytes received
- through ports, and <c>Output</c> is the total number of
- bytes output to ports.</p>
- </item>
- <tag><marker id="statistics_reductions"><c>reductions</c></marker></tag>
- <item>
- <p>Returns
- <c>{Total_Reductions, Reductions_Since_Last_Call}</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="statistics" arity="1" clause_i="4"/>
+ <fsummary>Information about io</fsummary>
+ <desc>
+ <p><c><anno>Input</anno></c> is the total number of bytes received
+ through ports, and <c><anno>Output</anno></c> is the total number of
+ bytes output to ports.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="statistics" arity="1" clause_i="5"/>
+ <fsummary>Information about reductions</fsummary>
+ <desc>
+ <marker id="statistics_reductions"></marker>
<note>
- <p>From erts version 5.5 (OTP release R11B)
+ <p>Since erts-5.5 (OTP release R11B)
this value does not include reductions performed in current
time slices of currently scheduled processes. If an
exact value is wanted, use
@@ -4968,53 +4435,65 @@ true</pre>
> <input>statistics(reductions).</input>
{2046,11}
</pre>
- </item>
- <tag><c>run_queue</c></tag>
- <item>
- <p>Returns the length of the run queue, that is, the number
- of processes that are ready to run.</p>
- </item>
- <tag><c>runtime</c></tag>
- <item>
- <p>Returns <c>{Total_Run_Time, Time_Since_Last_Call}</c>.
- Note that the run-time is the sum of the run-time for all
- threads in the Erlang run-time system and may therefore be greater
- than the wall-clock time.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="statistics" arity="1" clause_i="6"/>
+ <fsummary>Information about the run-queue</fsummary>
+ <desc>
+ <p>Returns the length of the run queue, that is, the number
+ of processes that are ready to run.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="statistics" arity="1" clause_i="7"/>
+ <fsummary>Information about run-time</fsummary>
+ <desc>
+ <p>Note that the run-time is the sum of the run-time for all
+ threads in the Erlang run-time system and may therefore be greater
+ than the wall-clock time.</p>
<pre>
> <input>statistics(runtime).</input>
{1690,1620}
</pre>
- </item>
- <tag><marker id="statistics_scheduler_wall_time"><c>scheduler_wall_time</c></marker></tag>
- <item>
- <p>Returns a list of tuples with
- <c>{SchedulerId, ActiveTime, TotalTime}</c>, where <c>SchedulerId</c> is an integer id of the scheduler, <c>ActiveTime</c> is
- the duration the scheduler has been busy, <c>TotalTime</c> is the total time duration since
- <seealso marker="#system_flag_scheduler_wall_time">scheduler_wall_time</seealso>
- activation. The time unit is not defined and may be subject to change
- between releases, operating systems and system restarts.
- <c>scheduler_wall_time</c> should only be used to calculate relative
- values for scheduler-utilization. <c>ActiveTime</c> can never exceed <c>TotalTime</c>.
- </p>
+ </desc>
+ </func>
+ <func>
+ <name name="statistics" arity="1" clause_i="8"/>
+ <fsummary>Information about each schedulers work time</fsummary>
+ <desc>
+ <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
+ <c>SchedulerId</c> is an integer id of the scheduler, <c>ActiveTime</c> is
+ the duration the scheduler has been busy, <c>TotalTime</c> is the total time duration since
+ <seealso marker="#system_flag_scheduler_wall_time">scheduler_wall_time</seealso>
+ activation. The time unit is not defined and may be subject to change
+ between releases, operating systems and system restarts.
+ <c>scheduler_wall_time</c> should only be used to calculate relative
+ values for scheduler-utilization. <c>ActiveTime</c> can never exceed <c>TotalTime</c>.
+ </p>
- <p>The definition of a busy scheduler is when it is not idle or not
- scheduling (selecting) a process or port, meaning; executing process
- code, executing linked-in-driver or NIF code, executing
- built-in-functions or any other runtime handling, garbage collecting
- or handling any other memory management. Note, a scheduler may also be
- busy even if the operating system has scheduled out the scheduler
- thread.
- </p>
+ <p>The definition of a busy scheduler is when it is not idle or not
+ scheduling (selecting) a process or port, meaning; executing process
+ code, executing linked-in-driver or NIF code, executing
+ built-in-functions or any other runtime handling, garbage collecting
+ or handling any other memory management. Note, a scheduler may also be
+ busy even if the operating system has scheduled out the scheduler
+ thread.
+ </p>
- <p>
- Returns <c>undefined</c> if the system flag <seealso marker="#system_flag_scheduler_wall_time">
- scheduler_wall_time</seealso> is turned off.
- </p>
+ <p>
+ Returns <c>undefined</c> if the system flag
+ <seealso marker="#system_flag_scheduler_wall_time">scheduler_wall_time</seealso>
+ is turned off.
+ </p>
- <p>The list of scheduler information is unsorted and may appear in different order
- between calls.
- </p>
- <p>Using <c>scheduler_wall_time</c> to calculate scheduler utilization.</p>
+ <p>The list of scheduler information is unsorted and may appear in different order
+ between calls.
+ </p>
+ <p>Using <c>scheduler_wall_time</c> to calculate scheduler utilization.</p>
<pre>
> <input>erlang:system_flag(scheduler_wall_time, true).</input>
false
@@ -5042,34 +4521,26 @@ ok
{Ai + (A1 - A0), Ti + (T1 - T0)} end, {0, 0}, lists:zip(Ts0,Ts1)), A/T.</input>
0.9769136803764825
</pre>
-
<note>
<p><c>scheduler_wall_time</c> is by default disabled. Use <c>erlang:system_flag(scheduler_wall_time, true)</c> to enable it. </p>
</note>
- </item>
-
- <tag><c>wall_clock</c></tag>
- <item>
- <p>Returns
- <c>{Total_Wallclock_Time, Wallclock_Time_Since_Last_Call}</c>.
- <c>wall_clock</c> can be used in the same manner as
- <c>runtime</c>, except that real time is measured as
- opposed to runtime or CPU time.</p>
- </item>
- </taglist>
</desc>
</func>
<func>
- <name>erlang:suspend_process(Suspendee, OptList) -> boolean()</name>
+ <name name="statistics" arity="1" clause_i="9"/>
+ <fsummary>Information about wall-clock</fsummary>
+ <desc>
+ <p><c>wall_clock</c> can be used in the same manner as
+ <c>runtime</c>, except that real time is measured as
+ opposed to runtime or CPU time.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="suspend_process" arity="2"/>
<fsummary>Suspend a process</fsummary>
- <type>
- <v>Suspendee = pid()</v>
- <v>OptList = [Opt]</v>
- <v>Opt = atom()</v>
- </type>
<desc>
<p>Increases the suspend count on the process identified by
- <c>Suspendee</c> and puts it in the suspended state if it isn't
+ <c><anno>Suspendee</anno></c> and puts it in the suspended state if it isn't
already in the suspended state. A suspended process will not be
scheduled for execution until the process has been resumed.
</p>
@@ -5077,49 +4548,49 @@ ok
<p>A process can be suspended by multiple processes and can
be suspended multiple times by a single process. A suspended
process will not leave the suspended state until its suspend
- count reach zero. The suspend count of <c>Suspendee</c> is
- decreased when
- <seealso marker="#resume_process/1">erlang:resume_process(Suspendee)</seealso>
+ count reach 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(Suspendee)</c>. All increased suspend
+ <c>erlang:suspend_process(<anno>Suspendee</anno>)</c>. All increased suspend
counts on other processes acquired by a process will automatically be
decreased when the process terminates.</p>
- <p>Currently the following options (<c>Opt</c>s) are available:</p>
+ <p>Currently the following options (<c><anno>Opt</anno></c>s) are available:</p>
<taglist>
<tag><c>asynchronous</c></tag>
<item>
A suspend request is sent to the process identified by
- <c>Suspendee</c>. <c>Suspendee</c> will eventually suspend
+ <c><anno>Suspendee</anno></c>. <c><anno>Suspendee</anno></c> will eventually suspend
unless it is resumed before it was able to suspend. The caller
of <c>erlang:suspend_process/2</c> will return immediately,
- regardless of whether the <c>Suspendee</c> has suspended yet
- or not. Note that the point in time when the <c>Suspendee</c>
+ regardless of whether the <c><anno>Suspendee</anno></c> has suspended yet
+ or not. Note that the point in time when the <c><anno>Suspendee</anno></c>
will actually suspend cannot be deduced from other events
in the system. The only guarantee given is that the
- <c>Suspendee</c> will <em>eventually</em> suspend (unless it
+ <c><anno>Suspendee</anno></c> will <em>eventually</em> suspend (unless it
is resumed). If the <c>asynchronous</c> option has <em>not</em>
been passed, the caller of <c>erlang:suspend_process/2</c> will
- be blocked until the <c>Suspendee</c> has actually suspended.
+ be blocked until the <c><anno>Suspendee</anno></c> has actually suspended.
</item>
<tag><c>unless_suspending</c></tag>
<item>
- The process identified by <c>Suspendee</c> will be suspended
+ The process identified by <c><anno>Suspendee</anno></c> will be suspended
unless the calling process already is suspending the
- <c>Suspendee</c>. If <c>unless_suspending</c> is combined
+ <c><anno>Suspendee</anno></c>. If <c>unless_suspending</c> is combined
with the <c>asynchronous</c> option, a suspend request will be
sent unless the calling process already is suspending the
- <c>Suspendee</c> or if a suspend request already has been sent
+ <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
- the <c>Suspendee</c>, or if combined with the <c>asynchronous</c>
+ the <c><anno>Suspendee</anno></c>, or if combined with the <c>asynchronous</c>
option and a send request already is in transit,
- <c>false</c> is returned and the suspend count on <c>Suspendee</c>
+ <c>false</c> is returned and the suspend count on <c><anno>Suspendee</anno></c>
will remain unchanged.
</item>
</taglist>
<p>If the suspend count on the process identified by
- <c>Suspendee</c> was increased, <c>true</c> is returned; otherwise,
+ <c><anno>Suspendee</anno></c> was increased, <c>true</c> is returned; otherwise,
<c>false</c> is returned.</p>
<warning>
@@ -5129,28 +4600,28 @@ ok
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c>Suspendee</c> isn't a process identifier.
+ If <c><anno>Suspendee</anno></c> isn't a process identifier.
</item>
<tag><c>badarg</c></tag>
<item>
- If the process identified by <c>Suspendee</c> is same the process as
+ If the process identified by <c><anno>Suspendee</anno></c> is same the process as
the process calling <c>erlang:suspend_process/2</c>.
</item>
<tag><c>badarg</c></tag>
<item>
- If the process identified by <c>Suspendee</c> is not alive.
+ 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>Suspendee</c> resides on another node.
+ If the process identified by <c><anno>Suspendee</anno></c> resides on another node.
</item>
<tag><c>badarg</c></tag>
<item>
- If <c>OptList</c> isn't a proper list of valid <c>Opt</c>s.
+ If <c><anno>OptList</anno></c> isn't 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>Suspendee</c> has been suspended more
+ 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 current system limit
is larger than 2 000 000 000 suspends, and it will never be less
@@ -5173,292 +4644,322 @@ ok
</desc>
</func>
<func>
- <name>erlang:system_flag(Flag, Value) -> OldValue</name>
- <fsummary>Set system flags</fsummary>
- <type>
- <v>Flag, Value, OldValue -- see below</v>
- </type>
+ <name name="system_flag" arity="2" clause_i="1"/>
+ <fsummary>Set system flag backtrace_depth</fsummary>
+ <desc>
+ <p>Sets the maximum depth of call stack back-traces in the
+ exit reason element of <c>'EXIT'</c> tuples.</p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="system_flag" arity="2" clause_i="2"/>
+ <type name="cpu_topology"/>
+ <type name="level_entry"/>
+ <type name="level_tag"/>
+ <type name="sub_level"/>
+ <type name="info_list"/>
+ <fsummary>Set system flag cpu_topology</fsummary>
+ <desc>
+ <warning>
+ <p><marker id="system_flag_cpu_topology"></marker>
+ This argument is <em>deprecated</em> and
+ scheduled for removal in erts-5.10/OTP-R16. Instead of using
+ this argument you are advised to use the <c>erl</c> command
+ line argument <seealso marker="erts:erl#+sct">+sct</seealso>.
+ When this argument has been removed a final CPU topology to use
+ will be determined at emulator boot time.</p>
+ </warning>
+ <p>Sets the user defined <c><anno>CpuTopology</anno></c>. The user defined
+ CPU topology will override any automatically detected
+ CPU topology. By passing <c>undefined</c> as <c><anno>CpuTopology</anno></c>
+ the system will revert back 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 will be sent a request
+ to rebind according to the new CPU topology.
+ </p>
+ <p>The user defined CPU topology can also be set by passing
+ the <seealso marker="erts:erl#+sct">+sct</seealso> command
+ line argument to <c>erl</c>.
+ </p>
+ <p>For information on the <c><anno>CpuTopology</anno></c> type
+ and more, see the documentation of
+ <seealso marker="#system_info_cpu_topology">erlang:system_info(cpu_topology)</seealso>,
+ and the <c>erl</c> <seealso marker="erts:erl#+sct">+sct</seealso>
+ and <seealso marker="erts:erl#+sbt">+sbt</seealso>
+ command line flags.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name name="system_flag" arity="2" clause_i="3"/>
+ <fsummary>Set system flag fullsweep_after</fsummary>
+ <desc>
+ <p><c><anno>Number</anno></c> is a non-negative integer which indicates
+ how many times generational garbage collections can be
+ done without forcing a fullsweep collection. The value
+ applies to new processes; processes already running are
+ not affected.</p>
+ <p>Returns the old value of the flag.</p>
+ <p>In low-memory systems (especially without virtual
+ memory), setting the value to 0 can help to conserve
+ memory.</p>
+ <p>An alternative way to set this value is through the
+ (operating system) environment variable
+ <c>ERL_FULLSWEEP_AFTER</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="system_flag" arity="2" clause_i="4"/>
+ <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> only
+ effects processes spawned after the change of
+ <c>min_heap_size</c> has been made.
+ The <c>min_heap_size</c> can be set for individual
+ processes by use of
+ <seealso marker="#spawn_opt/4">spawn_opt/N</seealso> or
+ <seealso marker="#process_flag/2">process_flag/2</seealso>. </p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="system_flag" arity="2" clause_i="5"/>
+ <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. The new <c>min_bin_vhheap_size</c> only
+ effects processes spawned after the change of
+ <c>min_bin_vhheap_size</c> has been made.
+ The <c>min_bin_vheap_size</c> can be set for individual
+ processes by use of
+ <seealso marker="#spawn_opt/4">spawn_opt/N</seealso> or
+ <seealso marker="#process_flag/2">process_flag/2</seealso>. </p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="system_flag" arity="2" clause_i="6"/>
+ <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 has been blocked, only
+ one scheduler thread will schedule Erlang processes.</p>
+ <p>If <c><anno>BlockState</anno> =:= block</c>, multi-scheduling will
+ be blocked. If <c><anno>BlockState</anno> =:= unblock</c> and no-one
+ else is blocking multi-scheduling and this process has
+ only blocked one time, multi-scheduling will be unblocked.
+ One process can block multi-scheduling multiple times.
+ If a process has blocked multiple times, it has to
+ 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 will release its
+ blocking of 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>erlang:system_flag(multi_scheduling, <anno>BlockState</anno>)</c>
+ has been made. The return values are described in the
+ documentation of <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>.</p>
+ <p><em>NOTE</em>: Blocking of multi-scheduling should normally
+ not be needed. If you feel that you need to
+ block multi-scheduling, think through the
+ problem at least a couple of times again.
+ Blocking multi-scheduling should only be used
+ as a last resort since it will most likely be
+ a <em>very inefficient</em> way to solve the
+ problem.</p>
+ <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>
+ </desc>
+ </func>
+ <func>
+ <name name="system_flag" arity="2" clause_i="7"/>
+ <type name="scheduler_bind_type"/>
+ <fsummary>Set system flag scheduler_bind_type</fsummary>
<desc>
<warning>
- <p>The
- <seealso marker="#system_flag_cpu_topology">cpu_topology</seealso>,
- and
- <seealso marker="#system_flag_scheduler_bind_type">scheduler_bind_type</seealso>
- <c>Flag</c>s are <em>deprecated</em> and have been scheduled for
- removal in erts-5.10/OTP-R16.</p>
+ <p><marker id="system_flag_scheduler_bind_type"></marker>
+ This argument is <em>deprecated</em> and
+ scheduled for removal in erts-5.10/OTP-R16. Instead of using
+ this argument you are advised to use the <c>erl</c> command
+ line argument <seealso marker="erts:erl#+sbt">+sbt</seealso>.
+ When this argument has been removed a final scheduler bind type
+ to use will be determined at emulator boot time.</p>
</warning>
- <p>Sets various system properties of the Erlang node. Returns
- the old value of the flag.</p>
+ <p>Controls if and how schedulers are bound to logical
+ 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 which causes them to try to bind or unbind as requested.
+ <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="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>.
+ </p>
+ <p>Schedulers can currently only be bound on newer Linux,
+ Solaris, FreeBSD, and Windows systems, but more systems will be
+ supported in the future.
+ </p>
+ <p>In order for the runtime system to be able to bind schedulers,
+ the CPU topology needs to be known. If the runtime system fails
+ to automatically detect the CPU topology, it can be defined.
+ For more information on how to define the CPU topology, see
+ the <c>erl</c> <seealso marker="erts:erl#+sct">+sct</seealso> command
+ line flag.
+ </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>
+ <p>Schedulers can be bound in different ways. The <c><anno>How</anno></c>
+ argument determines how schedulers are bound. <c><anno>How</anno></c> can
+ currently be one of:</p>
<taglist>
- <tag><c>erlang:system_flag(backtrace_depth, Depth)</c></tag>
- <item>
- <p>Sets the maximum depth of call stack back-traces in the
- exit reason element of <c>'EXIT'</c> tuples.</p>
- </item>
- <tag><marker id="system_flag_cpu_topology"><c>erlang:system_flag(cpu_topology, CpuTopology)</c></marker></tag>
- <item>
- <p><em>NOTE:</em> This argument is <em>deprecated</em> and
- scheduled for removal in erts-5.10/OTP-R16. Instead of using
- this argument you are advised to use the <c>erl</c> command
- line argument <seealso marker="erts:erl#+sct">+sct</seealso>.
- When this argument has been removed a final CPU topology to use
- will be determined at emulator boot time.</p>
- <p>Sets the user defined <c>CpuTopology</c>. The user defined
- CPU topology will override any automatically detected
- CPU topology. By passing <c>undefined</c> as <c>CpuTopology</c>
- the system will revert back 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>The CPU topology is used when binding schedulers to logical
- processors. If schedulers are already bound when the CPU
- topology is changed, the schedulers will be sent a request
- to rebind according to the new CPU topology.
- </p>
- <p>The user defined CPU topology can also be set by passing
- the <seealso marker="erts:erl#+sct">+sct</seealso> command
- line argument to <c>erl</c>.
- </p>
- <p>For information on the <c>CpuTopology</c> type
- and more, see the documentation of
- <seealso marker="#system_info_cpu_topology">erlang:system_info(cpu_topology)</seealso>,
- and the <c>erl</c> <seealso marker="erts:erl#+sct">+sct</seealso>
- and <seealso marker="erts:erl#+sbt">+sbt</seealso>
- command line flags.
- </p>
- </item>
- <tag><c>erlang:system_flag(fullsweep_after, Number)</c></tag>
- <item>
- <p><c>Number</c> is a non-negative integer which indicates
- how many times generational garbage collections can be
- done without forcing a fullsweep collection. The value
- applies to new processes; processes already running are
- not affected.</p>
- <p>In low-memory systems (especially without virtual
- memory), setting the value to 0 can help to conserve
- memory.</p>
- <p>An alternative way to set this value is through the
- (operating system) environment variable
- <c>ERL_FULLSWEEP_AFTER</c>.</p>
- </item>
- <tag><c>erlang:system_flag(min_heap_size, MinHeapSize)</c></tag>
- <item>
- <p>Sets the default minimum heap size for processes. The
- size is given in words. The new <c>min_heap_size</c> only
- effects processes spawned after the change of
- <c>min_heap_size</c> has been made.
- The <c>min_heap_size</c> can be set for individual
- processes by use of
- <seealso marker="#spawn_opt/4">spawn_opt/N</seealso> or
- <seealso marker="#process_flag/2">process_flag/2</seealso>. </p>
- </item>
- <tag><c>erlang:system_flag(min_bin_vheap_size, MinBinVHeapSize)</c></tag>
- <item>
- <p>Sets the default minimum binary virtual heap size for processes. The
- size is given in words. The new <c>min_bin_vhheap_size</c> only
- effects processes spawned after the change of
- <c>min_bin_vhheap_size</c> has been made.
- The <c>min_bin_vheap_size</c> can be set for individual
- processes by use of
- <seealso marker="#spawn_opt/4">spawn_opt/N</seealso> or
- <seealso marker="#process_flag/2">process_flag/2</seealso>. </p>
- </item>
- <tag><marker id="system_flag_multi_scheduling"><c>erlang:system_flag(multi_scheduling, BlockState)</c></marker></tag>
- <item>
- <p><c>BlockState = block | unblock</c></p>
- <p>If multi-scheduling is enabled, more than one scheduler
- thread is used by the emulator. Multi-scheduling can be
- blocked. When multi-scheduling has been blocked, only
- one scheduler thread will schedule Erlang processes.</p>
- <p>If <c>BlockState =:= block</c>, multi-scheduling will
- be blocked. If <c>BlockState =:= unblock</c> and no-one
- else is blocking multi-scheduling and this process has
- only blocked one time, multi-scheduling will be unblocked.
- One process can block multi-scheduling multiple times.
- If a process has blocked multiple times, it has to
- 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 will release its
- blocking of 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>erlang:system_flag(multi_scheduling, BlockState)</c>
- has been made. The return values are described in the
- documentation of <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>.</p>
- <p><em>NOTE</em>: Blocking of multi-scheduling should normally
- not be needed. If you feel that you need to
- block multi-scheduling, think through the
- problem at least a couple of times again.
- Blocking multi-scheduling should only be used
- as a last resort since it will most likely be
- a <em>very inefficient</em> way to solve the
- problem.</p>
- <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>
- </item>
- <tag><marker id="system_flag_scheduler_bind_type"><c>erlang:system_flag(scheduler_bind_type, How)</c></marker></tag>
- <item>
- <p><em>NOTE:</em> This argument is <em>deprecated</em> and
- scheduled for removal in erts-5.10/OTP-R16. Instead of using
- this argument you are advised to use the <c>erl</c> command
- line argument <seealso marker="erts:erl#+sbt">+sbt</seealso>.
- When this argument has been removed a final scheduler bind type
- to use will be determined at emulator boot time.</p>
- <p>Controls if and how schedulers are bound to logical
- processors.</p>
- <p>When <c>erlang:system_flag(scheduler_bind_type, How)</c> is
- called, an asynchronous signal is sent to all schedulers
- online which causes them to try to bind or unbind as requested.
- <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="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>.
- </p>
- <p>Schedulers can currently only be bound on newer Linux,
- Solaris, FreeBSD, and Windows systems, but more systems will be
- supported in the future.
- </p>
- <p>In order for the runtime system to be able to bind schedulers,
- the CPU topology needs to be known. If the runtime system fails
- to automatically detect the CPU topology, it can be defined.
- For more information on how to define the CPU topology, see
- the <c>erl</c> <seealso marker="erts:erl#+sct">+sct</seealso> command
- line flag.
- </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>
- <p>Schedulers can be bound in different ways. The <c>How</c>
- argument determines how schedulers are bound. <c>How</c> can
- currently be one of:</p>
- <taglist>
- <tag><c>unbound</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt u</seealso>.
- </p></item>
- <tag><c>no_spread</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt ns</seealso>.
- </p></item>
- <tag><c>thread_spread</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt ts</seealso>.
- </p></item>
- <tag><c>processor_spread</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt ps</seealso>.
- </p></item>
- <tag><c>spread</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt s</seealso>.
- </p></item>
- <tag><c>no_node_thread_spread</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt nnts</seealso>.
- </p></item>
- <tag><c>no_node_processor_spread</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt nnps</seealso>.
- </p></item>
- <tag><c>thread_no_node_processor_spread</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt tnnps</seealso>.
- </p></item>
- <tag><c>default_bind</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt db</seealso>.
- </p></item>
- </taglist>
- <p>The value returned equals <c>How</c> before the
- <c>scheduler_bind_type</c> flag was changed.</p>
- <p>Failure:</p>
- <taglist>
- <tag><c>notsup</c></tag>
- <item>
- <p>If binding of schedulers is not supported.</p>
- </item>
- <tag><c>badarg</c></tag>
- <item>
- <p>If <c>How</c> isn't one of the documented alternatives.</p>
- </item>
- <tag><c>badarg</c></tag>
- <item>
- <p>If no CPU topology information is available.</p>
- </item>
- </taglist>
- <p>The scheduler bind type can also be set by passing
- the <seealso marker="erts:erl#+sbt">+sbt</seealso> command
- line argument to <c>erl</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>,
- the <c>erl</c> <seealso marker="erts:erl#+sbt">+sbt</seealso>
- and <seealso marker="erts:erl#+sct">+sct</seealso> command line
- flags.
- </p>
- </item>
- <tag><marker id="system_flag_scheduler_wall_time"><c>erlang:system_flag(scheduler_wall_time, Boolean)</c></marker></tag>
+ <tag><c>unbound</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt u</seealso>.
+ </p></item>
+ <tag><c>no_spread</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt ns</seealso>.
+ </p></item>
+ <tag><c>thread_spread</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt ts</seealso>.
+ </p></item>
+ <tag><c>processor_spread</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt ps</seealso>.
+ </p></item>
+ <tag><c>spread</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt s</seealso>.
+ </p></item>
+ <tag><c>no_node_thread_spread</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt nnts</seealso>.
+ </p></item>
+ <tag><c>no_node_processor_spread</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt nnps</seealso>.
+ </p></item>
+ <tag><c>thread_no_node_processor_spread</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt tnnps</seealso>.
+ </p></item>
+ <tag><c>default_bind</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt db</seealso>.
+ </p></item>
+ </taglist>
+ <p>The value returned equals <c><anno>How</anno></c> before the
+ <c>scheduler_bind_type</c> flag was changed.</p>
+ <p>Failure:</p>
+ <taglist>
+ <tag><c>notsup</c></tag>
<item>
- <p>Turns on/off scheduler wall time measurements. </p>
- <p>For more information see,
- <seealso marker="#statistics_scheduler_wall_time">erlang:statistics(scheduler_wall_time)</seealso>.
- </p>
+ <p>If binding of schedulers is not supported.</p>
</item>
-
- <tag><marker id="system_flag_schedulers_online"><c>erlang:system_flag(schedulers_online, SchedulersOnline)</c></marker></tag>
+ <tag><c>badarg</c></tag>
<item>
- <p>Sets the amount of schedulers online. Valid range is
- <![CDATA[1 <= SchedulerId <= erlang:system_info(schedulers)]]>.
- </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>
+ <p>If <c>How</c> isn't one of the documented alternatives.</p>
</item>
-
- <tag><c>erlang:system_flag(trace_control_word, TCW)</c></tag>
+ <tag><c>badarg</c></tag>
<item>
- <p>Sets the value of the node's trace control word to
- <c>TCW</c>. <c>TCW</c> should be an unsigned integer. For
- more information see documentation of the
- <seealso marker="erts:match_spec#set_tcw">set_tcw</seealso>
- function in the match specification documentation in the
- ERTS User's Guide.</p>
+ <p>If no CPU topology information is available.</p>
</item>
</taglist>
- <note>
- <p>The <c>schedulers</c> option has been removed as
- of erts version 5.5.3. The number of scheduler
- threads is determined at emulator boot time, and
- cannot be changed after that.</p>
- </note>
+ <p>The scheduler bind type can also be set by passing
+ the <seealso marker="erts:erl#+sbt">+sbt</seealso> command
+ line argument to <c>erl</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>,
+ the <c>erl</c> <seealso marker="erts:erl#+sbt">+sbt</seealso>
+ and <seealso marker="erts:erl#+sct">+sct</seealso> command line
+ flags.
+ </p>
</desc>
</func>
<func>
- <name>erlang:system_info(Type) -> Res</name>
- <fsummary>Information about the system</fsummary>
- <type>
- <v>Type, Res -- see below</v>
- </type>
+ <name name="system_flag" arity="2" clause_i="8"/>
+ <fsummary>Set system flag scheduler_wall_time</fsummary>
+ <desc><p><marker id="system_flag_scheduler_wall_time"></marker>
+ Turns on/off scheduler wall time measurements. </p>
+ <p>For more information see,
+ <seealso marker="#statistics_scheduler_wall_time">erlang:statistics(scheduler_wall_time)</seealso>.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name name="system_flag" arity="2" clause_i="9"/>
+ <fsummary>Set system flag schedulers_online</fsummary>
<desc>
- <p>Returns various information about the current system
- (emulator) as specified by <c>Type</c>:</p>
+ <p><marker id="system_flag_schedulers_online"></marker>
+ Sets the amount of schedulers online. Valid range is
+ <![CDATA[1 <= SchedulersOnline <= erlang:system_info(schedulers)]]>.
+ </p>
+ <p>Returns the old value of the flag.</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>
+ </desc>
+ </func>
+ <func>
+ <name name="system_flag" arity="2" clause_i="10"/>
+ <fsummary>Set system flag trace_control_word</fsummary>
+ <desc>
+ <p>Sets the value of the node's trace control word to
+ <c><anno>TCW</anno></c>. <c><anno>TCW</anno></c> should be an unsigned integer. For
+ more information see documentation of the
+ <seealso marker="erts:match_spec#set_tcw">set_tcw</seealso>
+ function in the match specification documentation in the
+ ERTS User's Guide.</p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="system_info" arity="1" clause_i="1"/>
+ <name name="system_info" arity="1" clause_i="2"/>
+ <name name="system_info" arity="1" clause_i="3"/>
+ <name name="system_info" arity="1" clause_i="4"/>
+ <name name="system_info" arity="1" clause_i="5"/>
+ <type variable="Allocator" name_i="2"/>
+ <type variable="Version" name_i="2"/>
+ <type variable="Features" name_i="2"/>
+ <type variable="Settings" name_i="2"/>
+ <type variable="Alloc" name_i="3"/>
+ <fsummary>Information about the allocators of the system</fsummary>
+ <desc>
+ <p>
+ Returns various information about the
+ <marker id="system_info_allocator_tags">allocators</marker> of the
+ current system (emulator) as specified by
+ <c><anno>Item</anno></c>:</p>
<taglist>
<tag><marker id="system_info_allocated_areas"><c>allocated_areas</c></marker></tag>
<item>
@@ -5483,37 +4984,27 @@ ok
</item>
<tag><marker id="system_info_allocator"><c>allocator</c></marker></tag>
<item>
- <p>Returns <c>{Allocator, Version, Features, Settings}.</c></p>
- <p>Types:</p>
- <list type="bulleted">
- <item><c>Allocator = undefined | glibc</c></item>
- <item><c>Version = [integer()]</c></item>
- <item><c>Features = [atom()]</c></item>
- <item><c>Settings = [{Subsystem, [{Parameter, Value}]}]</c></item>
- <item><c>Subsystem = atom()</c></item>
- <item><c>Parameter = atom()</c></item>
- <item><c>Value = term()</c></item>
- </list>
+ <p>Returns <c>{<anno>Allocator</anno>, <anno>Version</anno>, <anno>Features</anno>, <anno>Settings</anno>}.</c></p>
<p>Explanation:</p>
<list type="bulleted">
<item>
- <p><c>Allocator</c> corresponds to the <c>malloc()</c>
- implementation used. If <c>Allocator</c> equals
+ <p><c><anno>Allocator</anno></c> corresponds to the <c>malloc()</c>
+ implementation used. If <c><anno>Allocator</anno></c> equals
<c>undefined</c>, the <c>malloc()</c> implementation
used could not be identified. Currently
<c>glibc</c> can be identified.</p>
</item>
<item>
- <p><c>Version</c> is a list of integers (but not a
+ <p><c><anno>Version</anno></c> is a list of integers (but not a
string) representing the version of
the <c>malloc()</c> implementation used.</p>
</item>
<item>
- <p><c>Features</c> is a list of atoms representing
+ <p><c><anno>Features</anno></c> is a list of atoms representing
allocation features used.</p>
</item>
<item>
- <p><c>Settings</c> is a list of subsystems, their
+ <p><c><anno>Settings</anno></c> is a list of subsystems, their
configurable parameters, and used values. Settings
may differ between different combinations of
platforms, allocators, and allocation features.
@@ -5533,15 +5024,15 @@ ok
erts_alloc(3)</seealso> documentation.
</p>
</item>
- <tag><marker id="system_info_allocator_tuple"><c>{allocator, Alloc}</c></marker></tag>
+ <tag><marker id="system_info_allocator_tuple"><c>{allocator, <anno>Alloc</anno>}</c></marker></tag>
<item>
<p>Returns information about the specified allocator.
As of erts version 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>Alloc</c> is not a recognized allocator,
- <c>undefined</c> is returned. If <c>Alloc</c> is disabled,
+ 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><em>Note:</em> The information returned is highly
implementation dependent and may be changed, or removed
@@ -5570,54 +5061,51 @@ ok
values. The first value is memory pool size and
the second value used memory size.</p>
</item>
- <tag><marker id="system_info_allocator_sizes"><c>{allocator_sizes, Alloc}</c></marker></tag>
+ <tag><marker id="system_info_allocator_sizes"><c>{allocator_sizes, <anno>Alloc</anno>}</c></marker></tag>
<item>
<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">erlang:system_info({allocator, Alloc})</seealso>.
+ <seealso marker="#system_info_allocator_tuple">erlang:system_info({allocator, <anno>Alloc</anno>})</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
- may be added and/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>
- </item>
- <tag><c>check_io</c></tag>
- <item>
- <p>Returns a list containing miscellaneous information
- regarding the emulators internal I/O checking. Note,
- the content of the returned list may vary between
- platforms and over time. The only thing guaranteed is
- that a list is returned.</p>
- </item>
- <tag><c>compat_rel</c></tag>
- <item>
- <p>Returns the compatibility mode of the local node as
- an integer. The integer returned represents the
- Erlang/OTP release which the current emulator has been
- set to be backward compatible with. The compatibility
- mode can be configured at startup by using the command
- line flag <c>+R</c>, see
- <seealso marker="erts:erl#compat_rel">erl(1)</seealso>.</p>
- </item>
- <tag><marker id="system_info_cpu_topology"><c>cpu_topology</c></marker></tag>
+ </taglist>
+ </desc>
+ </func>
+ <func>
+ <name name="system_info" arity="1" clause_i="10"/>
+ <name name="system_info" arity="1" clause_i="11"/>
+ <type name="cpu_topology"/>
+ <type name="level_entry"/>
+ <type_desc name="cpu_topology">
+ <marker id="system_info_cpu_topology"></marker>
+ All <c><anno>LevelEntry</anno></c>s of a list
+ must contain the same <c><anno>LevelTag</anno></c>, except
+ on the top level where both <c>node</c> and
+ <c>processor</c> <c><anno>LevelTag</anno></c>s may co-exist.
+ </type_desc>
+ <type_desc name="level_entry">
+ <c>{<anno>LevelTag</anno>, <anno>SubLevel</anno>} == {<anno>LevelTag</anno>, [], <anno>SubLevel</anno>}</c>
+ </type_desc>
+ <type name="level_tag"/>
+ <type_desc name="level_tag">
+ More <c><anno>LevelTag</anno></c>s may be introduced in the future.
+ </type_desc>
+ <type name="sub_level"/>
+ <type name="info_list"/>
+ <type_desc name="info_list">
+ The <c>info_list()</c> may be extended in the future.
+ </type_desc>
+ <fsummary>Information about the CPU topology of the system</fsummary>
+ <desc>
+ <p>Returns various information about the
+ <marker id="system_info_cpu_topology_tags">CPU topology</marker>
+ 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>CpuTopology</c> which currently is used by the
+ <p>Returns the <c><anno>CpuTopology</anno></c> which currently is 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
@@ -5625,31 +5113,11 @@ ok
<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>Types:</p>
- <list type="bulleted">
- <item><c>CpuTopology = LevelEntryList | undefined</c></item>
- <item><c>LevelEntryList = [LevelEntry]</c> (all
- <c>LevelEntry</c>s of a <c>LevelEntryList</c>
- must contain the same <c>LevelTag</c>, except
- on the top level where both <c>node</c> and
- <c>processor</c> <c>LevelTag</c>s may co-exist)</item>
- <item><c>LevelEntry = {LevelTag, SubLevel}
- | {LevelTag, InfoList, SubLevel}</c>
- (<c>{LevelTag, SubLevel}
- == {LevelTag, [], SubLevel}</c>)</item>
- <item><c>LevelTag = node|processor|core|thread</c>
- (more <c>LevelTag</c>s may be introduced in
- the future)</item>
- <item><c>SubLevel = [LevelEntry] | LogicalCpuId</c></item>
- <item><c>LogicalCpuId = {logical, integer()}</c></item>
- <item><c>InfoList = []</c> (the <c>InfoList</c>
- may be extended in the future)</item>
- </list>
<p><c>node</c> refers to NUMA (non-uniform memory access)
nodes, and <c>thread</c> refers to hardware threads
(e.g. Intels hyper-threads).</p>
- <p>A level in the <c>CpuTopology</c> term can be omitted if
- only one entry exists and the <c>InfoList</c> is empty.
+ <p>A level in the <c><anno>CpuTopology</anno></c> term can be omitted if
+ only one entry exists and the <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 either <c>processor</c>
@@ -5661,15 +5129,15 @@ ok
consist of a mix of processor internal and external
NUMA nodes, as long as each logical CPU belongs to one
and only one NUMA node. Cache hierarchy is not part of
- the <c>CpuTopology</c> type yet, but will be in the
+ the <c><anno>CpuTopology</anno></c> type yet, but will be in the
future. Other things may also make it into the CPU
topology in the future. In other words, expect the
- <c>CpuTopology</c> type to change.
+ <c><anno>CpuTopology</anno></c> type to change.
</p>
</item>
<tag><marker id="system_info_cpu_topology_defined"><c>{cpu_topology, defined}</c></marker></tag>
<item>
- <p>Returns the user defined <c>CpuTopology</c>. For more
+ <p>Returns the user defined <c><anno>CpuTopology</anno></c>. For more
information see the documentation of
the <c>erl</c> <seealso marker="erts:erl#+sct">+sct</seealso> command
line flag, and the documentation of the
@@ -5679,7 +5147,7 @@ ok
</item>
<tag><marker id="system_info_cpu_topology_detected"><c>{cpu_topology, detected}</c></marker></tag>
<item>
- <p>Returns the automatically detected <c>CpuTopology</c>. The
+ <p>Returns the automatically detected <c><anno>CpuTopology</anno></c>. The
emulator currently only 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.
@@ -5691,13 +5159,112 @@ ok
</item>
<tag><c>{cpu_topology, used}</c></tag>
<item>
- <p>Returns the <c>CpuTopology</c> which is used by the
+ <p>Returns the <c><anno>CpuTopology</anno></c> which is used by the
emulator. For more information see the
documentation of the
<seealso marker="#system_info_cpu_topology">cpu_topology</seealso>
argument.
</p>
</item>
+ </taglist>
+ </desc>
+ </func>
+ <func>
+ <name name="system_info" arity="1" clause_i="6"/>
+ <name name="system_info" arity="1" clause_i="7"/>
+ <name name="system_info" arity="1" clause_i="8"/>
+ <name name="system_info" arity="1" clause_i="9"/>
+ <name name="system_info" arity="1" clause_i="12"/>
+ <name name="system_info" arity="1" clause_i="13"/>
+ <name name="system_info" arity="1" clause_i="14"/>
+ <name name="system_info" arity="1" clause_i="15"/>
+ <name name="system_info" arity="1" clause_i="16"/>
+ <name name="system_info" arity="1" clause_i="17"/>
+ <name name="system_info" arity="1" clause_i="18"/>
+ <name name="system_info" arity="1" clause_i="19"/>
+ <name name="system_info" arity="1" clause_i="20"/>
+ <name name="system_info" arity="1" clause_i="21"/>
+ <name name="system_info" arity="1" clause_i="22"/>
+ <name name="system_info" arity="1" clause_i="23"/>
+ <name name="system_info" arity="1" clause_i="24"/>
+ <name name="system_info" arity="1" clause_i="25"/>
+ <name name="system_info" arity="1" clause_i="26"/>
+ <name name="system_info" arity="1" clause_i="27"/>
+ <name name="system_info" arity="1" clause_i="28"/>
+ <name name="system_info" arity="1" clause_i="29"/>
+ <name name="system_info" arity="1" clause_i="30"/>
+ <name name="system_info" arity="1" clause_i="31"/>
+ <name name="system_info" arity="1" clause_i="32"/>
+ <name name="system_info" arity="1" clause_i="33"/>
+ <name name="system_info" arity="1" clause_i="34"/>
+ <name name="system_info" arity="1" clause_i="35"/>
+ <name name="system_info" arity="1" clause_i="36"/>
+ <name name="system_info" arity="1" clause_i="37"/>
+ <name name="system_info" arity="1" clause_i="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"/>
+ <name name="system_info" arity="1" clause_i="43"/>
+ <name name="system_info" arity="1" clause_i="44"/>
+ <name name="system_info" arity="1" clause_i="45"/>
+ <name name="system_info" arity="1" clause_i="46"/>
+ <name name="system_info" arity="1" clause_i="47"/>
+ <name name="system_info" arity="1" clause_i="48"/>
+ <name name="system_info" arity="1" clause_i="49"/>
+ <name name="system_info" arity="1" clause_i="50"/>
+ <name name="system_info" arity="1" clause_i="51"/>
+ <fsummary>Information about the system</fsummary>
+ <desc>
+ <p>Returns various information about the current system
+ (emulator) as specified by <c><anno>Item</anno></c>:</p>
+ <taglist>
+ <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>
+ </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
+ may be added and/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>
+ </item>
+ <tag><c>check_io</c></tag>
+ <item>
+ <p>Returns a list containing miscellaneous information
+ regarding the emulators internal I/O checking. Note,
+ the content of the returned list may vary between
+ platforms and over time. The only thing guaranteed is
+ that a list is returned.</p>
+ </item>
+ <tag><c>compat_rel</c></tag>
+ <item>
+ <p>Returns the compatibility mode of the local node as
+ an integer. The integer returned represents the
+ Erlang/OTP release which the current emulator has been
+ set to be backward compatible with. The compatibility
+ mode can be configured at startup by using the command
+ line flag <c>+R</c>, see
+ <seealso marker="erts:erl#compat_rel">erl(1)</seealso>.</p>
+ </item>
+ <tag><c>cpu_topology</c></tag>
+ <item>
+ <p>See <seealso marker="#system_info_cpu_topology_tags">above</seealso>.</p>
+ </item>
<tag><c>creation</c></tag>
<item>
<p>Returns the creation of the local node as an integer.
@@ -5727,10 +5294,10 @@ ok
<item>
<p>Returns a list of tuples
<c>{Node, ControllingEntity}</c>, one entry for each
- connected remote node. The <c>Node</c> is the name of the
- node and the <c>ControllingEntity</c> is the port or pid
+ connected remote node. The <c><anno>Node</anno></c> is the name of the
+ node and the <c><anno>ControllingEntity</anno></c> is the port or pid
responsible for the communication to that node. More
- specifically, the <c>ControllingEntity</c> for nodes
+ specifically, the <c><anno>ControllingEntity</anno></c> for nodes
connected via TCP/IP (the normal case) is the socket
actually used in communication with the specific node.</p>
</item>
@@ -5778,7 +5345,7 @@ ok
</item>
<tag><c>fullsweep_after</c></tag>
<item>
- <p>Returns <c>{fullsweep_after, integer()}</c> which is the
+ <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>
@@ -5871,12 +5438,12 @@ ok
</item>
<tag><c>min_heap_size</c></tag>
<item>
- <p>Returns <c>{min_heap_size, MinHeapSize}</c> where <c>MinHeapSize</c> is the current system wide
+ <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, MinBinVHeapSize}</c> where <c>MinBinVHeapSize</c> is the current system wide
+ <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>
@@ -5920,10 +5487,10 @@ ok
</item>
<tag><marker id="system_info_multi_scheduling_blockers"><c>multi_scheduling_blockers</c></marker></tag>
<item>
- <p>Returns a list of <c>PID</c>s when multi-scheduling
- is blocked; otherwise, the empty list. The <c>PID</c>s
- in the list is <c>PID</c>s of the processes currently
- blocking multi-scheduling. A <c>PID</c> will only be
+ <p>Returns a list of <c><anno>PID</anno></c>s when multi-scheduling
+ is blocked; otherwise, the empty list. The <c><anno>PID</anno></c>s
+ in the list is <c><anno>PID</anno></c>s of the processes currently
+ blocking multi-scheduling. A <c><anno>PID</anno></c> will only be
present 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>,
@@ -5998,7 +5565,7 @@ ok
<item>
<p>Returns the scheduler id (<c>SchedulerId</c>) of the
scheduler thread that the calling process is executing
- on. <c>SchedulerId</c> is a positive integer; where
+ 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>
</item>
@@ -6030,7 +5597,8 @@ ok
<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>
+ </p> <name name="system_info" arity="1" clause_i="49"/>
+
</item>
<tag><c>smp_support</c></tag>
<item>
@@ -6124,53 +5692,39 @@ ok
</func>
<func>
- <name>erlang:system_monitor() -> MonSettings</name>
+ <name name="system_monitor" arity="0"/>
+ <type name="system_monitor_option"/>
<fsummary>Current system performance monitoring settings</fsummary>
- <type>
- <v>MonSettings -> {MonitorPid, Options} | undefined</v>
- <v>&nbsp;MonitorPid = pid()</v>
- <v>&nbsp;Options = [Option]</v>
- <v>&nbsp;&nbsp;Option = {long_gc, Time} | {large_heap, Size} | busy_port | busy_dist_port</v>
- <v>&nbsp;&nbsp;&nbsp;Time = Size = integer()</v>
- </type>
<desc>
<p>Returns the current system monitoring settings set by
<seealso marker="#system_monitor/2">erlang:system_monitor/2</seealso>
- as <c>{MonitorPid, Options}</c>, or <c>undefined</c> if there
+ as <c>{<anno>MonitorPid</anno>, <anno>Options</anno>}</c>, or <c>undefined</c> if there
are no settings. The order of the options may be different
from the one that was set.</p>
</desc>
</func>
<func>
- <name>erlang:system_monitor(undefined | {MonitorPid, Options}) -> MonSettings</name>
+ <name name="system_monitor" arity="1"/>
+ <type name="system_monitor_option"/>
<fsummary>Set or clear system performance monitoring options</fsummary>
- <type>
- <v>MonitorPid, Options, MonSettings -- see below</v>
- </type>
<desc>
<p>When called with the argument <c>undefined</c>, all
system performance monitoring settings are cleared.</p>
- <p>Calling the function with <c>{MonitorPid, Options}</c> as
+ <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">erlang:system_monitor(MonitorPid, Options)</seealso>.</p>
+ <seealso marker="#system_monitor/2">erlang:system_monitor(<anno>MonitorPid</anno>, <anno>Options</anno>)</seealso>.</p>
<p>Returns the previous system monitor settings just like
<seealso marker="#system_monitor/0">erlang:system_monitor/0</seealso>.</p>
</desc>
</func>
<func>
- <name>erlang:system_monitor(MonitorPid, [Option]) -> MonSettings</name>
+ <name name="system_monitor" arity="2"/>
+ <type name="system_monitor_option"/>
<fsummary>Set system performance monitoring options</fsummary>
- <type>
- <v>MonitorPid = pid()</v>
- <v>Option = {long_gc, Time} | {large_heap, Size} | busy_port | busy_dist_port</v>
- <v>&nbsp;Time = Size = integer()</v>
- <v>MonSettings = {OldMonitorPid, [Option]}</v>
- <v>&nbsp;OldMonitorPid = pid()</v>
- </type>
- <desc>
- <p>Sets system performance monitoring options. <c>MonitorPid</c>
+ <desc>
+ <p>Sets system performance monitoring options. <c><anno>MonitorPid</anno></c>
is a local pid that will receive system monitor messages, and
the second argument is a list of monitoring options:</p>
<taglist>
@@ -6179,7 +5733,7 @@ ok
<p>If a garbage collection in the system takes at least
<c>Time</c> wallclock milliseconds, a message
<c>{monitor, GcPid, long_gc, Info}</c> is sent to
- <c>MonitorPid</c>. <c>GcPid</c> is the pid that was
+ <c><anno>MonitorPid</anno></c>. <c>GcPid</c> is the pid that was
garbage collected and <c>Info</c> is a list of two-element
tuples describing the result of the garbage collection.
One of the tuples is <c>{timeout, GcTime}</c> where
@@ -6202,7 +5756,7 @@ ok
<p>If a garbage collection in the system results in
the allocated size of a heap being at least <c>Size</c>
words, a message <c>{monitor, GcPid, large_heap, Info}</c>
- is sent to <c>MonitorPid</c>. <c>GcPid</c> and <c>Info</c>
+ is sent to <c><anno>MonitorPid</anno></c>. <c>GcPid</c> and <c>Info</c>
are the same as for <c>long_gc</c> above, except that
the tuple tagged with <c>timeout</c> is not present.
<em>Note</em>: As of erts version 5.6 the monitor message
@@ -6218,7 +5772,7 @@ ok
<p>If a process in the system gets suspended because it
sends to a busy port, a message
<c>{monitor, SusPid, busy_port, Port}</c> is sent to
- <c>MonitorPid</c>. <c>SusPid</c> is the pid that got
+ <c><anno>MonitorPid</anno></c>. <c>SusPid</c> is the pid that got
suspended when sending to <c>Port</c>.</p>
</item>
<tag><c>busy_dist_port</c></tag>
@@ -6227,7 +5781,7 @@ ok
sends to a process on a remote node whose inter-node
communication was handled by a busy port, a message
<c>{monitor, SusPid, busy_dist_port, Port}</c> is sent to
- <c>MonitorPid</c>. <c>SusPid</c> is the pid that got
+ <c><anno>MonitorPid</anno></c>. <c>SusPid</c> is the pid that got
suspended when sending through the inter-node
communication port <c>Port</c>.</p>
</item>
@@ -6242,48 +5796,48 @@ ok
<p>Keep the monitoring process neat and do not set the system
monitor limits too tight.</p>
</note>
- <p>Failure: <c>badarg</c> if <c>MonitorPid</c> does not exist.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>MonitorPid</anno></c> does not exist or is not a local process.</p>
</desc>
</func>
<func>
- <name>erlang:system_profile() -> ProfilerSettings</name>
+ <name name="system_profile" arity="0"/>
+ <type name="system_profile_option"/>
<fsummary>Current system profiling settings</fsummary>
- <type>
- <v>ProfilerSettings -> {ProfilerPid, Options} | undefined</v>
- <v>&nbsp;ProfilerPid = pid() | port()</v>
- <v>&nbsp;Options = [Option]</v>
- <v>&nbsp;&nbsp;Option = runnable_procs | runnable_ports | scheduler | exclusive</v>
- </type>
<desc>
<p>Returns the current system profiling settings set by
<seealso marker="#system_profile/2">erlang:system_profile/2</seealso>
- as <c>{ProfilerPid, Options}</c>, or <c>undefined</c> if there
+ as <c>{<anno>ProfilerPid</anno>, <anno>Options</anno>}</c>, or <c>undefined</c> if there
are no settings. The order of the options may be different
from the one that was set.</p>
</desc>
</func>
<func>
- <name>erlang:system_profile(ProfilerPid, Options) -> ProfilerSettings</name>
+ <name name="system_profile" arity="2"/>
+ <type name="system_profile_option"/>
<fsummary>Current system profiling settings</fsummary>
- <type>
- <v>ProfilerSettings -> {ProfilerPid, Options} | undefined</v>
- <v>&nbsp;ProfilerPid = pid() | port()</v>
- <v>&nbsp;Options = [Option]</v>
- <v>&nbsp;&nbsp;Option = runnable_procs | runnable_ports | scheduler | exclusive</v>
- </type>
- <desc>
- <p>Sets system profiler options. <c>ProfilerPid</c>
+ <desc>
+ <p>Sets system profiler options. <c><anno>ProfilerPid</anno></c>
is a local pid or port that will receive profiling messages. The
receiver is excluded from all profiling.
The second argument is a list of profiling options:</p>
<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 subsequently <c>active</c> when the port
+ callback returns.
+ </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>ProfilerPid</c>. Running processes that is reinserted into the
+ <c><anno>ProfilerPid</anno></c>. Running processes that is reinserted into the
run queue after having been preemptively scheduled out will not trigger this
message.
</p>
@@ -6292,24 +5846,14 @@ ok
<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>ProfilerPid</c>.
+ <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>ProfilerPid</c>.
- </p>
- </item>
- <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 subsequently <c>active</c> when the port
- callback returns.
+ to <c><anno>ProfilerPid</anno></c>.
</p>
</item>
</taglist>
@@ -6320,14 +5864,11 @@ ok
</func>
<func>
- <name>term_to_binary(Term) -> ext_binary()</name>
+ <name name="term_to_binary" arity="1"/>
<fsummary>Encode a term to an Erlang external term format binary</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
<p>Returns a binary data object which is the result of encoding
- <c>Term</c> according to the Erlang external term format.</p>
+ <c><anno>Term</anno></c> according to the Erlang external term format.</p>
<p>This can be used for a variety of 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
@@ -6337,20 +5878,16 @@ ok
</desc>
</func>
<func>
- <name>term_to_binary(Term, [Option]) -> ext_binary()</name>
+ <name name="term_to_binary" arity="2"/>
<fsummary>Encode a term to en Erlang external term format binary</fsummary>
- <type>
- <v>Term = term()</v>
- <v>Option = compressed | {compressed,Level} | {minor_version,Version}</v>
- </type>
<desc>
<p>Returns a binary data object which is the result of encoding
- <c>Term</c> according to the Erlang external term format.</p>
+ <c><anno>Term</anno></c> according to the Erlang external term format.</p>
<p>If the option <c>compressed</c> is provided, the external
term format will be compressed. The compressed format is
automatically recognized by <c>binary_to_term/1</c> in R7B and later.</p>
<p>It is also possible to specify a compression level by giving
- the option <c>{compressed,Level}</c>, where <c>Level</c> is an
+ the option <c>{compressed, <anno>Level</anno>}</c>, where <c><anno>Level</anno></c> is an
integer from 0 through 9. <c>0</c> means that no compression
will be done (it is the same as not giving any <c>compressed</c> option);
<c>1</c> will take the least time but may not compress as well as
@@ -6359,16 +5896,16 @@ ok
on the input term, level 9 compression may or may not produce a smaller
result than level 1 compression.</p>
<p>Currently, <c>compressed</c> gives the same result as
- <c>{compressed,6}</c>.</p>
- <p>The option <c>{minor_version,Version}</c> can be use to control
+ <c>{compressed, 6}</c>.</p>
+ <p>The option <c>{minor_version, <anno>Version</anno>}</c> can be use to control
some details of the encoding. This option was
- introduced in R11B-4. Currently, the allowed values for <c>Version</c>
+ introduced in R11B-4. Currently, the allowed values for <c><anno>Version</anno></c>
are <c>0</c> and <c>1</c>.</p>
- <p><c>{minor_version,1}</c> forces any floats in the term to be encoded
+ <p><c>{minor_version, 1}</c> 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). <c>binary_to_term/1</c>
in R11B-4 and later is able decode the new representation.</p>
- <p><c>{minor_version,0}</c> is currently the default, meaning that floats
+ <p><c>{minor_version, 0}</c> is currently the default, meaning that floats
will be encoded using a textual representation; this option is useful if
you want to ensure that releases prior to R11B-4 can decode resulting
binary.</p>
@@ -6377,14 +5914,11 @@ ok
</desc>
</func>
<func>
- <name>throw(Any)</name>
+ <name name="throw" arity="1"/>
<fsummary>Throw an exception</fsummary>
- <type>
- <v>Any = term()</v>
- </type>
<desc>
<p>A non-local return from a function. If evaluated within a
- <c>catch</c>, <c>catch</c> will return the value <c>Any</c>.</p>
+ <c>catch</c>, <c>catch</c> will return the value <c><anno>Any</anno></c>.</p>
<pre>
> <input>catch throw({hello, there}).</input>
{hello,there}</pre>
@@ -6392,11 +5926,8 @@ ok
</desc>
</func>
<func>
- <name>time() -> {Hour, Minute, Second}</name>
+ <name name="time" arity="0"/>
<fsummary>Current time</fsummary>
- <type>
- <v>Hour = Minute = Second = integer() >= 0</v>
- </type>
<desc>
<p>Returns the current time as <c>{Hour, Minute, Second}</c>.</p>
<p>The time zone and daylight saving time correction depend on
@@ -6407,35 +5938,27 @@ ok
</desc>
</func>
<func>
- <name>tl(List1) -> List2</name>
+ <name name="tl" arity="1"/>
<fsummary>Tail of a list</fsummary>
- <type>
- <v>List1 = List2 = [term()]</v>
- </type>
<desc>
- <p>Returns the tail of <c>List1</c>, that is, the list minus
+ <p>Returns the tail of <c><anno>List</anno></c>, that is, the list minus
the first element.</p>
<pre>
> <input>tl([geesties, guilies, beasties]).</input>
[guilies, beasties]</pre>
<p>Allowed in guard tests.</p>
- <p>Failure: <c>badarg</c> if <c>List</c> is the empty list [].</p>
+ <p>Failure: <c>badarg</c> if <c><anno>List</anno></c> is the empty list [].</p>
</desc>
</func>
<func>
- <name>erlang:trace(PidSpec, How, FlagList) -> integer() >= 0</name>
+ <name name="trace" arity="3"/>
+ <type name="trace_flag"/>
<fsummary>Set trace flags for a process or processes</fsummary>
- <type>
- <v>PidSpec = pid() | existing | new | all</v>
- <v>How = boolean()</v>
- <v>FlagList = [Flag]</v>
- <v>&nbsp;Flag -- see below</v>
- </type>
- <desc>
- <p>Turns on (if <c>How == true</c>) or off (if
- <c>How == false</c>) the trace flags in <c>FlagList</c> for
- the process or processes represented by <c>PidSpec</c>.</p>
- <p><c>PidSpec</c> is either a pid for a local process, or one of
+ <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 pid for a local process, or one of
the following atoms:</p>
<taglist>
<tag><c>existing</c></tag>
@@ -6452,7 +5975,7 @@ ok
will be created in the future.</p>
</item>
</taglist>
- <p><c>FlagList</c> can contain any number of the following
+ <p><c><anno>FlagList</anno></c> can contain any number of the following
flags (the "message tags" refers to the list of messages
following below):</p>
<taglist>
@@ -6767,11 +6290,11 @@ ok
<p>Only one process can trace a particular process. For this
reason, attempts to trace an already traced process will fail.</p>
<p>Returns: A number indicating the number of processes that
- matched <c>PidSpec</c>. If <c>PidSpec</c> is a pid,
- the return value will be <c>1</c>. If <c>PidSpec</c> is
+ matched <c><anno>PidSpec</anno></c>. If <c><anno>PidSpec</anno></c> is a pid,
+ the return value will be <c>1</c>. If <c><anno>PidSpec</anno></c> is
<c>all</c> or <c>existing</c> the return value will be
the number of processes running, excluding tracer processes.
- If <c>PidSpec</c> is <c>new</c>, the return value will be
+ If <c><anno>PidSpec</anno></c> is <c>new</c>, the return value will be
<c>0</c>.</p>
<p>Failure: If specified arguments are not supported. For
example <c>cpu_timestamp</c> is not supported on all
@@ -6779,64 +6302,58 @@ ok
</desc>
</func>
<func>
- <name>erlang:trace_delivered(Tracee) -> Ref</name>
+ <name name="trace_delivered" arity="1"/>
<fsummary>Notification when trace has been delivered</fsummary>
- <type>
- <v>Tracee = pid() | all</v>
- <v>Ref = reference()</v>
- </type>
<desc>
<p>The delivery of trace messages is dislocated on the time-line
compared to other events in the system. If you know that the
- <c>Tracee</c> has passed some specific point in its execution,
+ <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
- you can use <c>erlang:trace_delivered(Tracee)</c>. A
- <c>{trace_delivered, Tracee, Ref}</c> message is sent to
- the caller of <c>erlang:trace_delivered(Tracee)</c> when it
+ you can 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 have been delivered to
- the tracer up to the point that the <c>Tracee</c> had reached
+ the tracer up to the point that the <c><anno>Tracee</anno></c> had reached
at the time of the call to
- <c>erlang:trace_delivered(Tracee)</c>.</p>
+ <c>erlang:trace_delivered(<anno>Tracee</anno>)</c>.</p>
<p>Note that the <c>trace_delivered</c> message does <em>not</em>
imply that trace messages have been delivered; instead, it implies
that all trace messages that <em>should</em> be delivered have
- been delivered. It is not an error if <c>Tracee</c> isn't, and
+ been delivered. It is not an error if <c><anno>Tracee</anno></c> isn't, and
hasn't been traced by someone, but if this is the case,
<em>no</em> trace messages will have been delivered when the
<c>trace_delivered</c> message arrives.</p>
- <p>Note that <c>Tracee</c> has to refer to a process currently,
+ <p>Note that <c><anno>Tracee</anno></c> has to refer to a process currently,
or previously existing on the same node as the caller of
- <c>erlang:trace_delivered(Tracee)</c> resides on.
- The special <c>Tracee</c> atom <c>all</c> denotes all processes
+ <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>
- <p>An example: Process <c>A</c> is tracee, port <c>B</c> is
+ <p>An 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. <c>C</c>
can ensure that the trace isn't truncated by calling
<c>erlang:trace_delivered(A)</c> when <c>A</c> exits and wait
- for the <c>{trace_delivered, A, Ref}</c> message before closing
+ for the <c>{trace_delivered, A, <anno>Ref</anno>}</c> message before closing
<c>B</c>.</p>
- <p>Failure: <c>badarg</c> if <c>Tracee</c> does not refer to a
+ <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
- <c>erlang:trace_delivered(Tracee)</c> resides on.</p>
+ <c>erlang:trace_delivered(<anno>Tracee</anno>)</c> resides on.</p>
</desc>
</func>
<func>
- <name>erlang:trace_info(PidOrFunc, Item) -> Res</name>
+ <name name="trace_info" arity="2"/>
+ <type name="trace_info_return"/>
+ <type name="trace_info_item_result"/>
+ <type name="trace_info_flag"/>
+ <type name="trace_match_spec"/>
<fsummary>Trace information about a process or function</fsummary>
- <type>
- <v>PidOrFunc = pid() | new | {Module, Function, Arity} | on_load</v>
- <v>&nbsp;Module = Function = atom()</v>
- <v>&nbsp;Arity = arity()</v>
- <v>Item, Res -- see below</v>
- </type>
<desc>
<p>Returns trace information about a process or function.</p>
- <p>To get information about a process, <c>PidOrFunc</c> should
+ <p>To get information about a process, <c><anno>PidOrFunc</anno></c> should
be a pid or the atom <c>new</c>. The atom <c>new</c> means
that the default trace state for processes to be created will
- be returned. <c>Item</c> must have one of the following
+ be returned. <c><anno>Item</anno></c> must have one of the following
values:</p>
<taglist>
<tag><c>flags</c></tag>
@@ -6916,22 +6433,24 @@ ok
<tag><c>all</c></tag>
<item>
- <p>Return a list containing the <c>{Item, Value}</c> tuples
+ <p>Return a list containing the <c>{<anno>Item</anno>, Value}</c> tuples
for all other items, or return <c>false</c> if no tracing
is active for this function.</p>
</item>
</taglist>
- <p>The actual return value will be <c>{Item, Value}</c>, where
+ <p>The actual return value will be <c>{<anno>Item</anno>, Value}</c>, where
<c>Value</c> is the requested information as described above.
If a pid for a dead process was given, or the name of a
non-existing function, <c>Value</c> will be <c>undefined</c>.</p>
- <p>If <c>PidOrFunc</c> is the <c>on_load</c>, the information
+ <p>If <c><anno>PidOrFunc</anno></c> is the <c>on_load</c>, the information
returned refers to the default value for code that will be
loaded.</p>
</desc>
</func>
<func>
- <name>erlang:trace_pattern(MFA, MatchSpec) -> integer() >= 0</name>
+ <name name="trace_pattern" arity="2" clause_i="1"/>
+ <type name="trace_pattern_mfa"/>
+ <type name="trace_match_spec"/>
<fsummary>Set trace patterns for global call tracing</fsummary>
<desc>
<p>The same as
@@ -6940,11 +6459,11 @@ ok
</desc>
</func>
<func>
- <name>erlang:trace_pattern(MFA, MatchSpec, FlagList) -> integer() >= 0</name>
+ <name name="trace_pattern" arity="3"/>
+ <type name="trace_pattern_mfa"/>
+ <type name="trace_match_spec"/>
+ <type name="trace_pattern_flag"/>
<fsummary>Set trace patterns for tracing of function calls</fsummary>
- <type>
- <v>MFA, MatchSpec, FlagList -- see below</v>
- </type>
<desc>
<p>This BIF is used to enable or disable call tracing for
exported functions. It must be combined with
@@ -6969,7 +6488,7 @@ ok
and an action to be performed. The default action is to send a
trace message. If the pattern does not match or the guard
fails, the action will not be executed.</p>
- <p>The <c>MFA</c> argument should be a tuple like
+ <p>The <c><anno>MFA</anno></c> argument should be a tuple like
<c>{Module, Function, Arity}</c> or the atom <c>on_load</c>
(described below). It can be the module, function, and arity
for an exported function (or a BIF in any module).
@@ -6992,11 +6511,11 @@ ok
</taglist>
<p>Other combinations, such as <c>{Module,'_',Arity}</c>, are
not allowed. Local functions will match wildcards only if
- the <c>local</c> option is in the <c>FlagList</c>.</p>
- <p>If the <c>MFA</c> argument is the atom <c>on_load</c>,
+ the <c>local</c> option is in the <c><anno>FlagList</anno></c>.</p>
+ <p>If the <c><anno>MFA</anno></c> argument is the atom <c>on_load</c>,
the match specification and flag list will be used on all
modules that are newly loaded.</p>
- <p>The <c>MatchSpec</c> argument can take any of the following
+ <p>The <c><anno>MatchSpec</anno></c> argument can take any of the following
forms:</p>
<taglist>
<tag><c>false</c></tag>
@@ -7008,7 +6527,7 @@ ok
<item>
<p>Enable tracing for the matching function(s).</p>
</item>
- <tag><c>MatchSpecList</c></tag>
+ <tag><c><anno>MatchSpecList</anno></c></tag>
<item>
<p>A list of match specifications. An empty list is
equivalent to <c>true</c>. See the ERTS User's Guide
@@ -7016,18 +6535,18 @@ ok
</item>
<tag><c>restart</c></tag>
<item>
- <p>For the <c>FlagList</c> option <c>call_count</c> and <c>call_time</c>:
+ <p>For the <c><anno>FlagList</anno></c> option <c>call_count</c> and <c>call_time</c>:
restart the existing counters. The behaviour is undefined
- for other <c>FlagList</c> options.</p>
+ for other <c><anno>FlagList</anno></c> options.</p>
</item>
<tag><c>pause</c></tag>
<item>
- <p>For the <c>FlagList</c> option <c>call_count</c> and <c>call_time</c>: pause
+ <p>For the <c><anno>FlagList</anno></c> option <c>call_count</c> and <c>call_time</c>: pause
the existing counters. The behaviour is undefined for
other <c>FlagList</c> options.</p>
</item>
</taglist>
- <p>The <c>FlagList</c> parameter is a list of options.
+ <p>The <c><anno>FlagList</anno></c> parameter is a list of options.
The following options are allowed:</p>
<taglist>
<tag><c>global</c></tag>
@@ -7046,13 +6565,13 @@ ok
the process, a <c>return_to</c> message will also be sent
when this function returns to its caller.</p>
</item>
- <tag><c>meta | {meta, Pid}</c></tag>
+ <tag><c>meta | {meta, <anno>Pid</anno>}</c></tag>
<item>
<p>Turn on or off meta tracing for all types of function
calls. Trace messages will be sent to the tracer process
- or port <c>Pid</c> whenever any of the specified
+ or port <c><anno>Pid</anno></c> whenever any of the specified
functions are called, regardless of how they are called.
- If no <c>Pid</c> is specified, <c>self()</c> is used as a
+ If no <c><anno>Pid</anno></c> 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>,
@@ -7064,32 +6583,32 @@ ok
</item>
<tag><c>call_count</c></tag>
<item>
- <p>Starts (<c>MatchSpec == true</c>) or stops
- (<c>MatchSpec == false</c>) call count tracing for all
+ <p>Starts (<c><anno>MatchSpec</anno> == true</c>) or stops
+ (<c><anno>MatchSpec</anno> == false</c>) call count tracing for all
types of function calls. For every function a counter is
incremented when the function is called, in any process.
No process trace flags need to be activated.</p>
<p>If call count tracing is started while already running,
the count is restarted from zero. Running counters can be
- paused with <c>MatchSpec == pause</c>. Paused and running
+ paused with <c><anno>MatchSpec</anno> == pause</c>. Paused and running
counters can be restarted from zero with
- <c>MatchSpec == restart</c>.</p>
+ <c><anno>MatchSpec</anno> == restart</c>.</p>
<p>The counter value can be read with
<seealso marker="#trace_info/2">erlang:trace_info/2</seealso>.</p>
</item>
<tag><c>call_time</c></tag>
<item>
- <p>Starts (<c>MatchSpec == true</c>) or stops
- (<c>MatchSpec == false</c>) call time tracing for all
+ <p>Starts (<c><anno>MatchSpec</anno> == true</c>) or stops
+ (<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. Time spent in the function
is accumulated in two other counters, seconds and micro-seconds.
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. Running counters can be
- paused with <c>MatchSpec == pause</c>. Paused and running
+ paused with <c><anno>MatchSpec</anno> == pause</c>. Paused and running
counters can be restarted from zero with
- <c>MatchSpec == restart</c>.</p>
+ <c><anno>MatchSpec</anno> == restart</c>.</p>
<p>The counter value can be read with
<seealso marker="#trace_info/2">erlang:trace_info/2</seealso>.</p>
</item>
@@ -7115,18 +6634,15 @@ ok
<seealso marker="#trace_info/2">erlang:trace_info/2</seealso>
BIF to retrieve the existing match specification.</p>
<p>Returns the number of exported functions that matched
- the <c>MFA</c> argument. This will be zero if none matched at
+ the <c><anno>MFA</anno></c> argument. This will be zero if none matched at
all.</p>
</desc>
</func>
<func>
- <name>trunc(Number) -> integer()</name>
+ <name name="trunc" arity="1"/>
<fsummary>Return an integer by the truncating a number</fsummary>
- <type>
- <v>Number = number()</v>
- </type>
<desc>
- <p>Returns an integer by the truncating <c>Number</c>.</p>
+ <p>Returns an integer by the truncating <c><anno>Number</anno></c>.</p>
<pre>
> <input>trunc(5.5).</input>
5</pre>
@@ -7134,13 +6650,10 @@ ok
</desc>
</func>
<func>
- <name>tuple_size(Tuple) -> integer() >= 0</name>
+ <name name="tuple_size" arity="1"/>
<fsummary>Return the size of a tuple</fsummary>
- <type>
- <v>Tuple = tuple()</v>
- </type>
<desc>
- <p>Returns an integer which is the number of elements in <c>Tuple</c>.</p>
+ <p>Returns an integer which is the number of elements in <c><anno>Tuple</anno></c>.</p>
<pre>
> <input>tuple_size({morni, mulle, bwange}).</input>
3</pre>
@@ -7148,25 +6661,19 @@ ok
</desc>
</func>
<func>
- <name>tuple_to_list(Tuple) -> [term()]</name>
+ <name name="tuple_to_list" arity="1"/>
<fsummary>Convert a tuple to a list</fsummary>
- <type>
- <v>Tuple = tuple()</v>
- </type>
<desc>
- <p>Returns a list which corresponds to <c>Tuple</c>.
- <c>Tuple</c> may contain any Erlang terms.</p>
+ <p>Returns a list which corresponds to <c><anno>Tuple</anno></c>.
+ <c><anno>Tuple</anno></c> may contain any Erlang terms.</p>
<pre>
> <input>tuple_to_list({share, {'Ericsson_B', 163}}).</input>
[share,{'Ericsson_B',163}]</pre>
</desc>
</func>
<func>
- <name>erlang:universaltime() -> DateTime</name>
+ <name name="universaltime" arity="0"/>
<fsummary>Current date and time according to Universal Time Coordinated (UTC)</fsummary>
- <type>
- <v>DateTime = <seealso marker="calendar#type-datetime">calendar:datetime()</seealso></v>
- </type>
<desc>
<p>Returns the current date and time according to Universal
Time Coordinated (UTC), also called GMT, in the form
@@ -7180,46 +6687,39 @@ ok
</desc>
</func>
<func>
- <name>erlang:universaltime_to_localtime({Date1, Time1}) -> {Date2, Time2}</name>
+ <name name="universaltime_to_localtime" arity="1"/>
<fsummary>Convert from Universal Time Coordinated (UTC) to local date and time</fsummary>
- <type>
- <v>Date1 = Date2 = <seealso marker="calendar#type-date">calendar:date()</seealso></v>
- <v>Time1 = Time2 = <seealso marker="calendar#type-time">calendar:time()</seealso></v>
- </type>
<desc>
<p>Converts Universal Time Coordinated (UTC) date and time to
local date and time, if this is supported by the underlying
OS. Otherwise, no conversion is done, and
- <c>{Date1, Time1}</c> is returned.</p>
+ <c><anno>Universaltime</anno></c> is returned.</p>
<pre>
> <input>erlang:universaltime_to_localtime({{1996,11,6},{14,18,43}}).</input>
{{1996,11,7},{15,18,43}}</pre>
- <p>Failure: <c>badarg</c> if <c>Date1</c> or <c>Time1</c> do
- not denote a valid date or time.</p>
+ <p>Failure: <c>badarg</c> if <c>Universaltime</c> does not denote
+ a valid date and time.</p>
</desc>
</func>
<func>
- <name>unlink(Id) -> true</name>
+ <name name="unlink" arity="1"/>
<fsummary>Remove a link, if there is one, to another process or port</fsummary>
- <type>
- <v>Id = pid() | port()</v>
- </type>
<desc>
<p>Removes the link, if there is one, between the calling
- process and the process or port referred to by <c>Id</c>.</p>
+ process and the process or port referred to by <c><anno>Id</anno></c>.</p>
<p>Returns <c>true</c> and does not fail, even if there is no
- link to <c>Id</c>, or if <c>Id</c> does not exist.</p>
- <p>Once <c>unlink(Id)</c> has returned it is guaranteed that
+ link to <c><anno>Id</anno></c>, or if <c><anno>Id</anno></c> does not exist.</p>
+ <p>Once <c>unlink(<anno>Id</anno>)</c> has returned it is guaranteed that
the link between the caller and the entity referred to by
- <c>Id</c> has no effect on the caller in the future (unless
+ <c><anno>Id</anno></c> has no effect on the caller in the future (unless
the link is setup again). If caller is trapping exits, an
- <c>{'EXIT', Id, _}</c> message due to the link might have
+ <c>{'EXIT', <anno>Id</anno>, _}</c> message due to the link might have
been placed in the caller's message queue prior to the call,
- though. Note, the <c>{'EXIT', Id, _}</c> message can be the
- result of the link, but can also be the result of <c>Id</c>
+ though. Note, the <c>{'EXIT', <anno>Id</anno>, _}</c> message can be the
+ result of the link, but can also be the result of <c><anno>Id</anno></c>
calling <c>exit/2</c>. Therefore, it <em>may</em> be
appropriate to cleanup the message queue when trapping exits
- after the call to <c>unlink(Id)</c>, as follow:</p>
+ after the call to <c>unlink(<anno>Id</anno>)</c>, as follow:</p>
<code type="none">
unlink(Id),
@@ -7242,13 +6742,10 @@ ok
</desc>
</func>
<func>
- <name>unregister(RegName) -> true</name>
+ <name name="unregister" arity="1"/>
<fsummary>Remove the registered name for a process (or port)</fsummary>
- <type>
- <v>RegName = atom()</v>
- </type>
<desc>
- <p>Removes the registered name <c>RegName</c>, associated with a
+ <p>Removes the registered name <c><anno>RegName</anno></c>, associated with a
pid or a port identifier.</p>
<pre>
> <input>unregister(db).</input>
@@ -7259,7 +6756,7 @@ true</pre>
</desc>
</func>
<func>
- <name>whereis(RegName) -> pid() | port() | undefined</name>
+ <name name="whereis" arity="1"/>
<fsummary>Get the pid (or port) with a given registered name</fsummary>
<desc>
<p>Returns the pid or port identifier with the registered name
diff --git a/erts/doc/src/erlc.xml b/erts/doc/src/erlc.xml
index 3358b8f115..81dffe45cf 100644
--- a/erts/doc/src/erlc.xml
+++ b/erts/doc/src/erlc.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>1997</year><year>2011</year>
+ <year>1997</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -67,7 +67,7 @@
<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_dir]]></c> directive, the
+ <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">
diff --git a/erts/doc/src/erlsrv.xml b/erts/doc/src/erlsrv.xml
index c1ecbc7b77..365ae21d39 100644
--- a/erts/doc/src/erlsrv.xml
+++ b/erts/doc/src/erlsrv.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>1998</year><year>2011</year>
+ <year>1998</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -357,11 +357,12 @@ The environment of an Erlang machine started
the console subsystem and programs running as window
applications. An application which runs in the console subsystem
(normal for port programs) uses the win32 function
- <c><![CDATA[SetConsoleCtrlHandler]]></c> to a control handler that returns
- TRUE in answer to the <c><![CDATA[CTRL_LOGOFF_EVENT]]></c>. 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
+ <c><![CDATA[SetConsoleCtrlHandler]]></c> to register a control handler
+ that returns TRUE 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>
<code type="none"><![CDATA[
#include <windows.h>
@@ -372,6 +373,8 @@ The environment of an Erlang machine started
BOOL WINAPI service_aware_handler(DWORD ctrl){
if(ctrl == CTRL_LOGOFF_EVENT)
return TRUE;
+ if(ctrl == CTRL_SHUTDOWN_EVENT)
+ return TRUE;
return FALSE;
}
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 6b6a3374d1..cdb72b2b98 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,379 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 5.9.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix erl_prim_loader errors in handling of primary
+ archive. The following errors have been corrected:</p>
+ <p>
+ <list> <item> If primary archive was named "xxx", then a
+ file in the same directory named "xxxyyy" would be
+ interpreted as a file named "yyy" inside the archive.
+ </item> <item> erl_prim_loader did not correctly create
+ and normalize absolute paths for primary archive and
+ files inside it, so unless given with exact same path
+ files inside the archive would not be found. E.g. if
+ escript was started as /full/path/to/xxx then
+ "./xxx/file" would not be found since erl_prim_loader
+ would try to match /full/path/to/xxx with
+ /full/path/to/./xxx. Same problem with ../. </item>
+ <item> Depending on how the primary archive was built,
+ erl_prim_loader:list_dir/1 would sometimes return an
+ empty string inside the file list. This was a virtual
+ element representing the top directory of the archive.
+ This has been removed. </item> </list></p>
+ <p>
+ Thanks to Tuncer Ayaz and Shunichi Shinohara for
+ reporting and co-authoring corrections.</p>
+ <p>
+ Own Id: OTP-10071</p>
+ </item>
+ <item>
+ <p>Fix: Add port-I/O statistics for active once and true
+ and not only active false.</p>
+ <p>
+ Own Id: OTP-10073</p>
+ </item>
+ <item>
+ <p>
+ The 64-bit windows installer did not look in the right
+ directories for 64-bit version of Microsoft Visual C++
+ 2010 Redistibutable Package and hence took the wrong
+ decision about having to install the redistributable
+ package if the 32-bit version was installed but not the
+ 64-bit and vice versa. This bug has now been fixed
+ Furthermore the sub-installer for the redistributable
+ package is now run in silent mode if the erlang installer
+ is.</p>
+ <p>
+ Own Id: OTP-10096</p>
+ </item>
+ <item>
+ <p>
+ epmd would fail to start automatically when starting a
+ distributed erlang node installed in a location with a
+ whitespace in the path.</p>
+ <p>
+ Own Id: OTP-10106</p>
+ </item>
+ <item>
+ <p>
+ A more or less harmless bug that sometimes caused memory
+ deallocations to be delayed longer than intended has been
+ fixed.</p>
+ <p>
+ Own Id: OTP-10116</p>
+ </item>
+ <item>
+ <p>
+ Fix bug causing emulator crash when running HiPE on ARM.
+ Bug has existed since R15B.</p>
+ <p>
+ Own Id: OTP-10137</p>
+ </item>
+ <item>
+ <p> A bug regarding spaces in C function prototypes has
+ been fixed. (Thanks to Richard O'Keefe.) </p>
+ <p>
+ Own Id: OTP-10138</p>
+ </item>
+ <item>
+ <p>
+ Corrected dtrace pid length in message related probes.
+ (Thanks to Zheng Siyao)</p>
+ <p>
+ Own Id: OTP-10142</p>
+ </item>
+ <item>
+ <p>
+ Correct formating in exit error messages</p>
+ <p>
+ Ensure displayed sizes are not negative. (Thanks to
+ Michael Santos)</p>
+ <p>
+ Own Id: OTP-10148</p>
+ </item>
+ <item>
+ <p>
+ fix escript/primary archive reloading</p>
+ <p>
+ If the mtime of an escript/primary archive file changes
+ after being added to the code path, correctly reload the
+ archive and update the cache. (Thanks to Tuncer Ayaz)</p>
+ <p>
+ Own Id: OTP-10151</p>
+ </item>
+ <item>
+ <p>
+ Doc fix: link from erlang:now/0 to os:timestamp/0</p>
+ <p>
+ Sometimes os:timestamp/0 is more appropriate than
+ erlang:now/0. The documentation for the former has a link
+ to the latter; this patch adds a link in the other
+ direction to make os:timestamp/0 more visible. Thanks to
+ Magnus Henoch</p>
+ <p>
+ Own Id: OTP-10180</p>
+ </item>
+ <item>
+ <p>
+ The caret in the werl window (on Windows) could appear at
+ the wrong place after regaining focus. This is now
+ corrected.</p>
+ <p>
+ Own Id: OTP-10181</p>
+ </item>
+ <item>
+ <p>
+ Fix bug that in some cases could cause corrupted binaries
+ in ETS tables with <c>compressed</c> option.</p>
+ <p>
+ Own Id: OTP-10182</p>
+ </item>
+ <item>
+ <p>
+ Fix use of "clever" mktime</p>
+ <p>
+ Commit 1eef765 introduced regression (conditional
+ _always_ evaluates to true) in which
+ erlang:localtime_to_universaltime/2 stopped working on
+ systems configured with timezone without DST (i.e. UTC)
+ on *BSD platforms: 1>
+ erlang:localtime_to_universaltime({{2012,1,1},{0,0,0}},
+ true). ** exception error: bad argument Thanks to Piotr
+ Sikora</p>
+ <p>
+ Own Id: OTP-10187</p>
+ </item>
+ <item>
+ <p>
+ Relocate bodies of DTrace probes to the statically-linked
+ VM.</p>
+ <p>
+ Due to various operating systems (in both the DTrace and
+ SystemTap worlds) not fully supporting DTrace probes (or
+ SystemTap-compatibility mode probes) in shared libraries,
+ we relocate those probes to the statically-linked virtual
+ machine. This could be seen as pollution of the pristine
+ VM by a (yet) experimental feature. However:</p>
+ <p>
+ 1. This code can be eliminated completely by the C
+ preprocessor. 2. Leaving the probes in the dyntrace NIF
+ shared library simply does not work correctly on too many
+ platforms. *Many* thanks to Macneil Shonle at Basho for
+ assisting when my RSI-injured fingers gave out. (note:
+ Solaris 10 and FreeBSD 9.0-RELEASE can take a long time
+ to compile)</p>
+ <p>
+ Own Id: OTP-10189</p>
+ </item>
+ <item>
+ <p>
+ Fix bug in <c>ets:test_ms/2</c> that could cause emulator
+ crash when using <c>'$_'</c> in match spec.</p>
+ <p>
+ Own Id: OTP-10190</p>
+ </item>
+ <item>
+ <p>
+ Supplying a filename longer than the operating system
+ MAX_PATH to file:read_link/1 would cause a crash
+ (Segemntation fault/Critical Error) on all platforms.
+ This is now corrected.</p>
+ <p>
+ Own Id: OTP-10200</p>
+ </item>
+ <item>
+ <p>
+ If Perl was configured to interpret files as being
+ encoded in UTF-8, the build would crash in
+ <c>make_preload</c>. (Thanks to Aaron Harnly for noticing
+ this issue.)</p>
+ <p>
+ Own Id: OTP-10201</p>
+ </item>
+ <item>
+ <p>
+ Fix the erlc -MP flag</p>
+ <p>
+ Because of a copy-and-paste error in erlc.c, the -MP flag
+ had the same effect as -MG. As a workaround, you had to
+ pass +makedep_phony to enable the MP option. This patch
+ makes -MP work as intended.</p>
+ <p>
+ Own Id: OTP-10211</p>
+ </item>
+ <item>
+ <p>
+ Allow mixed IPv4 and IPv6 addresses to sctp_bindx</p>
+ <p>
+ Also allow mixed address families to bind, since the
+ first address on a multihomed sctp socket must be bound
+ with bind, while the rest are to be bound using
+ sctp_bindx. At least Linux supports adding address of
+ mixing families. Make inet_set_faddress function
+ available also when HAVE_SCTP is not defined, since we
+ use it to find an address for bind to be able to mix ipv4
+ and ipv6 addresses. Thanks to Tomas Abrahamsson</p>
+ <p>
+ Own Id: OTP-10217</p>
+ </item>
+ <item>
+ <p>
+ Fix support for leap seconds-aware timezones</p>
+ <p>
+ erlang:universaltime_to_localtime is leap seconds-aware
+ (since 2008), however erlang:localtime_to_universaltime
+ is not, which gives surprising results on systems
+ configured with leap seconds-aware timezones: 1>
+ erlang:universaltime_to_localtime({{2012,1,1},{0,0,0}}).
+ {{2012,1,1},{0,0,0}} 2>
+ erlang:localtime_to_universaltime({{2012,1,1},{0,0,0}}).
+ {{2012,1,1},{0,0,24}} and completely breaks
+ calendar:local_time_to_universal_time_dst: 3>
+ calendar:local_time_to_universal_time_dst({{2011,1,1},{0,0,0}}).
+ [] Thanks to Piotr Sikora</p>
+ <p>
+ Own Id: OTP-10227</p>
+ </item>
+ <item>
+ <p>
+ erlsrv: gracefully stop emulator on Windows shutdown</p>
+ <p>
+ Windows will send the SERVICE_CONTROL_SHUTDOWN event to
+ the service control handler when shutting down the
+ system. Instead of ignoring the event, erlsrv will now
+ invoke the stop action. Likewise, the Erlang emulator
+ (and it's po drivers) must not quit upon reception of the
+ CTRL_SHUTDOWN_EVENT event in th console control handler.
+ Thanks to Jan Kloetzke</p>
+ <p>
+ Own Id: OTP-10228</p>
+ </item>
+ <item>
+ <p>
+ Fix dtrace bug in file rename operation.</p>
+ <p>
+ Own Id: OTP-10234</p>
+ </item>
+ <item>
+ <p>
+ Fix bug in memory management of driver port data locks
+ (PDL). In some cases PDLs could be deallocated before
+ <c>ready_async</c> or <c>async_free</c> callback was
+ called.</p>
+ <p>
+ Own Id: OTP-10249</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Add port and suspend options to lock-counter
+ profiling. (Thanks to Rick Reed)</p>
+ <p>
+ Own Id: OTP-10051</p>
+ </item>
+ <item>
+ <p>
+ Latency when using the active_once option in gen_tcp
+ communication is reduced.</p>
+ <p>
+ Own Id: OTP-10055 Aux Id: sto139 </p>
+ </item>
+ <item>
+ <p>Remove bit8 option support from inet</p>
+ <p>
+ Own Id: OTP-10056</p>
+ </item>
+ <item>
+ <p>
+ The OS Pid of a port program is now available by calling
+ erlang:port_info(Port,os_pid), Thanks to Matthias Lang
+ for the original patch.</p>
+ <p>
+ Own Id: OTP-10057</p>
+ </item>
+ <item>
+ <p>
+ Fix openpty usage in run_erl. </p>
+ <p>
+ Reopening a slave file descriptor which was closed
+ earlier could lead to a misbehaving connection. This has
+ now been remedied.</p>
+ <p>
+ Own Id: OTP-10076</p>
+ </item>
+ <item>
+ <p>
+ Remove all code, documentation, options and diagnostic
+ functions which were related to the experimental hybrid
+ heap implementation.</p>
+ <p>
+ Own Id: OTP-10105</p>
+ </item>
+ <item>
+ <p>
+ Optimizations of memory deallocations.</p>
+ <p>
+ Own Id: OTP-10162 Aux Id: OTP-7775 </p>
+ </item>
+ <item>
+ <p>
+ Optimization of process locking.</p>
+ <p>
+ Own Id: OTP-10163</p>
+ </item>
+ <item>
+ <p>
+ Added a xcomp example file for powerpc-dso-linux-gnu</p>
+ <p>
+ Own Id: OTP-10198</p>
+ </item>
+ <item>
+ <p>
+ Detect when middle endian doubles are used by a platform
+ and account for it when decoding floats. (Thanks to Mike
+ Sperber)</p>
+ <p>
+ Own Id: OTP-10209</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 5.9.1.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ On Linux systems using heart (erl -heart) and a
+ HEAR_BEAT_TIMEOUT less than default, heart could fire
+ even though Erlang was running fine after approx 298 to
+ 497 days (depending on kernel config). This was due to
+ the behaviour of the times(2) system call. Usage of
+ times(2) is now replaced with clock_gettime(2) and the
+ CLOCK_MONOTONIC clock, resulting in a more stable
+ solution. The Erlang VM itself has used clock_gettime(2)
+ on linux since before R12B, so this only affects the
+ heart program.</p>
+ <p>
+ Own Id: OTP-10111 Aux Id: seq12075 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 5.9.1.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/erts/doc/src/ref_man.xml b/erts/doc/src/ref_man.xml
index 2042cf28bd..5c9938075c 100644
--- a/erts/doc/src/ref_man.xml
+++ b/erts/doc/src/ref_man.xml
@@ -49,7 +49,6 @@
<xi:include href="escript.xml"/>
<xi:include href="erlsrv.xml"/>
<xi:include href="start_erl.xml"/>
- <xi:include href="erl_set_memory_block.xml"/>
<xi:include href="run_erl.xml"/>
<xi:include href="start.xml"/>
<xi:include href="erl_driver.xml"/>
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 985ef72517..7e966c81bb 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -332,15 +332,11 @@ LIBS += $(THR_LIBS)
ifneq ($(findstring erts_internal_r, $(THR_LIBS)),erts_internal_r)
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-ERTS_INTERNAL_LIB=erts_internal
-else
ifneq ($(strip $(THR_LIB_NAME)),)
ERTS_INTERNAL_LIB=erts_internal_r
else
ERTS_INTERNAL_LIB=erts_internal
endif
-endif
LIBS += -l$(ERTS_INTERNAL_LIB)$(TYPEMARKER)
@@ -463,8 +459,6 @@ _create_dirs := $(shell mkdir -p $(CREATE_DIRS))
GENERATE =
HIPE_ASM =
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-else
ifdef HIPE_ENABLED
HIPE_ASM += $(TTF_DIR)/hipe_x86_asm.h \
$(TTF_DIR)/hipe_amd64_asm.h \
@@ -476,7 +470,6 @@ GENERATE += $(HIPE_ASM) \
$(TTF_DIR)/hipe_literals.h \
$(BINDIR)/hipe_mkliterals$(TF_MARKER)
endif
-endif
ifdef DTRACE_ENABLED
# global.h causes problems by including dtrace-wrapper.h which includes
@@ -616,11 +609,6 @@ ifdef PERFCTR_PATH
INCLUDES += -I$(PERFCTR_PATH)/usr.lib -I$(PERFCTR_PATH)/linux/include
endif
-# Need to include etc dir on VxWorks
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-INCLUDES += -I$(ERL_TOP)/erts/etc/vxworks
-endif
-
ifeq ($(TARGET),win32)
$(OBJDIR)/dll_sys.o: sys/$(ERLANG_OSTYPE)/sys.c
$(CC) $(CFLAGS) -DERL_RUN_SHARED_LIB=1 $(INCLUDES) -c $< -o $@
@@ -663,12 +651,6 @@ $(OBJDIR)/%.o: drivers/common/%.c
$(OBJDIR)/%.o: drivers/$(ERLANG_OSTYPE)/%.c
$(CC) $(CFLAGS) $(INCLUDES) -Idrivers/common -Idrivers/$(ERLANG_OSTYPE) -I../etc/$(ERLANG_OSTYPE) -c $< -o $@
-# VxWorks uses unix drivers too...
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-$(OBJDIR)/%.o: drivers/unix/%.c
- $(CC) $(CFLAGS) $(INCLUDES) -Idrivers/common -c $< -o $@
-endif
-
# ----------------------------------------------------------------------
# Specials
#
@@ -709,7 +691,9 @@ EMU_OBJS = \
$(OBJDIR)/beam_emu.o $(OBJDIR)/beam_opcodes.o \
$(OBJDIR)/beam_load.o $(OBJDIR)/beam_bif_load.o \
$(OBJDIR)/beam_debug.o $(OBJDIR)/beam_bp.o \
- $(OBJDIR)/beam_catches.o
+ $(OBJDIR)/beam_catches.o \
+ $(OBJDIR)/code_ix.o \
+ $(OBJDIR)/beam_ranges.o
RUN_OBJS = \
$(OBJDIR)/erl_pbifs.o $(OBJDIR)/benchmark.o \
@@ -779,12 +763,8 @@ OS_OBJS = \
$(OBJDIR)/gzio.o \
$(OBJDIR)/elib_memmove.o
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
- OS_OBJS += $(OBJDIR)/int64.o
-else
OS_OBJS += $(OBJDIR)/sys_float.o \
$(OBJDIR)/sys_time.o
-endif
DRV_OBJS = \
$(OBJDIR)/efile_drv.o \
$(OBJDIR)/inet_drv.o \
@@ -792,9 +772,7 @@ DRV_OBJS = \
$(OBJDIR)/ram_file_drv.o
endif
-ifneq ($(findstring vxworks,$(TARGET)),vxworks)
DRV_OBJS += $(OBJDIR)/ttsl_drv.o
-endif
ifeq ($(ERTS_ENABLE_KERNEL_POLL),yes)
OS_OBJS += $(OBJDIR)/erl_poll.kp.o \
@@ -932,31 +910,6 @@ $(OBJDIR)/hipe_arm.o: hipe/hipe_arm.c
# end of HiPE section
########################################
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-########################################
-# Extract what we need from libgcc.a
-########################################
-GCCLIBFLAGS=@GCCLIBFLAGS@
-STRIP=@STRIP@
-SYMPREFIX=@SYMPREFIX@
-
-NEEDFUNCTIONS=__divdi3 __moddi3 __udivdi3
-KEEPSYMS=$(NEEDFUNCTIONS:%=-K $(SYMPREFIX)%)
-
-$(OBJDIR)/int64.o: $(TARGET)/int64.c
- $(CC) -o $(OBJDIR)/int64tmp.o -c $(TARGET)/int64.c
- $(LD) -o $(OBJDIR)/int64.o $(OBJDIR)/int64tmp.o $(LDFLAGS) $(GCCLIBFLAGS)
- $(STRIP) $(KEEPSYMS) $(OBJDIR)/int64.o
-
-$(TARGET)/int64.c:
- echo 'void dummy(void); void dummy(void) {' > $(TARGET)/int64.c
- for x in $(NEEDFUNCTIONS); do echo 'extern void '$$x'();' \
- >> $(TARGET)/int64.c; done
- for x in $(NEEDFUNCTIONS); do echo $$x'();' >> $(TARGET)/int64.c; done
- echo '}' >> $(TARGET)/int64.c
-
-endif
-
# ----------------------------------------------------------------------
# The emulator itself
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index ada2d152b7..e8f8a04344 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-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2012. 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
@@ -37,25 +37,83 @@
static void set_default_trace_pattern(Eterm module);
static Eterm check_process_code(Process* rp, Module* modp);
-static void delete_code(Process *c_p, ErtsProcLocks c_p_locks, Module* modp);
-static void delete_export_references(Eterm module);
-static int purge_module(int module);
+static void delete_code(Module* modp);
static void decrement_refc(BeamInstr* code);
static int is_native(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 remove_from_address_table(BeamInstr* code);
-Eterm
-load_module_2(BIF_ALIST_2)
+
+
+BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1)
{
- Eterm reason;
- Eterm* hp;
- int sz;
- byte* code;
+ Module* modp;
Eterm res;
+ ErtsCodeIndex code_ix;
+
+ if (is_not_atom(BIF_ARG_1)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ code_ix = erts_active_code_ix();
+ if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) {
+ return am_undefined;
+ }
+ erts_rlock_old_code(code_ix);
+ res = ((modp->curr.code && is_native(modp->curr.code)) ||
+ (modp->old.code != 0 && is_native(modp->old.code))) ?
+ am_true : am_false;
+ erts_runlock_old_code(code_ix);
+ return res;
+}
+
+BIF_RETTYPE code_make_stub_module_3(BIF_ALIST_3)
+{
+ Module* modp;
+ Eterm res;
+
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ ERTS_BIF_YIELD3(bif_export[BIF_code_make_stub_module_3],
+ BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+ }
+
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_thr_progress_block();
+
+ modp = erts_get_module(BIF_ARG_1, erts_active_code_ix());
+
+ if (modp && modp->curr.num_breakpoints > 0) {
+ ASSERT(modp->curr.code != NULL);
+ erts_clear_module_break(modp);
+ ASSERT(modp->curr.num_breakpoints == 0);
+ }
+
+ erts_start_staging_code_ix();
+
+ res = erts_make_stub_module(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+
+ if (res == BIF_ARG_1) {
+ erts_end_staging_code_ix();
+ erts_commit_staging_code_ix();
+ }
+ else {
+ erts_abort_staging_code_ix();
+ }
+ erts_smp_thr_progress_unblock();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_release_code_write_permission();
+ return res;
+}
+
+BIF_RETTYPE
+prepare_loading_2(BIF_ALIST_2)
+{
byte* temp_alloc = NULL;
- struct LoaderState* stp;
+ byte* code;
+ Uint sz;
+ Binary* magic;
+ Eterm reason;
+ Eterm* hp;
+ Eterm res;
if (is_not_atom(BIF_ARG_1)) {
error:
@@ -65,108 +123,277 @@ load_module_2(BIF_ALIST_2)
if ((code = erts_get_aligned_binary_bytes(BIF_ARG_2, &temp_alloc)) == NULL) {
goto error;
}
- hp = HAlloc(BIF_P, 3);
- /*
- * Read the BEAM file and prepare the module for loading.
- */
- stp = erts_alloc_loader_state();
+ magic = erts_alloc_loader_state();
sz = binary_size(BIF_ARG_2);
- reason = erts_prepare_loading(stp, BIF_P, BIF_P->group_leader,
+ reason = erts_prepare_loading(magic, BIF_P, BIF_P->group_leader,
&BIF_ARG_1, code, sz);
erts_free_aligned_binary_bytes(temp_alloc);
if (reason != NIL) {
+ hp = HAlloc(BIF_P, 3);
res = TUPLE2(hp, am_error, reason);
BIF_RET(res);
}
+ hp = HAlloc(BIF_P, PROC_BIN_SIZE);
+ res = erts_mk_magic_binary_term(&hp, &MSO(BIF_P), magic);
+ erts_refc_dec(&magic->refc, 1);
+ BIF_RET(res);
+}
- /*
- * Stop all other processes and finish the loading of the module.
- */
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
+struct m {
+ Binary* code;
+ Eterm module;
+ Module* modp;
+ Uint exception;
+};
- reason = erts_finish_loading(stp, BIF_P, 0, &BIF_ARG_1);
- if (reason != NIL) {
- res = TUPLE2(hp, am_error, reason);
- } else {
- set_default_trace_pattern(BIF_ARG_1);
- res = TUPLE2(hp, am_module, BIF_ARG_1);
- }
+static Eterm staging_epilogue(Process* c_p, int, Eterm res, int, struct m*, int);
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
- BIF_RET(res);
+static Eterm
+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--;
+ }
+ return TUPLE2(hp, tag, res);
}
-BIF_RETTYPE purge_module_1(BIF_ALIST_1)
+
+BIF_RETTYPE
+finish_loading_1(BIF_ALIST_1)
{
- int purge_res;
+ int i;
+ int n;
+ struct m* p = NULL;
+ Uint exceptions;
+ Eterm res;
+ int is_blocking = 0;
+ int do_commit = 0;
- if (is_not_atom(BIF_ARG_1)) {
- BIF_ERROR(BIF_P, BADARG);
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ ERTS_BIF_YIELD1(bif_export[BIF_finish_loading_1], BIF_P, BIF_ARG_1);
}
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
+ /*
+ * Validate the argument before we start loading; it must be a
+ * proper list where each element is a magic binary containing
+ * prepared (not previously loaded) code.
+ *
+ * First count the number of elements and allocate an array
+ * to keep the elements in.
+ */
- erts_export_consolidate();
- purge_res = purge_module(atom_val(BIF_ARG_1));
+ n = list_length(BIF_ARG_1);
+ if (n == -1) {
+ ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG);
+ goto done;
+ }
+ p = erts_alloc(ERTS_ALC_T_LOADER_TMP, n*sizeof(struct m));
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ /*
+ * We now know that the argument is a proper list. Validate
+ * and collect the binaries into the array.
+ */
- if (purge_res < 0) {
- BIF_ERROR(BIF_P, BADARG);
+ for (i = 0; i < n; i++) {
+ Eterm* cons = list_val(BIF_ARG_1);
+ Eterm term = CAR(cons);
+ ProcBin* pb;
+
+ if (!ERTS_TERM_IS_MAGIC_BINARY(term)) {
+ ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG);
+ goto done;
+ }
+ 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;
+ }
+ BIF_ARG_1 = CDR(cons);
}
- BIF_RET(am_true);
-}
-BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1)
-{
- Module* modp;
+ /*
+ * 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 (is_not_atom(BIF_ARG_1)) {
- BIF_ERROR(BIF_P, BADARG);
+ if (n > 1) {
+ ERTS_BIF_PREP_ERROR(res, BIF_P, SYSTEM_LIMIT);
+ goto done;
}
- if ((modp = erts_get_module(BIF_ARG_1)) == NULL) {
- return am_undefined;
+
+ /*
+ * All types are correct. There cannot be a BADARG from now on.
+ * Before we can start loading, we must check whether any of
+ * the modules already has old code. To avoid a race, we must
+ * not allow other process to initiate a code loading operation
+ * from now on.
+ */
+
+ res = am_ok;
+ erts_start_staging_code_ix();
+
+ for (i = 0; i < n; i++) {
+ p[i].modp = erts_put_module(p[i].module);
+ }
+ for (i = 0; i < n; i++) {
+ if (p[i].modp->curr.num_breakpoints > 0 ||
+ p[i].modp->curr.num_traced_exports > 0 ||
+ erts_is_default_trace_enabled()) {
+ /* tracing involved, fallback with thread blocking */
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_thr_progress_block();
+ is_blocking = 1;
+ break;
+ }
}
- return ((modp->code && is_native(modp->code)) ||
- (modp->old_code != 0 && is_native(modp->old_code))) ?
- am_true : am_false;
-}
-BIF_RETTYPE code_make_stub_module_3(BIF_ALIST_3)
-{
- Eterm res;
+ if (is_blocking) {
+ for (i = 0; i < n; i++) {
+ if (p[i].modp->curr.num_breakpoints) {
+ erts_clear_module_break(p[i].modp);
+ ASSERT(p[i].modp->curr.num_breakpoints == 0);
+ }
+ }
+ }
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
+ exceptions = 0;
+ for (i = 0; i < n; i++) {
+ p[i].exception = 0;
+ if (p[i].modp->curr.code && p[i].modp->old.code) {
+ p[i].exception = 1;
+ exceptions++;
+ }
+ }
- erts_export_consolidate();
- res = erts_make_stub_module(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+ if (exceptions) {
+ res = exception_list(BIF_P, am_not_purged, p, exceptions);
+ } else {
+ /*
+ * Now we can load all code. This can't fail.
+ */
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
- return res;
+ exceptions = 0;
+ for (i = 0; i < n; i++) {
+ Eterm mod;
+ Eterm retval;
+
+ erts_refc_inc(&p[i].code->refc, 1);
+ retval = erts_finish_loading(p[i].code, BIF_P, 0, &mod);
+ ASSERT(retval == NIL || retval == am_on_load);
+ if (retval == am_on_load) {
+ p[i].exception = 1;
+ exceptions++;
+ }
+ }
+
+ /*
+ * Check whether any module has an on_load_handler.
+ */
+
+ if (exceptions) {
+ res = exception_list(BIF_P, am_on_load, p, exceptions);
+ }
+ do_commit = 1;
+ }
+
+done:
+ return staging_epilogue(BIF_P, do_commit, res, is_blocking, p, n);
+}
+
+static Eterm
+staging_epilogue(Process* c_p, int commit, Eterm res, int is_blocking,
+ struct m* loaded, int nloaded)
+{
+#ifdef ERTS_SMP
+ if (is_blocking || !commit)
+#endif
+ {
+ if (commit) {
+ erts_end_staging_code_ix();
+ erts_commit_staging_code_ix();
+ if (loaded) {
+ int i;
+ for (i=0; i < nloaded; i++) {
+ set_default_trace_pattern(loaded[i].module);
+ }
+ }
+ }
+ else {
+ erts_abort_staging_code_ix();
+ }
+ if (loaded) {
+ erts_free(ERTS_ALC_T_LOADER_TMP, loaded);
+ }
+ if (is_blocking) {
+ erts_smp_thr_progress_unblock();
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+ }
+ erts_release_code_write_permission();
+ return res;
+ }
+#ifdef ERTS_SMP
+ else {
+ ErtsThrPrgrVal later;
+ ASSERT(is_value(res));
+
+ if (loaded) {
+ erts_free(ERTS_ALC_T_LOADER_TMP, loaded);
+ }
+ erts_end_staging_code_ix();
+ /*
+ * Now we must wait for all schedulers to do a memory barrier before
+ * we can activate and let them access the new staged code. This allows
+ * schedulers to read active code_ix in a safe way while executing
+ * without any memory barriers at all.
+ */
+
+ later = erts_thr_progress_later(c_p->scheduler_data);
+ erts_thr_progress_wakeup(c_p->scheduler_data, later);
+ erts_notify_code_ix_activation(c_p, later);
+ erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
+ /*
+ * handle_code_ix_activation() will do the rest "later"
+ * and resume this process to return 'res'.
+ */
+ ERTS_BIF_YIELD_RETURN(c_p, res);
+ }
+#endif
}
BIF_RETTYPE
check_old_code_1(BIF_ALIST_1)
{
+ ErtsCodeIndex code_ix;
Module* modp;
+ Eterm res = am_false;
if (is_not_atom(BIF_ARG_1)) {
BIF_ERROR(BIF_P, BADARG);
}
- modp = erts_get_module(BIF_ARG_1);
- if (modp == NULL) { /* Doesn't exist. */
- BIF_RET(am_false);
- } else if (modp->old_code == NULL) { /* No old code. */
- BIF_RET(am_false);
+ code_ix = erts_active_code_ix();
+ modp = erts_get_module(BIF_ARG_1, code_ix);
+ if (modp != NULL) {
+ erts_rlock_old_code(code_ix);
+ if (modp->old.code != NULL) {
+ res = am_true;
+ }
+ erts_runlock_old_code(code_ix);
}
- BIF_RET(am_true);
+ BIF_RET(res);
}
Eterm
@@ -180,14 +407,20 @@ check_process_code_2(BIF_ALIST_2)
}
if (is_internal_pid(BIF_ARG_1)) {
Eterm res;
+ ErtsCodeIndex code_ix;
if (internal_pid_index(BIF_ARG_1) >= erts_max_processes)
goto error;
- modp = erts_get_module(BIF_ARG_2);
+ code_ix = erts_active_code_ix();
+ modp = erts_get_module(BIF_ARG_2, code_ix);
if (modp == NULL) { /* Doesn't exist. */
return am_false;
- } else if (modp->old_code == NULL) { /* No old code. */
+ }
+ erts_rlock_old_code(code_ix);
+ if (modp->old.code == NULL) { /* No old code. */
+ erts_runlock_old_code(code_ix);
return am_false;
}
+ erts_runlock_old_code(code_ix);
#ifdef ERTS_SMP
rp = erts_pid2proc_suspend(BIF_P, ERTS_PROC_LOCK_MAIN,
@@ -202,7 +435,14 @@ check_process_code_2(BIF_ALIST_2)
ERTS_BIF_YIELD2(bif_export[BIF_check_process_code_2], BIF_P,
BIF_ARG_1, BIF_ARG_2);
}
- res = check_process_code(rp, modp);
+ erts_rlock_old_code(code_ix);
+ if (modp->old.code != NULL) { /* must check again */
+ res = check_process_code(rp, modp);
+ }
+ else {
+ res = am_false;
+ }
+ erts_runlock_old_code(code_ix);
#ifdef ERTS_SMP
if (BIF_P != rp) {
erts_resume(rp, ERTS_PROC_LOCK_MAIN);
@@ -223,56 +463,71 @@ check_process_code_2(BIF_ALIST_2)
BIF_RETTYPE delete_module_1(BIF_ALIST_1)
{
- int res;
+ ErtsCodeIndex code_ix;
+ Module* modp;
+ int is_blocking = 0;
+ int success = 0;
+ Eterm res = NIL;
- if (is_not_atom(BIF_ARG_1))
- goto badarg;
+ if (is_not_atom(BIF_ARG_1)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ ERTS_BIF_YIELD1(bif_export[BIF_delete_module_1], BIF_P, BIF_ARG_1);
+ }
{
- Module *modp = erts_get_module(BIF_ARG_1);
+ erts_start_staging_code_ix();
+ 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 != 0) {
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
erts_dsprintf(dsbufp, "Module %T must be purged before loading\n",
BIF_ARG_1);
erts_send_error_to_logger(BIF_P->group_leader, dsbufp);
- res = am_badarg;
+ ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG);
}
else {
- delete_export_references(BIF_ARG_1);
- delete_code(BIF_P, 0, modp);
+ if (modp->curr.num_breakpoints > 0 ||
+ modp->curr.num_traced_exports > 0) {
+ /* we have tracing, retry single threaded */
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_thr_progress_block();
+ is_blocking = 1;
+ if (modp->curr.num_breakpoints) {
+ erts_clear_module_break(modp);
+ ASSERT(modp->curr.num_breakpoints == 0);
+ }
+ }
+ delete_code(modp);
res = am_true;
+ success = 1;
}
}
-
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
-
- if (res == am_badarg) {
- badarg:
- BIF_ERROR(BIF_P, BADARG);
- }
- BIF_RET(res);
+ return staging_epilogue(BIF_P, success, res, is_blocking, NULL, 0);
}
BIF_RETTYPE module_loaded_1(BIF_ALIST_1)
{
Module* modp;
+ ErtsCodeIndex code_ix;
+ Eterm res = am_false;
if (is_not_atom(BIF_ARG_1)) {
BIF_ERROR(BIF_P, BADARG);
}
- if ((modp = erts_get_module(BIF_ARG_1)) == NULL ||
- modp->code == NULL ||
- modp->code[MI_ON_LOAD_FUNCTION_PTR] != 0) {
- BIF_RET(am_false);
+ 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) {
+ res = am_true;
+ }
}
- BIF_RET(am_true);
+ BIF_RET(res);
}
BIF_RETTYPE pre_loaded_0(BIF_ALIST_0)
@@ -282,27 +537,28 @@ BIF_RETTYPE pre_loaded_0(BIF_ALIST_0)
BIF_RETTYPE loaded_0(BIF_ALIST_0)
{
+ ErtsCodeIndex code_ix = erts_active_code_ix();
+ Module* modp;
Eterm previous = NIL;
Eterm* hp;
int i;
int j = 0;
-
- for (i = 0; i < module_code_size(); i++) {
- if (module_code(i) != NULL &&
- ((module_code(i)->code_length != 0) ||
- (module_code(i)->old_code_length != 0))) {
+
+ 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))) {
j++;
}
}
if (j > 0) {
hp = HAlloc(BIF_P, j*2);
- for (i = 0; i < module_code_size(); i++) {
- if (module_code(i) != NULL &&
- ((module_code(i)->code_length != 0) ||
- (module_code(i)->old_code_length != 0))) {
- previous = CONS(hp, make_atom(module_code(i)->module),
- previous);
+ 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))) {
+ previous = CONS(hp, make_atom(modp->module), previous);
hp += 2;
}
}
@@ -312,54 +568,65 @@ BIF_RETTYPE loaded_0(BIF_ALIST_0)
BIF_RETTYPE call_on_load_function_1(BIF_ALIST_1)
{
- Module* modp = erts_get_module(BIF_ARG_1);
- Eterm on_load;
+ Module* modp = erts_get_module(BIF_ARG_1, erts_active_code_ix());
- if (!modp || modp->code == 0) {
- error:
- BIF_ERROR(BIF_P, BADARG);
+ if (modp && modp->curr.code) {
+ BIF_TRAP_CODE_PTR_0(BIF_P, modp->curr.code[MI_ON_LOAD_FUNCTION_PTR]);
}
- if ((on_load = modp->code[MI_ON_LOAD_FUNCTION_PTR]) == 0) {
- goto error;
+ else {
+ BIF_ERROR(BIF_P, BADARG);
}
- BIF_TRAP_CODE_PTR_0(BIF_P, on_load);
}
BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
{
- Module* modp = erts_get_module(BIF_ARG_1);
+ ErtsCodeIndex code_ix;
+ Module* modp;
Eterm on_load;
- if (!modp || modp->code == 0) {
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ ERTS_BIF_YIELD2(bif_export[BIF_finish_after_on_load_2],
+ BIF_P, BIF_ARG_1, BIF_ARG_2);
+ }
+
+ /* ToDo: Use code_ix staging instead of thread blocking */
+
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_thr_progress_block();
+
+ code_ix = erts_active_code_ix();
+ modp = erts_get_module(BIF_ARG_1, code_ix);
+
+ if (!modp || modp->curr.code == 0) {
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->code[MI_ON_LOAD_FUNCTION_PTR]) == 0) {
+ if ((on_load = modp->curr.code[MI_ON_LOAD_FUNCTION_PTR]) == 0) {
goto error;
}
if (BIF_ARG_2 != am_false && BIF_ARG_2 != am_true) {
goto error;
}
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
-
if (BIF_ARG_2 == am_true) {
int i;
/*
* The on_load function succeded. Fix up export entries.
*/
- for (i = 0; i < export_list_size(); i++) {
- Export *ep = export_list(i);
+ for (i = 0; i < export_list_size(code_ix); i++) {
+ Export *ep = export_list(i,code_ix);
if (ep != NULL &&
ep->code[0] == BIF_ARG_1 &&
ep->code[4] != 0) {
- ep->address = (void *) ep->code[4];
+ ep->addressv[code_ix] = (void *) ep->code[4];
ep->code[4] = 0;
}
}
- modp->code[MI_ON_LOAD_FUNCTION_PTR] = 0;
+ modp->curr.code[MI_ON_LOAD_FUNCTION_PTR] = 0;
set_default_trace_pattern(BIF_ARG_1);
} else if (BIF_ARG_2 == am_false) {
BeamInstr* code;
@@ -370,19 +637,21 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
* This is an combination of delete and purge. We purge
* the current code; the old code is not touched.
*/
- erts_total_code_size -= modp->code_length;
- code = modp->code;
- end = (BeamInstr *)((char *)code + modp->code_length);
+ 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->catches, code, modp->code_length);
+ beam_catches_delmod(modp->curr.catches, code, modp->curr.code_length,
+ erts_active_code_ix());
erts_free(ERTS_ALC_T_CODE, (void *) code);
- modp->code = NULL;
- modp->code_length = 0;
- modp->catches = BEAM_CATCHES_NIL;
- remove_from_address_table(code);
+ modp->curr.code = NULL;
+ modp->curr.code_length = 0;
+ modp->curr.catches = BEAM_CATCHES_NIL;
+ erts_remove_from_ranges(code);
}
erts_smp_thr_progress_unblock();
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_release_code_write_permission();
BIF_RET(am_true);
}
@@ -403,11 +672,11 @@ set_default_trace_pattern(Eterm module)
if (trace_pattern_is_on) {
Eterm mfa[1];
mfa[0] = module;
- (void) erts_set_trace_pattern(mfa, 1,
+ (void) erts_set_trace_pattern(0, mfa, 1,
match_spec,
meta_match_spec,
1, trace_pattern_flags,
- meta_tracer_pid);
+ meta_tracer_pid, 1);
}
}
@@ -427,10 +696,10 @@ check_process_code(Process* rp, Module* modp)
/*
* Pick up limits for the module.
*/
- start = modp->old_code;
- end = (BeamInstr *)((char *)start + modp->old_code_length);
+ start = modp->old.code;
+ end = (BeamInstr *)((char *)start + modp->old.code_length);
mod_start = (char *) start;
- mod_size = modp->old_code_length;
+ mod_size = modp->old.code_length;
/*
* Check if current instruction or continuation pointer points into module.
@@ -562,10 +831,10 @@ check_process_code(Process* rp, Module* modp)
done_gc = 1;
FLAGS(rp) |= F_NEED_FULLSWEEP;
(void) 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;
+ 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];
+ modp->old.code[MI_LITERALS_OFF_HEAP];
erts_garbage_collect_literals(rp, literals, lit_size, oh);
}
}
@@ -624,53 +893,82 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size)
#undef in_area
-
-static int
-purge_module(int module)
+BIF_RETTYPE purge_module_1(BIF_ALIST_1)
{
+ ErtsCodeIndex code_ix;
BeamInstr* code;
BeamInstr* end;
Module* modp;
+ int is_blocking = 0;
+ Eterm ret;
- /*
- * Correct module?
- */
-
- if ((modp = erts_get_module(make_atom(module))) == NULL) {
- return -2;
+ if (is_not_atom(BIF_ARG_1)) {
+ BIF_ERROR(BIF_P, BADARG);
}
- /*
- * Any code to purge?
- */
- if (modp->old_code == 0) {
- return -1;
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ ERTS_BIF_YIELD1(bif_export[BIF_purge_module_1], BIF_P, BIF_ARG_1);
}
+ code_ix = erts_active_code_ix();
+
/*
- * Unload any NIF library
+ * Correct module?
*/
- if (modp->old_nif != NULL) {
- erts_unload_nif(modp->old_nif);
- modp->old_nif = NULL;
+
+ if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) {
+ ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG);
}
+ else {
+ erts_rwlock_old_code(code_ix);
- /*
- * Remove the old code.
- */
- 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);
- 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;
- remove_from_address_table(code);
- return 0;
+ /*
+ * Any code to purge?
+ */
+ if (modp->old.code == 0) {
+ ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG);
+ }
+ 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 = 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_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();
+ return ret;
}
static void
@@ -690,92 +988,48 @@ decrement_refc(BeamInstr* code)
}
}
-static void
-remove_from_address_table(BeamInstr* code)
-{
- int i;
-
- for (i = 0; i < num_loaded_modules; i++) {
- if (modules[i].start == code) {
- num_loaded_modules--;
- while (i < num_loaded_modules) {
- modules[i] = modules[i+1];
- i++;
- }
- mid_module = &modules[num_loaded_modules/2];
- return;
- }
- }
- ASSERT(0); /* Not found? */
-}
-
-
/*
- * Move code from current to old.
+ * Move code from current to old and null all export entries for the module
*/
-static void
-delete_code(Process *c_p, ErtsProcLocks c_p_locks, Module* modp)
-{
-#ifdef ERTS_ENABLE_LOCK_CHECK
-#ifdef ERTS_SMP
- if (c_p && c_p_locks)
- erts_proc_lc_chk_only_proc_main(c_p);
- else
-#endif
- erts_lc_check_exact(NULL, 0);
-#endif
-
- /*
- * Clear breakpoints if any
- */
- if (modp->code != NULL && modp->code[MI_NUM_BREAKPOINTS] > 0) {
- if (c_p && c_p_locks)
- erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
- erts_clear_module_break(modp);
- modp->code[MI_NUM_BREAKPOINTS] = 0;
- erts_smp_thr_progress_unblock();
- if (c_p && c_p_locks)
- erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
- }
- modp->old_code = modp->code;
- modp->old_code_length = modp->code_length;
- modp->old_catches = modp->catches;
- modp->old_nif = modp->nif;
- modp->code = NULL;
- modp->code_length = 0;
- modp->catches = BEAM_CATCHES_NIL;
- modp->nif = NULL;
-}
-
-
-/* null all references on the export table for the module called with the
- atom index below */
-
static void
-delete_export_references(Eterm module)
+delete_code(Module* modp)
{
+ ErtsCodeIndex code_ix = erts_staging_code_ix();
+ Eterm module = make_atom(modp->module);
int i;
- ASSERT(is_atom(module));
-
- for (i = 0; i < export_list_size(); i++) {
- Export *ep = export_list(i);
+ for (i = 0; i < export_list_size(code_ix); i++) {
+ Export *ep = export_list(i, code_ix);
if (ep != NULL && (ep->code[0] == module)) {
- if (ep->address == ep->code+3 &&
- (ep->code[3] == (BeamInstr) em_apply_bif)) {
- 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(modp->curr.num_traced_exports > 0);
+ erts_clear_export_break(modp, ep->code+3);
+ }
+ else ASSERT(ep->code[3] == (BeamInstr) em_call_error_handler
+ || !erts_initialized);
}
- ep->address = ep->code+3;
+ ep->addressv[code_ix] = ep->code+3;
ep->code[3] = (BeamInstr) em_call_error_handler;
ep->code[4] = 0;
- MatchSetUnref(ep->match_prog_set);
- ep->match_prog_set = NULL;
}
}
+
+ ASSERT(modp->curr.num_breakpoints == 0);
+ ASSERT(modp->curr.num_traced_exports == 0);
+ modp->old = modp->curr;
+ modp->curr.code = NULL;
+ modp->curr.code_length = 0;
+ modp->curr.catches = BEAM_CATCHES_NIL;
+ modp->curr.nif = NULL;
}
-
+
Eterm
beam_make_current_old(Process *c_p, ErtsProcLocks c_p_locks, Eterm module)
@@ -787,11 +1041,10 @@ 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->code != NULL && modp->old_code != NULL) {
+ if (modp->curr.code != NULL && modp->old.code != NULL) {
return am_not_purged;
- } else if (modp->old_code == NULL) { /* Make the current version old. */
- delete_code(c_p, c_p_locks, modp);
- delete_export_references(module);
+ } else if (modp->old.code == NULL) { /* 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 d772bea02f..50d18b0347 100644
--- a/erts/emulator/beam/beam_bp.c
+++ b/erts/emulator/beam/beam_bp.c
@@ -44,67 +44,34 @@
#define ReAlloc(P, SIZ) erts_realloc(ERTS_ALC_T_BPD, (P), (SZ))
#define Free(P) erts_free(ERTS_ALC_T_BPD, (P))
-/*
-** Doubly linked ring macros
-*/
-
-#define BpInit(a,i) \
-do { \
- (a)->orig_instr = (i); \
- (a)->next = (a); \
- (a)->prev = (a); \
-} while (0)
-
-#define BpSpliceNext(a,b) \
-do { \
- register BpData *c = (a), *d = (b), *e; \
- e = c->next->prev; \
- c->next->prev = d->next->prev; \
- d->next->prev = e; \
- e = c->next; \
- c->next = d->next; \
- d->next = e; \
-} while (0)
-
-#define BpSplicePrev(a,b) \
-do { \
- register BpData *c = (a), *d = (b), *e; \
- e = c->prev->next; \
- c->prev->next = d->prev->next; \
- d->prev->next = e; \
- e = c->prev; \
- c->prev = d->prev; \
- d->prev = e; \
-} while (0)
-
-#ifdef DEBUG
-# define BpSingleton(a) ((a)->next == (a) && (a)->prev == (a))
+#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP)
+# define ERTS_SMP_REQ_PROC_MAIN_LOCK(P) \
+ if ((P)) erts_proc_lc_require_lock((P), ERTS_PROC_LOCK_MAIN)
+# define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P) \
+ if ((P)) erts_proc_lc_unrequire_lock((P), ERTS_PROC_LOCK_MAIN)
#else
-# define BpSingleton(a) ((a)->next == (a))
+# define ERTS_SMP_REQ_PROC_MAIN_LOCK(P)
+# define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P)
#endif
-#define BpInitAndSpliceNext(a,i,b) \
-do { \
- (a)->orig_instr = (i); \
- (a)->prev = (b); \
- (b)->next->prev = (a); \
- (a)->next = (b)->next; \
- (b)->next = (a); \
-} while (0)
+#define ERTS_BPF_LOCAL_TRACE 0x01
+#define ERTS_BPF_META_TRACE 0x02
+#define ERTS_BPF_COUNT 0x04
+#define ERTS_BPF_COUNT_ACTIVE 0x08
+#define ERTS_BPF_DEBUG 0x10
+#define ERTS_BPF_TIME_TRACE 0x20
+#define ERTS_BPF_TIME_TRACE_ACTIVE 0x40
+#define ERTS_BPF_GLOBAL_TRACE 0x80
-#define BpInitAndSplicePrev(a,i,b) \
-do { \
- (a)->orig_instr = (i); \
- (a)->next = (b); \
- (b)->prev->next = (a); \
- (a)->prev = (b)->prev; \
- (b)->prev = (a); \
-} while (0)
+#define ERTS_BPF_ALL 0xFF
+extern Eterm beam_return_to_trace[1]; /* OpCode(i_return_to_trace) */
+extern Eterm beam_return_trace[1]; /* OpCode(i_return_trace) */
+extern Eterm beam_exception_trace[1]; /* OpCode(i_exception_trace) */
+extern Eterm beam_return_time_trace[1]; /* OpCode(i_return_time_trace) */
-#define BREAK_IS_BIF (1)
-#define BREAK_IS_ERL (0)
-
+erts_smp_atomic32_t erts_active_bp_index;
+erts_smp_atomic32_t erts_staging_bp_index;
/* *************************************************************************
** Local prototypes
@@ -113,26 +80,30 @@ do { \
/*
** Helpers
*/
-
-static int set_break(Eterm mfa[3], int specified,
- Binary *match_spec, BeamInstr break_op,
- enum erts_break_op count_op, Eterm tracer_pid);
-static int set_module_break(Module *modp, Eterm mfa[3], int specified,
- Binary *match_spec, BeamInstr break_op,
- enum erts_break_op count_op, Eterm tracer_pid);
-static int set_function_break(Module *modp, BeamInstr *pc, int bif,
- Binary *match_spec, BeamInstr break_op,
- enum erts_break_op count_op, Eterm tracer_pid);
-
-static int clear_break(Eterm mfa[3], int specified,
- BeamInstr break_op);
-static int clear_module_break(Module *modp, Eterm mfa[3], int specified,
- BeamInstr break_op);
-static int clear_function_break(Module *modp, BeamInstr *pc, int bif,
- BeamInstr break_op);
-
-static BpData *is_break(BeamInstr *pc, BeamInstr break_op);
-static BpData *get_break(Process *p, BeamInstr *pc, BeamInstr break_op);
+static Eterm do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg,
+ int local, Binary* ms, Eterm tracer_pid);
+static void set_break(BpFunctions* f, Binary *match_spec, Uint break_flags,
+ enum erts_break_op count_op, Eterm tracer_pid);
+static void set_function_break(BeamInstr *pc,
+ Binary *match_spec,
+ Uint break_flags,
+ enum erts_break_op count_op,
+ Eterm tracer_pid);
+
+static void clear_break(BpFunctions* f, Uint break_flags);
+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_time_diff(bp_data_time_item_t *item,
+ process_breakpoint_time_t *pbt,
+ Uint ms, Uint s, Uint us);
+
+static void bp_meta_unref(BpMetaPid* bmp);
+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);
+static void uninstall_breakpoint(BeamInstr* pc);
/* bp_hash */
#define BP_TIME_ADD(pi0, pi1) \
@@ -152,240 +123,996 @@ static ERTS_INLINE bp_data_time_item_t * bp_hash_get(bp_time_hash_t *hash, bp_da
static ERTS_INLINE bp_data_time_item_t * bp_hash_put(bp_time_hash_t *hash, bp_data_time_item_t *sitem);
static void bp_hash_delete(bp_time_hash_t *hash);
-
/* *************************************************************************
** External interfaces
*/
-erts_smp_spinlock_t erts_bp_lock;
-
void
erts_bp_init(void) {
- erts_smp_spinlock_init(&erts_bp_lock, "breakpoints");
+ erts_smp_atomic32_init_nob(&erts_active_bp_index, 0);
+ erts_smp_atomic32_init_nob(&erts_staging_bp_index, 1);
}
-int
-erts_set_trace_break(Eterm mfa[3], int specified, Binary *match_spec,
- Eterm tracer_pid) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return set_break(mfa, specified, match_spec,
- (BeamInstr) BeamOp(op_i_trace_breakpoint), 0, tracer_pid);
+
+void
+erts_bp_match_functions(BpFunctions* f, Eterm mfa[3], int specified)
+{
+ ErtsCodeIndex code_ix = erts_active_code_ix();
+ Uint max_funcs = 0;
+ int current;
+ int max_modules = module_code_size(code_ix);
+ int num_modules = 0;
+ Module* modp;
+ Module** module;
+ Uint i;
+
+ module = (Module **) Alloc(max_modules*sizeof(Module *));
+ 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];
+ module[num_modules++] = modp;
+ }
+ }
+
+ 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;
+ BeamInstr* code;
+ Uint num_functions = (Uint) code_base[MI_NUM_FUNCTIONS];
+ Uint fi;
+
+ if (specified > 0) {
+ if (mfa[0] != make_atom(module[current]->module)) {
+ /* Wrong module name */
+ continue;
+ }
+ }
+
+ for (fi = 0; fi < num_functions; fi++) {
+ Eterm* pc;
+ int wi;
+
+ code = code_base[MI_FUNCTIONS+fi];
+ ASSERT(code[0] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
+ pc = code+5;
+ if (erts_is_native_break(pc)) {
+ continue;
+ }
+ if (is_nil(code[3])) { /* Ignore BIF stub */
+ continue;
+ }
+ for (wi = 0;
+ wi < specified && (Eterm) code[2+wi] == mfa[wi];
+ wi++) {
+ /* Empty loop body */
+ }
+ if (wi == specified) {
+ /* Store match */
+ f->matching[i].pc = pc;
+ f->matching[i].mod = module[current];
+ i++;
+ }
+ }
+ }
+ f->matched = i;
+ Free(module);
}
-int
-erts_set_mtrace_break(Eterm mfa[3], int specified, Binary *match_spec,
- Eterm tracer_pid) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return set_break(mfa, specified, match_spec,
- (BeamInstr) BeamOp(op_i_mtrace_breakpoint), 0, tracer_pid);
+void
+erts_bp_match_export(BpFunctions* f, Eterm mfa[3], int specified)
+{
+ ErtsCodeIndex code_ix = erts_active_code_ix();
+ int i;
+ int num_exps = export_list_size(code_ix);
+ int ne;
+
+ f->matching = (BpFunction *) Alloc(num_exps*sizeof(BpFunction));
+ ne = 0;
+ for (i = 0; i < num_exps; i++) {
+ Export* ep = export_list(i, code_ix);
+ BeamInstr* pc;
+ int j;
+
+ for (j = 0; j < specified && mfa[j] == ep->code[j]; j++) {
+ /* Empty loop body */
+ }
+ if (j < specified) {
+ continue;
+ }
+ pc = ep->code+3;
+ if (ep->addressv[code_ix] == pc) {
+ if ((*pc == (BeamInstr) em_apply_bif ||
+ *pc == (BeamInstr) em_call_error_handler)) {
+ continue;
+ }
+ ASSERT(*pc == (BeamInstr) BeamOp(op_i_generic_breakpoint));
+ } else if (erts_is_native_break(ep->addressv[code_ix])) {
+ continue;
+ }
+
+ f->matching[ne].pc = pc;
+ f->matching[ne].mod = erts_get_module(ep->code[0], code_ix);
+ ne++;
+
+ }
+ f->matched = ne;
}
-/* set breakpoint data for on exported bif entry */
+void
+erts_bp_free_matched_functions(BpFunctions* f)
+{
+ Free(f->matching);
+}
void
-erts_set_mtrace_bif(BeamInstr *pc, Binary *match_spec, Eterm tracer_pid) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- set_function_break(NULL, pc, BREAK_IS_BIF, match_spec, (BeamInstr) BeamOp(op_i_mtrace_breakpoint), 0, tracer_pid);
+erts_consolidate_bp_data(BpFunctions* f, int local)
+{
+ BpFunction* fs = f->matching;
+ Uint i;
+ Uint n = f->matched;
+
+ ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked());
+
+ for (i = 0; i < n; i++) {
+ consolidate_bp_data(fs[i].mod, fs[i].pc, local);
+ }
}
-void erts_set_time_trace_bif(BeamInstr *pc, enum erts_break_op count_op) {
- set_function_break(NULL, pc, BREAK_IS_BIF, NULL, (BeamInstr) BeamOp(op_i_time_breakpoint), count_op, NIL);
+void
+erts_consolidate_bif_bp_data(void)
+{
+ int i;
+
+ ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked());
+ for (i = 0; i < BIF_SIZE; i++) {
+ Export *ep = bif_export[i];
+ consolidate_bp_data(0, ep->code+3, 0);
+ }
}
-void erts_clear_time_trace_bif(BeamInstr *pc) {
- clear_function_break(NULL, pc, BREAK_IS_BIF, (BeamInstr) BeamOp(op_i_time_breakpoint));
+static void
+consolidate_bp_data(Module* modp, BeamInstr* pc, int local)
+{
+ GenericBp* g = (GenericBp *) pc[-4];
+ GenericBpData* src;
+ GenericBpData* dst;
+ Uint flags;
+
+ if (g == 0) {
+ return;
+ }
+
+ src = &g->data[erts_active_bp_ix()];
+ dst = &g->data[erts_staging_bp_ix()];
+
+ /*
+ * The contents of the staging area may be out of date.
+ * Decrement all reference pointers.
+ */
+
+ flags = dst->flags;
+ if (flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) {
+ MatchSetUnref(dst->local_ms);
+ }
+ if (flags & ERTS_BPF_META_TRACE) {
+ bp_meta_unref(dst->meta_pid);
+ MatchSetUnref(dst->meta_ms);
+ }
+ if (flags & ERTS_BPF_COUNT) {
+ bp_count_unref(dst->count);
+ }
+ if (flags & ERTS_BPF_TIME_TRACE) {
+ bp_time_unref(dst->time);
+ }
+
+ /*
+ * If all flags are zero, deallocate all breakpoint data.
+ */
+
+ flags = dst->flags = src->flags;
+ if (flags == 0) {
+ if (modp) {
+ if (local) {
+ modp->curr.num_breakpoints--;
+ } else {
+ modp->curr.num_traced_exports--;
+ }
+ ASSERT(modp->curr.num_breakpoints >= 0);
+ ASSERT(modp->curr.num_traced_exports >= 0);
+ ASSERT(*pc != (BeamInstr) BeamOp(op_i_generic_breakpoint));
+ }
+ pc[-4] = 0;
+ Free(g);
+ return;
+ }
+
+ /*
+ * Copy the active data to the staging area (making it ready
+ * for the next time it will be used).
+ */
+
+ if (flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) {
+ dst->local_ms = src->local_ms;
+ 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_ms = src->meta_ms;
+ MatchSetRef(dst->meta_ms);
+ }
+ if (flags & ERTS_BPF_COUNT) {
+ dst->count = src->count;
+ erts_refc_inc(&dst->count->refc, 1);
+ }
+ if (flags & ERTS_BPF_TIME_TRACE) {
+ dst->time = src->time;
+ erts_refc_inc(&dst->time->refc, 1);
+ ASSERT(dst->time->hash);
+ }
}
-int
-erts_set_debug_break(Eterm mfa[3], int specified) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return set_break(mfa, specified, NULL,
- (BeamInstr) BeamOp(op_i_debug_breakpoint), 0, NIL);
+void
+erts_commit_staged_bp(void)
+{
+ ErtsBpIndex staging = erts_staging_bp_ix();
+ ErtsBpIndex active = erts_active_bp_ix();
+
+ erts_smp_atomic32_set_nob(&erts_active_bp_index, staging);
+ erts_smp_atomic32_set_nob(&erts_staging_bp_index, active);
}
-int
-erts_set_count_break(Eterm mfa[3], int specified, enum erts_break_op count_op) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return set_break(mfa, specified, NULL,
- (BeamInstr) BeamOp(op_i_count_breakpoint), count_op, NIL);
+void
+erts_install_breakpoints(BpFunctions* f)
+{
+ Uint i;
+ Uint n = f->matched;
+ BeamInstr br = (BeamInstr) BeamOp(op_i_generic_breakpoint);
+
+ for (i = 0; i < n; i++) {
+ BeamInstr* pc = f->matching[i].pc;
+ GenericBp* g = (GenericBp *) pc[-4];
+ if (*pc != br && g) {
+ Module* modp = f->matching[i].mod;
+
+ /*
+ * The breakpoint must be disabled in the active data
+ * (it will enabled later by switching bp indices),
+ * and enabled in the staging data.
+ */
+ ASSERT(g->data[erts_active_bp_ix()].flags == 0);
+ ASSERT(g->data[erts_staging_bp_ix()].flags != 0);
+
+ /*
+ * The following write is not protected by any lock. We
+ * assume that the hardware guarantees that a write of an
+ * aligned word-size (or half-word) writes is atomic
+ * (i.e. that other processes executing this code will not
+ * see a half pointer).
+ */
+ *pc = br;
+ modp->curr.num_breakpoints++;
+ }
+ }
}
-int
-erts_set_time_break(Eterm mfa[3], int specified, enum erts_break_op count_op) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return set_break(mfa, specified, NULL,
- (BeamInstr) BeamOp(op_i_time_breakpoint), count_op, NIL);
+void
+erts_uninstall_breakpoints(BpFunctions* f)
+{
+ Uint i;
+ Uint n = f->matched;
+
+ for (i = 0; i < n; i++) {
+ BeamInstr* pc = f->matching[i].pc;
+ uninstall_breakpoint(pc);
+ }
}
-int
-erts_clear_trace_break(Eterm mfa[3], int specified) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return clear_break(mfa, specified,
- (BeamInstr) BeamOp(op_i_trace_breakpoint));
+static void
+uninstall_breakpoint(BeamInstr* pc)
+{
+ if (*pc == (BeamInstr) BeamOp(op_i_generic_breakpoint)) {
+ GenericBp* g = (GenericBp *) pc[-4];
+ if (g->data[erts_active_bp_ix()].flags == 0) {
+ /*
+ * The following write is not protected by any lock. We
+ * assume that the hardware guarantees that a write of an
+ * aligned word-size (or half-word) writes is atomic
+ * (i.e. that other processes executing this code will not
+ * see a half pointer).
+ */
+ *pc = g->orig_instr;
+ }
+ }
}
-int
-erts_clear_mtrace_break(Eterm mfa[3], int specified) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return clear_break(mfa, specified,
- (BeamInstr) BeamOp(op_i_mtrace_breakpoint));
+void
+erts_set_trace_break(BpFunctions* f, Binary *match_spec)
+{
+ set_break(f, match_spec, ERTS_BPF_LOCAL_TRACE, 0, am_true);
}
void
-erts_clear_mtrace_bif(BeamInstr *pc) {
- clear_function_break(NULL, pc, BREAK_IS_BIF, (BeamInstr) BeamOp(op_i_mtrace_breakpoint));
+erts_set_mtrace_break(BpFunctions* f, Binary *match_spec, Eterm tracer_pid)
+{
+ set_break(f, match_spec, ERTS_BPF_META_TRACE, 0, tracer_pid);
}
-int
-erts_clear_debug_break(Eterm mfa[3], int specified) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return clear_break(mfa, specified,
- (BeamInstr) BeamOp(op_i_debug_breakpoint));
+void
+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);
}
-int
-erts_clear_count_break(Eterm mfa[3], int specified) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return clear_break(mfa, specified,
- (BeamInstr) BeamOp(op_i_count_breakpoint));
+void
+erts_set_mtrace_bif(BeamInstr *pc, Binary *match_spec, Eterm tracer_pid)
+{
+ set_function_break(pc, match_spec, ERTS_BPF_META_TRACE, 0, tracer_pid);
}
-int
-erts_clear_time_break(Eterm mfa[3], int specified) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return clear_break(mfa, specified,
- (BeamInstr) BeamOp(op_i_time_breakpoint));
+void
+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);
}
-int
-erts_clear_break(Eterm mfa[3], int specified) {
+void
+erts_clear_time_trace_bif(BeamInstr *pc) {
+ clear_function_break(pc, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE);
+}
+
+void
+erts_set_debug_break(BpFunctions* f) {
+ set_break(f, NULL, ERTS_BPF_DEBUG, 0, 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);
+}
+
+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);
+}
+
+void
+erts_clear_trace_break(BpFunctions* f)
+{
+ clear_break(f, ERTS_BPF_LOCAL_TRACE);
+}
+
+void
+erts_clear_call_trace_bif(BeamInstr *pc, int local)
+{
+ GenericBp* g = (GenericBp *) pc[-4];
+
+ if (g) {
+ Uint flags = local ? ERTS_BPF_LOCAL_TRACE : ERTS_BPF_GLOBAL_TRACE;
+ if (g->data[erts_staging_bp_ix()].flags & flags) {
+ clear_function_break(pc, flags);
+ }
+ }
+}
+
+void
+erts_clear_mtrace_break(BpFunctions* f)
+{
+ clear_break(f, ERTS_BPF_META_TRACE);
+}
+
+void
+erts_clear_mtrace_bif(BeamInstr *pc)
+{
+ clear_function_break(pc, ERTS_BPF_META_TRACE);
+}
+
+void
+erts_clear_debug_break(BpFunctions* f)
+{
ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return clear_break(mfa, specified, 0);
+ clear_break(f, ERTS_BPF_DEBUG);
+}
+
+void
+erts_clear_count_break(BpFunctions* f)
+{
+ clear_break(f, ERTS_BPF_COUNT|ERTS_BPF_COUNT_ACTIVE);
+}
+
+void
+erts_clear_time_break(BpFunctions* f)
+{
+ clear_break(f, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE);
+}
+
+void
+erts_clear_all_breaks(BpFunctions* f)
+{
+ clear_break(f, ERTS_BPF_ALL);
}
int
erts_clear_module_break(Module *modp) {
+ BeamInstr** code_base;
+ Uint n;
+ Uint i;
+
ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
ASSERT(modp);
- return clear_module_break(modp, NULL, 0, 0);
+ code_base = (BeamInstr **) modp->curr.code;
+ if (code_base == NULL) {
+ return 0;
+ }
+ n = (Uint) code_base[MI_NUM_FUNCTIONS];
+ for (i = 0; i < n; ++i) {
+ BeamInstr* pc;
+
+ pc = code_base[MI_FUNCTIONS+i] + 5;
+ if (erts_is_native_break(pc)) {
+ continue;
+ }
+ clear_function_break(pc, ERTS_BPF_ALL);
+ }
+
+ erts_commit_staged_bp();
+
+ for (i = 0; i < n; ++i) {
+ BeamInstr* pc;
+
+ pc = code_base[MI_FUNCTIONS+i] + 5;
+ if (erts_is_native_break(pc)) {
+ continue;
+ }
+ uninstall_breakpoint(pc);
+ consolidate_bp_data(modp, pc, 1);
+ ASSERT(pc[-4] == 0);
+ }
+ return n;
}
-int
-erts_clear_function_break(Module *modp, BeamInstr *pc) {
+void
+erts_clear_export_break(Module* modp, BeamInstr* pc)
+{
ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- ASSERT(modp);
- return clear_function_break(modp, pc, BREAK_IS_ERL, 0);
+
+ clear_function_break(pc, ERTS_BPF_ALL);
+ erts_commit_staged_bp();
+ *pc = (BeamInstr) 0;
+ consolidate_bp_data(modp, pc, 0);
+ ASSERT(pc[-4] == 0);
}
+BeamInstr
+erts_generic_breakpoint(Process* c_p, BeamInstr* I, Eterm* reg)
+{
+ GenericBp* g;
+ GenericBpData* bp;
+ Uint bp_flags;
+ ErtsBpIndex ix = erts_active_bp_ix();
+
+ g = (GenericBp *) I[-4];
+ bp = &g->data[ix];
+ bp_flags = bp->flags;
+ ASSERT((bp_flags & ~ERTS_BPF_ALL) == 0);
+ if (bp_flags & (ERTS_BPF_LOCAL_TRACE|
+ ERTS_BPF_GLOBAL_TRACE|
+ ERTS_BPF_TIME_TRACE_ACTIVE) &&
+ !IS_TRACED_FL(c_p, F_TRACE_CALLS)) {
+ bp_flags &= ~(ERTS_BPF_LOCAL_TRACE|
+ ERTS_BPF_GLOBAL_TRACE|
+ ERTS_BPF_TIME_TRACE|
+ ERTS_BPF_TIME_TRACE_ACTIVE);
+ if (bp_flags == 0) { /* Quick exit */
+ return g->orig_instr;
+ }
+ }
+
+ 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);
+ } else if (bp_flags & ERTS_BPF_GLOBAL_TRACE) {
+ (void) do_call_trace(c_p, I, reg, 0, bp->local_ms, am_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);
+ }
+ }
+
+ if (bp_flags & ERTS_BPF_COUNT_ACTIVE) {
+ erts_smp_atomic_inc_nob(&bp->count->acount);
+ }
+
+ if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE) {
+ Eterm w;
+ erts_trace_time_call(c_p, I, bp->time);
+ w = (BeamInstr) *c_p->cp;
+ if (! (w == (BeamInstr) BeamOp(op_i_return_time_trace) ||
+ w == (BeamInstr) BeamOp(op_return_trace) ||
+ w == (BeamInstr) BeamOp(op_i_return_to_trace)) ) {
+ Eterm* E = c_p->stop;
+ ASSERT(c_p->htop <= E && E <= c_p->hend);
+ if (E - 2 < c_p->htop) {
+ (void) erts_garbage_collect(c_p, 2, reg, I[-1]);
+ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
+ }
+ E = c_p->stop;
+ ASSERT(c_p->htop <= E && E <= c_p->hend);
+
+ E -= 2;
+ E[0] = make_cp(I);
+ E[1] = make_cp(c_p->cp); /* original return address */
+ c_p->cp = beam_return_time_trace;
+ c_p->stop = E;
+ }
+ }
+
+ if (bp_flags & ERTS_BPF_DEBUG) {
+ return (BeamInstr) BeamOp(op_i_debug_breakpoint);
+ } else {
+ return g->orig_instr;
+ }
+}
/*
- * SMP NOTE: Process p may have become exiting on return!
+ * Entry point called by the trace wrap functions in erl_bif_wrap.c
+ *
+ * The trace wrap functions are themselves called through the export
+ * entries instead of the original BIF functions.
*/
-BeamInstr
-erts_trace_break(Process *p, BeamInstr *pc, Eterm *args,
- Uint32 *ret_flags, Eterm *tracer_pid) {
- Eterm tpid1, tpid2;
- BpData **bds = (BpData **) (pc)[-4];
- BpDataTrace *bdt = NULL;
+Eterm
+erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
+{
+ Eterm result;
+ Eterm (*func)(Process*, Eterm*, BeamInstr*);
+ Export* ep = bif_export[bif_index];
+ Uint32 flags = 0, flags_meta = 0;
+ Eterm meta_tracer_pid = NIL;
+ int applying = (I == &(ep->code[3])); /* Yup, the apply code for a bif
+ * is actually in the
+ * export entry */
+ BeamInstr *cp = p->cp;
+ GenericBp* g;
+ GenericBpData* bp;
+ Uint bp_flags = 0;
+
+ ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
+
+ g = (GenericBp *) ep->fake_op_func_info_for_hipe[1];
+ if (g) {
+ bp = &g->data[erts_active_bp_ix()];
+ bp_flags = bp->flags;
+ }
- ASSERT(bds);
- ASSERT(pc[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
- bdt = (BpDataTrace *) bds[bp_sched2ix_proc(p)];
- ASSERT(bdt);
- bdt = (BpDataTrace *) bdt->next;
- ASSERT(bdt);
- ASSERT(ret_flags);
- ASSERT(tracer_pid);
-
- ErtsSmpBPLock(bdt);
- tpid1 = tpid2 = bdt->tracer_pid;
- ErtsSmpBPUnlock(bdt);
-
- *ret_flags = erts_call_trace(p, pc-3/*mfa*/, bdt->match_spec, args,
- 1, &tpid2);
- *tracer_pid = tpid2;
- if (tpid1 != tpid2) {
- ErtsSmpBPLock(bdt);
- bdt->tracer_pid = tpid2;
- ErtsSmpBPUnlock(bdt);
- }
- bds[bp_sched2ix_proc(p)] = (BpData *) bdt;
- return bdt->orig_instr;
+ /*
+ * Make continuation pointer OK, it is not during direct BIF calls,
+ * but it is correct during apply of bif.
+ */
+ if (!applying) {
+ p->cp = I;
+ }
+ if (bp_flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE) &&
+ IS_TRACED_FL(p, F_TRACE_CALLS)) {
+ int local = !!(bp_flags & ERTS_BPF_LOCAL_TRACE);
+ flags = erts_call_trace(p, ep->code, bp->local_ms, args,
+ local, &p->tracer_proc);
+ }
+ if (bp_flags & ERTS_BPF_META_TRACE) {
+ Eterm tpid1, tpid2;
+
+ tpid1 = tpid2 =
+ (Eterm) erts_smp_atomic_read_nob(&bp->meta_pid->pid);
+ 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);
+ }
+ }
+ if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE &&
+ IS_TRACED_FL(p, F_TRACE_CALLS)) {
+ BeamInstr *pc = (BeamInstr *)ep->code+3;
+ erts_trace_time_call(p, pc, bp->time);
+ }
+
+ /* Restore original continuation pointer (if changed). */
+ p->cp = cp;
+
+ func = bif_table[bif_index].f;
+
+ result = func(p, args, I);
+
+ if (applying && (flags & MATCH_SET_RETURN_TO_TRACE)) {
+ BeamInstr i_return_trace = beam_return_trace[0];
+ BeamInstr i_return_to_trace = beam_return_to_trace[0];
+ BeamInstr i_return_time_trace = beam_return_time_trace[0];
+ Eterm *cpp;
+ /* Maybe advance cp to skip trace stack frames */
+ for (cpp = p->stop; ; cp = cp_val(*cpp++)) {
+ if (*cp == i_return_trace) {
+ /* Skip stack frame variables */
+ while (is_not_CP(*cpp)) cpp++;
+ cpp += 2; /* Skip return_trace parameters */
+ } else if (*cp == i_return_time_trace) {
+ /* Skip stack frame variables */
+ while (is_not_CP(*cpp)) cpp++;
+ cpp += 1; /* Skip return_time_trace parameters */
+ } else if (*cp == i_return_to_trace) {
+ /* A return_to trace message is going to be generated
+ * by normal means, so we do not have to.
+ */
+ cp = NULL;
+ break;
+ } else break;
+ }
+ }
+
+ /* Try to get these in the order
+ * they usually appear in normal code... */
+ if (is_non_value(result)) {
+ Uint reason = p->freason;
+ if (reason != TRAP) {
+ Eterm class;
+ Eterm value = p->fvalue;
+ DeclareTmpHeapNoproc(nocatch,3);
+ UseTmpHeapNoproc(3);
+ /* Expand error value like in handle_error() */
+ if (reason & EXF_ARGLIST) {
+ Eterm *tp;
+ ASSERT(is_tuple(value));
+ tp = tuple_val(value);
+ value = tp[1];
+ }
+ if ((reason & EXF_THROWN) && (p->catches <= 0)) {
+ value = TUPLE2(nocatch, am_nocatch, value);
+ reason = EXC_ERROR;
+ }
+ /* Note: expand_error_value() could theoretically
+ * allocate on the heap, but not for any error
+ * returned by a BIF, and it would do no harm,
+ * just be annoying.
+ */
+ value = expand_error_value(p, reason, value);
+ class = exception_tag[GET_EXC_CLASS(reason)];
+
+ if (flags_meta & MATCH_SET_EXCEPTION_TRACE) {
+ erts_trace_exception(p, ep->code, class, value,
+ &meta_tracer_pid);
+ }
+ if (flags & MATCH_SET_EXCEPTION_TRACE) {
+ erts_trace_exception(p, ep->code, class, value,
+ &p->tracer_proc);
+ }
+ if ((flags & MATCH_SET_RETURN_TO_TRACE) && p->catches > 0) {
+ /* can only happen if(local)*/
+ Eterm *ptr = p->stop;
+ ASSERT(is_CP(*ptr));
+ ASSERT(ptr <= STACK_START(p));
+ /* Search the nearest stack frame for a catch */
+ while (++ptr < STACK_START(p)) {
+ if (is_CP(*ptr)) break;
+ if (is_catch(*ptr)) {
+ if (applying) {
+ /* Apply of BIF, cp is in calling function */
+ if (cp) erts_trace_return_to(p, cp);
+ } else {
+ /* Direct bif call, I points into
+ * calling function */
+ erts_trace_return_to(p, I);
+ }
+ }
+ }
+ }
+ UnUseTmpHeapNoproc(3);
+ if ((flags_meta|flags) & MATCH_SET_EXCEPTION_TRACE) {
+ erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
+ p->trace_flags |= F_EXCEPTION_TRACE;
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
+ }
+ }
+ } else {
+ if (flags_meta & MATCH_SET_RX_TRACE) {
+ erts_trace_return(p, ep->code, result, &meta_tracer_pid);
+ }
+ /* MATCH_SET_RETURN_TO_TRACE cannot occur if(meta) */
+ if (flags & MATCH_SET_RX_TRACE) {
+ erts_trace_return(p, ep->code, result, &p->tracer_proc);
+ }
+ if (flags & MATCH_SET_RETURN_TO_TRACE) {
+ /* can only happen if(local)*/
+ if (applying) {
+ /* Apply of BIF, cp is in calling function */
+ if (cp) erts_trace_return_to(p, cp);
+ } else {
+ /* Direct bif call, I points into calling function */
+ erts_trace_return_to(p, I);
+ }
+ }
+ }
+ ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
+ return result;
+}
+
+static Eterm
+do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg,
+ int local, Binary* ms, Eterm tracer_pid)
+{
+ Eterm* cpp;
+ int return_to_trace = 0;
+ BeamInstr w;
+ BeamInstr *cp_save;
+ Uint32 flags;
+ Uint need = 0;
+ Eterm* E = c_p->stop;
+
+ w = *c_p->cp;
+ if (w == (BeamInstr) BeamOp(op_return_trace)) {
+ cpp = &E[2];
+ } else if (w == (BeamInstr) BeamOp(op_i_return_to_trace)) {
+ return_to_trace = 1;
+ cpp = &E[0];
+ } else if (w == (BeamInstr) BeamOp(op_i_return_time_trace)) {
+ cpp = &E[0];
+ } else {
+ cpp = NULL;
+ }
+ if (cpp) {
+ for (;;) {
+ BeamInstr w = *cp_val(*cpp);
+ if (w == (BeamInstr) BeamOp(op_return_trace)) {
+ cpp += 3;
+ } else if (w == (BeamInstr) BeamOp(op_i_return_to_trace)) {
+ return_to_trace = 1;
+ cpp += 1;
+ } else if (w == (BeamInstr) BeamOp(op_i_return_time_trace)) {
+ cpp += 2;
+ } else {
+ break;
+ }
+ }
+ cp_save = c_p->cp;
+ c_p->cp = (BeamInstr *) cp_val(*cpp);
+ 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);
+ ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
+ if (cpp) {
+ c_p->cp = cp_save;
+ }
+
+ ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ if ((flags & MATCH_SET_RETURN_TO_TRACE) && !return_to_trace) {
+ need += 1;
+ }
+ if (flags & MATCH_SET_RX_TRACE) {
+ need += 3;
+ }
+ if (need) {
+ ASSERT(c_p->htop <= E && E <= c_p->hend);
+ if (E - need < c_p->htop) {
+ (void) erts_garbage_collect(c_p, need, reg, I[-1]);
+ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
+ E = c_p->stop;
+ }
+ }
+ if (flags & MATCH_SET_RETURN_TO_TRACE && !return_to_trace) {
+ E -= 1;
+ ASSERT(c_p->htop <= E && E <= c_p->hend);
+ E[0] = make_cp(c_p->cp);
+ c_p->cp = (BeamInstr *) beam_return_to_trace;
+ }
+ if (flags & MATCH_SET_RX_TRACE) {
+ E -= 3;
+ 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));
+ E[2] = make_cp(c_p->cp);
+ E[1] = tracer_pid;
+ E[0] = make_cp(I - 3); /* We ARE at the beginning of an
+ instruction,
+ the funcinfo is above i. */
+ c_p->cp = (flags & MATCH_SET_EXCEPTION_TRACE) ?
+ beam_exception_trace : beam_return_trace;
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ c_p->trace_flags |= F_EXCEPTION_TRACE;
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ }
+ c_p->stop = E;
+ return tracer_pid;
}
+void
+erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt)
+{
+ Uint ms,s,us;
+ process_breakpoint_time_t *pbt = NULL;
+ bp_data_time_item_t sitem, *item = NULL;
+ bp_time_hash_t *h = NULL;
+ BpDataTime *pbdt = NULL;
+ ASSERT(c_p);
+ ASSERT(erts_smp_atomic32_read_acqb(&c_p->state) & ERTS_PSFLG_RUNNING);
-/*
- * SMP NOTE: Process p may have become exiting on return!
- */
-Uint32
-erts_bif_mtrace(Process *p, BeamInstr *pc, Eterm *args, int local,
- Eterm *tracer_pid) {
- BpData **bds = (BpData **) (pc)[-4];
- BpDataTrace *bdt = NULL;
+ /* get previous timestamp and breakpoint
+ * from the process psd */
+ pbt = ERTS_PROC_GET_CALL_TIME(c_p);
+ get_sys_now(&ms, &s, &us);
- ASSERT(tracer_pid);
- if (bds) {
- Eterm tpid1, tpid2;
- Uint32 flags;
- bdt = (BpDataTrace *)bds[bp_sched2ix_proc(p)];
+ /* get pbt
+ * timestamp = t0
+ * lookup bdt from code
+ * set ts0 to pbt
+ * add call count here?
+ */
+ 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);
+ } else {
+ ASSERT(pbt->pc);
+ /* add time to previous code */
+ bp_time_diff(&sitem, pbt, ms, s, us);
+ sitem.pid = c_p->id;
+ sitem.count = 0;
- ErtsSmpBPLock(bdt);
- tpid1 = tpid2 = bdt->tracer_pid;
- ErtsSmpBPUnlock(bdt);
+ /* previous breakpoint */
+ pbdt = get_time_break(pbt->pc);
- flags = erts_call_trace(p, pc-3/*mfa*/, bdt->match_spec, args,
- local, &tpid2);
- *tracer_pid = tpid2;
- if (tpid1 != tpid2) {
- ErtsSmpBPLock(bdt);
- bdt->tracer_pid = tpid2;
- ErtsSmpBPUnlock(bdt);
+ /* if null then the breakpoint was removed */
+ if (pbdt) {
+ h = &(pbdt->hash[bp_sched2ix_proc(c_p)]);
+
+ ASSERT(h);
+ ASSERT(h->item);
+
+ item = bp_hash_get(h, &sitem);
+ if (!item) {
+ item = bp_hash_put(h, &sitem);
+ } else {
+ BP_TIME_ADD(item, &sitem);
+ }
}
- return flags;
}
- *tracer_pid = NIL;
- return 0;
+
+ /* Add count to this code */
+ sitem.pid = c_p->id;
+ sitem.count = 1;
+ sitem.s_time = 0;
+ sitem.us_time = 0;
+
+ /* this breakpoint */
+ ASSERT(bdt);
+ h = &(bdt->hash[bp_sched2ix_proc(c_p)]);
+
+ ASSERT(h);
+ ASSERT(h->item);
+
+ item = bp_hash_get(h, &sitem);
+ if (!item) {
+ item = bp_hash_put(h, &sitem);
+ } else {
+ BP_TIME_ADD(item, &sitem);
+ }
+
+ pbt->pc = I;
+ pbt->ms = ms;
+ pbt->s = s;
+ pbt->us = us;
}
+void
+erts_trace_time_return(Process *p, BeamInstr *pc)
+{
+ Uint ms,s,us;
+ process_breakpoint_time_t *pbt = NULL;
+ bp_data_time_item_t sitem, *item = NULL;
+ bp_time_hash_t *h = NULL;
+ BpDataTime *pbdt = NULL;
+
+ ASSERT(p);
+ ASSERT(erts_smp_atomic32_read_acqb(&p->state) & ERTS_PSFLG_RUNNING);
+ /* get previous timestamp and breakpoint
+ * from the process psd */
+
+ pbt = ERTS_PROC_GET_CALL_TIME(p);
+ get_sys_now(&ms,&s,&us);
+
+ /* get pbt
+ * lookup bdt from code
+ * timestamp = t1
+ * get ts0 from pbt
+ * get item from bdt->hash[bp_hash(p->id)]
+ * ack diff (t1, t0) to item
+ */
+
+ if (pbt) {
+ /* might have been removed due to
+ * trace_pattern(false)
+ */
+ ASSERT(pbt->pc);
+
+ bp_time_diff(&sitem, pbt, ms, s, us);
+ sitem.pid = p->id;
+ sitem.count = 0;
+
+ /* previous breakpoint */
+ pbdt = get_time_break(pbt->pc);
+
+ /* beware, the trace_pattern might have been removed */
+ if (pbdt) {
+ h = &(pbdt->hash[bp_sched2ix_proc(p)]);
+
+ ASSERT(h);
+ ASSERT(h->item);
+
+ item = bp_hash_get(h, &sitem);
+ if (!item) {
+ item = bp_hash_put(h, &sitem);
+ } else {
+ BP_TIME_ADD(item, &sitem);
+ }
+ }
+
+ pbt->pc = pc;
+ pbt->ms = ms;
+ pbt->s = s;
+ pbt->us = us;
+ }
+}
int
-erts_is_trace_break(BeamInstr *pc, Binary **match_spec_ret, Eterm *tracer_pid_ret) {
- BpDataTrace *bdt =
- (BpDataTrace *) is_break(pc, (BeamInstr) BeamOp(op_i_trace_breakpoint));
-
- if (bdt) {
+erts_is_trace_break(BeamInstr *pc, Binary **match_spec_ret, int local)
+{
+ Uint flags = local ? ERTS_BPF_LOCAL_TRACE : ERTS_BPF_GLOBAL_TRACE;
+ GenericBpData* bp = check_break(pc, flags);
+
+ if (bp) {
if (match_spec_ret) {
- *match_spec_ret = bdt->match_spec;
+ *match_spec_ret = bp->local_ms;
}
- if (tracer_pid_ret) {
- ErtsSmpBPLock(bdt);
- *tracer_pid_ret = bdt->tracer_pid;
- ErtsSmpBPUnlock(bdt);
- }
- return !0;
+ return 1;
}
return 0;
}
int
-erts_is_mtrace_break(BeamInstr *pc, Binary **match_spec_ret, Eterm *tracer_pid_ret) {
- BpDataTrace *bdt =
- (BpDataTrace *) is_break(pc, (BeamInstr) BeamOp(op_i_mtrace_breakpoint));
+erts_is_mtrace_break(BeamInstr *pc, Binary **match_spec_ret,
+ Eterm *tracer_pid_ret)
+{
+ GenericBpData* bp = check_break(pc, ERTS_BPF_META_TRACE);
- if (bdt) {
+ if (bp) {
if (match_spec_ret) {
- *match_spec_ret = bdt->match_spec;
+ *match_spec_ret = bp->meta_ms;
}
if (tracer_pid_ret) {
- ErtsSmpBPLock(bdt);
- *tracer_pid_ret = bdt->tracer_pid;
- ErtsSmpBPUnlock(bdt);
+ *tracer_pid_ret =
+ (Eterm) erts_smp_atomic_read_nob(&bp->meta_pid->pid);
}
- return !0;
+ return 1;
}
return 0;
}
@@ -402,15 +1129,15 @@ erts_is_native_break(BeamInstr *pc) {
}
int
-erts_is_count_break(BeamInstr *pc, Sint *count_ret) {
- BpDataCount *bdc =
- (BpDataCount *) is_break(pc, (BeamInstr) BeamOp(op_i_count_breakpoint));
+erts_is_count_break(BeamInstr *pc, Uint *count_ret)
+{
+ GenericBpData* bp = check_break(pc, ERTS_BPF_COUNT);
- if (bdc) {
+ if (bp) {
if (count_ret) {
- *count_ret = (Sint) erts_smp_atomic_read_nob(&bdc->acount);
+ *count_ret = (Uint) erts_smp_atomic_read_nob(&bp->count->acount);
}
- return !0;
+ return 1;
}
return 0;
}
@@ -421,7 +1148,7 @@ int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *retval) {
Uint size;
Eterm *hp, t;
bp_data_time_item_t *item = NULL;
- BpDataTime *bdt = (BpDataTime *) is_break(pc, (BeamInstr) BeamOp(op_i_time_breakpoint));
+ BpDataTime *bdt = get_time_break(pc);
if (bdt) {
if (retval) {
@@ -464,7 +1191,7 @@ int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *retval) {
}
bp_hash_delete(&hash);
}
- return !0;
+ return 1;
}
return 0;
@@ -478,15 +1205,16 @@ erts_find_local_func(Eterm mfa[3]) {
BeamInstr* code_ptr;
Uint i,n;
- if ((modp = erts_get_module(mfa[0])) == NULL)
+ if ((modp = erts_get_module(mfa[0], erts_active_code_ix())) == NULL)
return NULL;
- if ((code_base = (BeamInstr **) modp->code) == NULL)
+ if ((code_base = (BeamInstr **) modp->curr.code) == NULL)
return NULL;
n = (BeamInstr) code_base[MI_NUM_FUNCTIONS];
for (i = 0; i < n; ++i) {
code_ptr = code_base[MI_FUNCTIONS+i];
ASSERT(((BeamInstr) BeamOp(op_i_func_info_IaaI)) == code_ptr[0]);
- ASSERT(mfa[0] == ((Eterm) code_ptr[2]));
+ ASSERT(mfa[0] == ((Eterm) code_ptr[2]) ||
+ is_nil((Eterm) code_ptr[2]));
if (mfa[1] == ((Eterm) code_ptr[3]) &&
((BeamInstr) mfa[2]) == code_ptr[4]) {
return code_ptr + 5;
@@ -654,7 +1382,7 @@ void erts_schedule_time_break(Process *p, Uint schedule) {
* the previous breakpoint.
*/
- pbdt = (BpDataTime *) get_break(p, pbt->pc, (BeamInstr) BeamOp(op_i_time_breakpoint));
+ pbdt = get_time_break(pbt->pc);
if (pbdt) {
get_sys_now(&ms,&s,&us);
bp_time_diff(&sitem, pbt, ms, s, us);
@@ -692,669 +1420,259 @@ void erts_schedule_time_break(Process *p, Uint schedule) {
} /* pbt */
}
-/* call_time breakpoint
- * Accumulated times are added to the previous bp,
- * not the current one. The current one is saved
- * for future reference.
- * The previous breakpoint is stored in the process it self, the psd.
- * We do not need to store in a stack frame.
- * There is no need for locking, each thread has its own
- * area in each bp to save data.
- * Since we need to diffrentiate between processes for each bp,
- * every bp has a hash (per thread) to process-bp statistics.
- * - egil
- */
-
-void erts_trace_time_break(Process *p, BeamInstr *pc, BpDataTime *bdt, Uint type) {
- Uint ms,s,us;
- process_breakpoint_time_t *pbt = NULL;
- bp_data_time_item_t sitem, *item = NULL;
- bp_time_hash_t *h = NULL;
- BpDataTime *pbdt = NULL;
-
- ASSERT(p);
- ASSERT(p->status == P_RUNNING);
-
- /* get previous timestamp and breakpoint
- * from the process psd */
-
- pbt = ERTS_PROC_GET_CALL_TIME(p);
- get_sys_now(&ms,&s,&us);
-
- switch(type) {
- /* get pbt
- * timestamp = t0
- * lookup bdt from code
- * set ts0 to pbt
- * add call count here?
- */
- case ERTS_BP_CALL_TIME_CALL:
- case ERTS_BP_CALL_TIME_TAIL_CALL:
-
- if (pbt) {
- ASSERT(pbt->pc);
- /* add time to previous code */
- bp_time_diff(&sitem, pbt, ms, s, us);
- sitem.pid = p->id;
- sitem.count = 0;
-
- /* previous breakpoint */
- pbdt = (BpDataTime *) get_break(p, pbt->pc, (BeamInstr) BeamOp(op_i_time_breakpoint));
-
- /* if null then the breakpoint was removed */
- if (pbdt) {
- h = &(pbdt->hash[bp_sched2ix_proc(p)]);
-
- ASSERT(h);
- ASSERT(h->item);
-
- item = bp_hash_get(h, &sitem);
- if (!item) {
- item = bp_hash_put(h, &sitem);
- } else {
- BP_TIME_ADD(item, &sitem);
- }
- }
-
- } else {
- /* first call of process to instrumented function */
- pbt = Alloc(sizeof(process_breakpoint_time_t));
- (void *) ERTS_PROC_SET_CALL_TIME(p, ERTS_PROC_LOCK_MAIN, pbt);
- }
- /* add count to this code */
- sitem.pid = p->id;
- sitem.count = 1;
- sitem.s_time = 0;
- sitem.us_time = 0;
-
- /* this breakpoint */
- ASSERT(bdt);
- h = &(bdt->hash[bp_sched2ix_proc(p)]);
-
- ASSERT(h);
- ASSERT(h->item);
-
- item = bp_hash_get(h, &sitem);
- if (!item) {
- item = bp_hash_put(h, &sitem);
- } else {
- BP_TIME_ADD(item, &sitem);
- }
-
- pbt->pc = pc;
- pbt->ms = ms;
- pbt->s = s;
- pbt->us = us;
- break;
-
- case ERTS_BP_CALL_TIME_RETURN:
- /* get pbt
- * lookup bdt from code
- * timestamp = t1
- * get ts0 from pbt
- * get item from bdt->hash[bp_hash(p->id)]
- * ack diff (t1, t0) to item
- */
-
- if(pbt) {
- /* might have been removed due to
- * trace_pattern(false)
- */
- ASSERT(pbt->pc);
-
- bp_time_diff(&sitem, pbt, ms, s, us);
- sitem.pid = p->id;
- sitem.count = 0;
-
- /* previous breakpoint */
- pbdt = (BpDataTime *) get_break(p, pbt->pc, (BeamInstr) BeamOp(op_i_time_breakpoint));
-
- /* beware, the trace_pattern might have been removed */
- if (pbdt) {
- h = &(pbdt->hash[bp_sched2ix_proc(p)]);
-
- ASSERT(h);
- ASSERT(h->item);
-
- item = bp_hash_get(h, &sitem);
- if (!item) {
- item = bp_hash_put(h, &sitem);
- } else {
- BP_TIME_ADD(item, &sitem);
- }
- }
-
- pbt->pc = pc;
- pbt->ms = ms;
- pbt->s = s;
- pbt->us = us;
- }
- break;
- default :
- ASSERT(0);
- /* will never happen */
- break;
- }
-}
-
-
/* *************************************************************************
** Local helpers
*/
-static int set_break(Eterm mfa[3], int specified,
- Binary *match_spec, BeamInstr break_op,
- enum erts_break_op count_op, Eterm tracer_pid)
+static void
+set_break(BpFunctions* f, Binary *match_spec, Uint break_flags,
+ enum erts_break_op count_op, Eterm tracer_pid)
{
- Module *modp;
- int num_processed = 0;
- if (!specified) {
- /* Find and process all modules in the system... */
- int current;
- int last = module_code_size();
- for (current = 0; current < last; current++) {
- modp = module_code(current);
- ASSERT(modp != NULL);
- num_processed +=
- set_module_break(modp, mfa, specified,
- match_spec, break_op, count_op,
- tracer_pid);
- }
- } else {
- /* Process a single module */
- if ((modp = erts_get_module(mfa[0])) != NULL) {
- num_processed +=
- set_module_break(modp, mfa, specified,
- match_spec, break_op, count_op,
- tracer_pid);
- }
- }
- return num_processed;
-}
-
-static int set_module_break(Module *modp, Eterm mfa[3], int specified,
- Binary *match_spec, BeamInstr break_op,
- enum erts_break_op count_op, Eterm tracer_pid) {
- BeamInstr** code_base;
- BeamInstr* code_ptr;
- int num_processed = 0;
- Uint i,n;
+ Uint i;
+ Uint n;
- ASSERT(break_op);
- ASSERT(modp);
- code_base = (BeamInstr **) modp->code;
- if (code_base == NULL) {
- return 0;
+ n = f->matched;
+ for (i = 0; i < n; i++) {
+ BeamInstr* pc = f->matching[i].pc;
+ set_function_break(pc, match_spec, break_flags,
+ count_op, tracer_pid);
}
- n = (BeamInstr) code_base[MI_NUM_FUNCTIONS];
- for (i = 0; i < n; ++i) {
- code_ptr = code_base[MI_FUNCTIONS+i];
- ASSERT(code_ptr[0] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
- if ((specified < 2 || mfa[1] == ((Eterm) code_ptr[3])) &&
- (specified < 3 || ((int) mfa[2]) == ((int) code_ptr[4]))) {
- BeamInstr *pc = code_ptr+5;
-
- num_processed +=
- set_function_break(modp, pc, BREAK_IS_ERL, match_spec,
- break_op, count_op, tracer_pid);
- }
- }
- return num_processed;
}
-static int set_function_break(Module *modp, BeamInstr *pc, int bif,
- Binary *match_spec, BeamInstr break_op,
- enum erts_break_op count_op, Eterm tracer_pid) {
-
- BeamInstr **code_base = NULL;
- BpData *bd, **r, ***rs;
- size_t size;
- Uint ix = 0;
-
- if (bif == BREAK_IS_ERL) {
- code_base = (BeamInstr **)modp->code;
- ASSERT(code_base);
- ASSERT(code_base <= (BeamInstr **)pc);
- ASSERT((BeamInstr **)pc < code_base + (modp->code_length/sizeof(BeamInstr *)));
- } else {
- ASSERT(*pc == (BeamInstr) em_apply_bif);
- ASSERT(modp == NULL);
+static void
+set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
+ enum erts_break_op count_op, Eterm tracer_pid)
+{
+ GenericBp* g;
+ GenericBpData* bp;
+ Uint common;
+ ErtsBpIndex ix = erts_staging_bp_ix();
+
+ ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked());
+ g = (GenericBp *) pc[-4];
+ if (g == 0) {
+ int i;
+ if (count_op == erts_break_reset || count_op == erts_break_stop) {
+ /* Do not insert a new breakpoint */
+ return;
+ }
+ g = Alloc(sizeof(GenericBp));
+ g->orig_instr = *pc;
+ for (i = 0; i < ERTS_NUM_BP_IX; i++) {
+ g->data[i].flags = 0;
+ }
+ pc[-4] = (BeamInstr) g;
}
+ bp = &g->data[ix];
/*
- * Currently no trace support for native code.
+ * If we are changing an existing breakpoint, clean up old data.
*/
- if (erts_is_native_break(pc)) {
- return 0;
- }
- /* Do not allow two breakpoints of the same kind */
- if ( (bd = is_break(pc, break_op))) {
- if (break_op == (BeamInstr) BeamOp(op_i_trace_breakpoint)
- || break_op == (BeamInstr) BeamOp(op_i_mtrace_breakpoint)) {
-
- BpDataTrace *bdt = (BpDataTrace *) bd;
- Binary *old_match_spec;
-
- /* Update match spec and tracer */
- MatchSetRef(match_spec);
- ErtsSmpBPLock(bdt);
- old_match_spec = bdt->match_spec;
- bdt->match_spec = match_spec;
- bdt->tracer_pid = tracer_pid;
- ErtsSmpBPUnlock(bdt);
- MatchSetUnref(old_match_spec);
- } else {
- BpDataCount *bdc = (BpDataCount *) bd;
- erts_aint_t count = 0;
- erts_aint_t res = 0;
-
- ASSERT(! match_spec);
- ASSERT(is_nil(tracer_pid));
-
- if (break_op == (BeamInstr) BeamOp(op_i_count_breakpoint)) {
- if (count_op == erts_break_stop) {
- count = erts_smp_atomic_read_nob(&bdc->acount);
- if (count >= 0) {
- while(1) {
- res = erts_smp_atomic_cmpxchg_nob(&bdc->acount, -count - 1, count);
- if ((res == count) || count < 0) break;
- count = res;
- }
- }
- } else {
- /* Reset call counter */
- erts_smp_atomic_set_nob(&bdc->acount, 0);
- }
-
- } else if (break_op == (BeamInstr) BeamOp(op_i_time_breakpoint)) {
- BpDataTime *bdt = (BpDataTime *) bd;
- Uint i = 0;
-
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
-
- if (count_op == erts_break_stop) {
- bdt->pause = 1;
- } else {
- bdt->pause = 0;
- for (i = 0; i < bdt->n; i++) {
- bp_hash_delete(&(bdt->hash[i]));
- bp_hash_init(&(bdt->hash[i]), 32);
- }
- }
- } else {
- ASSERT (! count_op);
- }
- }
- return 1;
- }
- if (break_op == (BeamInstr) BeamOp(op_i_trace_breakpoint) ||
- break_op == (BeamInstr) BeamOp(op_i_mtrace_breakpoint)) {
- size = sizeof(BpDataTrace);
- } else {
- ASSERT(! match_spec);
- ASSERT(is_nil(tracer_pid));
- if (break_op == (BeamInstr) BeamOp(op_i_count_breakpoint)) {
- if (count_op == erts_break_reset || count_op == erts_break_stop) {
- /* Do not insert a new breakpoint */
- return 1;
- }
- size = sizeof(BpDataCount);
- } else if (break_op == (BeamInstr) BeamOp(op_i_time_breakpoint)) {
- if (count_op == erts_break_reset || count_op == erts_break_stop) {
- /* Do not insert a new breakpoint */
- return 1;
- }
- size = sizeof(BpDataTime);
+ common = break_flags & bp->flags;
+ if (common & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) {
+ MatchSetUnref(bp->local_ms);
+ } else if (common & ERTS_BPF_META_TRACE) {
+ MatchSetUnref(bp->meta_ms);
+ bp_meta_unref(bp->meta_pid);
+ } else if (common & ERTS_BPF_COUNT) {
+ if (count_op == erts_break_stop) {
+ bp->flags &= ~ERTS_BPF_COUNT_ACTIVE;
} else {
- ASSERT(! count_op);
- ASSERT(break_op == (BeamInstr) BeamOp(op_i_debug_breakpoint));
- size = sizeof(BpDataDebug);
+ bp->flags |= ERTS_BPF_COUNT_ACTIVE;
+ erts_smp_atomic_set_nob(&bp->count->acount, 0);
}
- }
- rs = (BpData ***) (pc-4);
- if (! *rs) {
- size_t ssize = sizeof(BeamInstr) * erts_no_schedulers;
- *rs = (BpData **) Alloc(ssize);
- sys_memzero(*rs, ssize);
- }
-
- r = &((*rs)[0]);
-
- if (! *r) {
- ASSERT(*pc != (BeamInstr) BeamOp(op_i_trace_breakpoint));
- ASSERT(*pc != (BeamInstr) BeamOp(op_i_mtrace_breakpoint));
- ASSERT(*pc != (BeamInstr) BeamOp(op_i_debug_breakpoint));
- ASSERT(*pc != (BeamInstr) BeamOp(op_i_count_breakpoint));
- ASSERT(*pc != (BeamInstr) BeamOp(op_i_time_breakpoint));
- /* First breakpoint; create singleton ring */
- bd = Alloc(size);
- BpInit(bd, *pc);
- *r = bd;
- if (bif == BREAK_IS_ERL) {
- *pc = break_op;
- }
- } else {
- ASSERT(*pc == (BeamInstr) BeamOp(op_i_trace_breakpoint) ||
- *pc == (BeamInstr) BeamOp(op_i_mtrace_breakpoint) ||
- *pc == (BeamInstr) BeamOp(op_i_debug_breakpoint) ||
- *pc == (BeamInstr) BeamOp(op_i_time_breakpoint) ||
- *pc == (BeamInstr) BeamOp(op_i_count_breakpoint) ||
- *pc == (BeamInstr) em_apply_bif);
- if (*pc == (BeamInstr) BeamOp(op_i_debug_breakpoint)) {
- /* Debug bp must be last, so if it is also first;
- * it must be singleton. */
- ASSERT(BpSingleton(*r));
- /* Insert new bp first in the ring, i.e second to last. */
- bd = Alloc(size);
- BpInitAndSpliceNext(bd, *pc, *r);
- if (bif == BREAK_IS_ERL) {
- *pc = break_op;
- }
- } else if ((*r)->prev->orig_instr
- == (BeamInstr) BeamOp(op_i_debug_breakpoint)) {
- /* Debug bp last in the ring; insert new second to last. */
- bd = Alloc(size);
- BpInitAndSplicePrev(bd, (*r)->prev->orig_instr, *r);
- (*r)->prev->orig_instr = break_op;
+ ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
+ return;
+ } else if (common & ERTS_BPF_TIME_TRACE) {
+ BpDataTime* bdt = bp->time;
+ Uint i = 0;
+
+ if (count_op == erts_break_stop) {
+ bp->flags &= ~ERTS_BPF_TIME_TRACE_ACTIVE;
} else {
- /* Just insert last in the ring */
- bd = Alloc(size);
- BpInitAndSpliceNext(bd, (*r)->orig_instr, *r);
- (*r)->orig_instr = break_op;
- *r = bd;
+ bp->flags |= ERTS_BPF_TIME_TRACE_ACTIVE;
+ for (i = 0; i < bdt->n; i++) {
+ bp_hash_delete(&(bdt->hash[i]));
+ bp_hash_init(&(bdt->hash[i]), 32);
+ }
}
- }
- for (ix = 1; ix < erts_no_schedulers; ++ix) {
- (*rs)[ix] = (*rs)[0];
+ ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
+ return;
}
- bd->this_instr = break_op;
- /* Init the bp type specific data */
- if (break_op == (BeamInstr) BeamOp(op_i_trace_breakpoint) ||
- break_op == (BeamInstr) BeamOp(op_i_mtrace_breakpoint)) {
-
- BpDataTrace *bdt = (BpDataTrace *) bd;
-
- MatchSetRef(match_spec);
- bdt->match_spec = match_spec;
- bdt->tracer_pid = tracer_pid;
- } else if (break_op == (BeamInstr) BeamOp(op_i_time_breakpoint)) {
- BpDataTime *bdt = (BpDataTime *) bd;
- Uint i = 0;
-
- bdt->pause = 0;
- bdt->n = erts_no_schedulers;
- bdt->hash = Alloc(sizeof(bp_time_hash_t)*(bdt->n));
+ /*
+ * Initialize the new breakpoint data.
+ */
+ if (break_flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) {
+ MatchSetRef(match_spec);
+ bp->local_ms = match_spec;
+ } else if (break_flags & ERTS_BPF_META_TRACE) {
+ BpMetaPid* bmp;
+ 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;
+ } 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_atomic_init_nob(&bcp->acount, 0);
+ bp->count = bcp;
+ } else if (break_flags & ERTS_BPF_TIME_TRACE) {
+ BpDataTime* bdt;
+ int i;
+
+ ASSERT((bp->flags & ERTS_BPF_TIME_TRACE) == 0);
+ bdt = Alloc(sizeof(BpDataTime));
+ erts_refc_init(&bdt->refc, 1);
+ bdt->n = erts_no_schedulers;
+ bdt->hash = Alloc(sizeof(bp_time_hash_t)*(bdt->n));
for (i = 0; i < bdt->n; i++) {
bp_hash_init(&(bdt->hash[i]), 32);
}
- } else if (break_op == (BeamInstr) BeamOp(op_i_count_breakpoint)) {
- BpDataCount *bdc = (BpDataCount *) bd;
- erts_smp_atomic_init_nob(&bdc->acount, 0);
+ bp->time = bdt;
}
- if (bif == BREAK_IS_ERL) {
- ++(*(BeamInstr*)&code_base[MI_NUM_BREAKPOINTS]);
+ bp->flags |= break_flags;
+ ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
+}
+
+static void
+clear_break(BpFunctions* f, Uint break_flags)
+{
+ Uint i;
+ Uint n;
+
+ n = f->matched;
+ for (i = 0; i < n; i++) {
+ BeamInstr* pc = f->matching[i].pc;
+ clear_function_break(pc, break_flags);
}
- return 1;
}
-static int clear_break(Eterm mfa[3], int specified, BeamInstr break_op)
+static int
+clear_function_break(BeamInstr *pc, Uint break_flags)
{
- int num_processed = 0;
- Module *modp;
+ GenericBp* g;
+ GenericBpData* bp;
+ Uint common;
+ ErtsBpIndex ix = erts_staging_bp_ix();
- if (!specified) {
- /* Iterate over all modules */
- int current;
- int last = module_code_size();
+ ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked());
- for (current = 0; current < last; current++) {
- modp = module_code(current);
- ASSERT(modp != NULL);
- num_processed += clear_module_break(modp, mfa, specified, break_op);
- }
- } else {
- /* Process a single module */
- if ((modp = erts_get_module(mfa[0])) != NULL) {
- num_processed +=
- clear_module_break(modp, mfa, specified, break_op);
- }
+ if ((g = (GenericBp *) pc[-4]) == 0) {
+ return 1;
}
- return num_processed;
-}
-static int clear_module_break(Module *m, Eterm mfa[3], int specified,
- BeamInstr break_op) {
- BeamInstr** code_base;
- BeamInstr* code_ptr;
- int num_processed = 0;
- Uint i;
- BeamInstr n;
-
- ASSERT(m);
- code_base = (BeamInstr **) m->code;
- if (code_base == NULL) {
- return 0;
+ bp = &g->data[ix];
+ ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
+ common = bp->flags & break_flags;
+ bp->flags &= ~break_flags;
+ if (common & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) {
+ MatchSetUnref(bp->local_ms);
}
- n = (BeamInstr) code_base[MI_NUM_FUNCTIONS];
- for (i = 0; i < n; ++i) {
- code_ptr = code_base[MI_FUNCTIONS+i];
- if ((specified < 2 || mfa[1] == ((Eterm) code_ptr[3])) &&
- (specified < 3 || ((int) mfa[2]) == ((int) code_ptr[4]))) {
- BeamInstr *pc = code_ptr + 5;
-
- num_processed +=
- clear_function_break(m, pc, BREAK_IS_ERL, break_op);
- }
+ if (common & ERTS_BPF_META_TRACE) {
+ MatchSetUnref(bp->meta_ms);
+ }
+ if (common & ERTS_BPF_COUNT) {
+ ASSERT((bp->flags & ERTS_BPF_COUNT_ACTIVE) == 0);
+ bp_count_unref(bp->count);
+ }
+ if (common & ERTS_BPF_TIME_TRACE) {
+ ASSERT((bp->flags & ERTS_BPF_TIME_TRACE_ACTIVE) == 0);
+ bp_time_unref(bp->time);
}
- return num_processed;
-}
-static int clear_function_break(Module *m, BeamInstr *pc, int bif, BeamInstr break_op) {
- BpData *bd;
- Uint ix = 0;
- BeamInstr **code_base = NULL;
+ ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
+ return 1;
+}
- if (bif == BREAK_IS_ERL) {
- code_base = (BeamInstr **)m->code;
- ASSERT(code_base);
- ASSERT(code_base <= (BeamInstr **)pc);
- ASSERT((BeamInstr **)pc < code_base + (m->code_length/sizeof(BeamInstr *)));
- } else {
- ASSERT(*pc == (BeamInstr) em_apply_bif);
- ASSERT(m == NULL);
+static void
+bp_meta_unref(BpMetaPid* bmp)
+{
+ if (erts_refc_dectest(&bmp->refc, 0) <= 0) {
+ Free(bmp);
}
+}
- /*
- * Currently no trace support for native code.
- */
- if (erts_is_native_break(pc)) {
- return 0;
+static void
+bp_count_unref(BpCount* bcp)
+{
+ if (erts_refc_dectest(&bcp->refc, 0) <= 0) {
+ Free(bcp);
}
+}
- while ( (bd = is_break(pc, break_op))) {
- /* Remove all breakpoints of this type.
- * There should be only one of each type,
- * but break_op may be 0 which matches any type.
+static void
+bp_time_unref(BpDataTime* bdt)
+{
+ if (erts_refc_dectest(&bdt->refc, 0) <= 0) {
+ Uint i = 0;
+ Uint j = 0;
+ Process *h_p = NULL;
+ bp_data_time_item_t* item = NULL;
+ process_breakpoint_time_t* pbt = NULL;
+
+ /* remove all psd associated with the hash
+ * and then delete the hash.
+ * ... sigh ...
*/
- BeamInstr op;
- BpData ***rs = (BpData ***) (pc - 4);
- BpData **r = NULL;
-
-#ifdef DEBUG
- for (ix = 1; ix < erts_no_schedulers; ++ix) {
- ASSERT((*rs)[ix] == (*rs)[0]);
- }
-#endif
-
- r = &((*rs)[0]);
-
- ASSERT(*r);
- /* Find opcode for this breakpoint */
- if (break_op) {
- op = break_op;
- } else {
- if (bd == (*r)->next) {
- /* First breakpoint in ring */
- op = *pc;
- } else {
- op = bd->prev->orig_instr;
- }
- }
- if (BpSingleton(bd)) {
- ASSERT(*r == bd);
- /* Only one breakpoint to remove */
- if (bif == BREAK_IS_ERL) {
- *pc = bd->orig_instr;
- }
- Free(*rs);
- *rs = NULL;
- } else {
- BpData *bd_prev = bd->prev;
-
- BpSpliceNext(bd, bd_prev);
- ASSERT(BpSingleton(bd));
- if (bd == *r) {
- /* We removed the last breakpoint in the ring */
- *r = bd_prev;
- bd_prev->orig_instr = bd->orig_instr;
- } else if (bd_prev == *r) {
- /* We removed the first breakpoint in the ring */
- if (bif == BREAK_IS_ERL) {
- *pc = bd->orig_instr;
- }
- } else {
- bd_prev->orig_instr = bd->orig_instr;
- }
- }
- if (op == (BeamInstr) BeamOp(op_i_trace_breakpoint) ||
- op == (BeamInstr) BeamOp(op_i_mtrace_breakpoint)) {
-
- BpDataTrace *bdt = (BpDataTrace *) bd;
- MatchSetUnref(bdt->match_spec);
- }
- if (op == (BeamInstr) BeamOp(op_i_time_breakpoint)) {
- BpDataTime *bdt = (BpDataTime *) bd;
- Uint i = 0;
- Uint j = 0;
- Process *h_p = NULL;
- bp_data_time_item_t *item = NULL;
- process_breakpoint_time_t *pbt = NULL;
-
- /* remove all psd associated with the hash
- * and then delete the hash.
- * ... sigh ...
- */
- for( i = 0; i < bdt->n; ++i) {
- if (bdt->hash[i].used) {
- for (j = 0; j < bdt->hash[i].n; ++j) {
- item = &(bdt->hash[i].item[j]);
- if (item->pid != NIL) {
- h_p = process_tab[internal_pid_index(item->pid)];
- if (h_p) {
- pbt = ERTS_PROC_SET_CALL_TIME(h_p, ERTS_PROC_LOCK_MAIN, NULL);
- if (pbt) {
- Free(pbt);
- }
+ for (i = 0; i < bdt->n; ++i) {
+ if (bdt->hash[i].used) {
+ for (j = 0; j < bdt->hash[i].n; ++j) {
+ item = &(bdt->hash[i].item[j]);
+ if (item->pid != NIL) {
+ 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);
+ if (pbt) {
+ Free(pbt);
}
+ erts_smp_proc_unlock(h_p, ERTS_PROC_LOCK_MAIN);
}
}
}
- bp_hash_delete(&(bdt->hash[i]));
}
- Free(bdt->hash);
- bdt->hash = NULL;
- bdt->n = 0;
+ bp_hash_delete(&(bdt->hash[i]));
}
- Free(bd);
- if (bif == BREAK_IS_ERL) {
- ASSERT(((BeamInstr) code_base[MI_NUM_BREAKPOINTS]) > 0);
- --(*(BeamInstr*)&code_base[MI_NUM_BREAKPOINTS]);
- }
- if (*rs) {
- for (ix = 1; ix < erts_no_schedulers; ++ix) {
- (*rs)[ix] = (*rs)[0];
- }
- }
- } /* while bd != NULL */
- return 1;
+ Free(bdt->hash);
+ Free(bdt);
+ }
}
-
-
-/*
-** Searches (linear forward) the breakpoint ring for a specified opcode
-** and returns a pointer to the breakpoint data structure or NULL if
-** not found. If the specified opcode is 0, the last breakpoint is
-** returned. The program counter must point to the first executable
-** (breakpoint) instruction of the function.
-*/
-
-BpData *erts_get_time_break(Process *p, BeamInstr *pc) {
- return get_break(p, pc, (BeamInstr) BeamOp(op_i_time_breakpoint));
+static BpDataTime*
+get_time_break(BeamInstr *pc)
+{
+ GenericBpData* bp = check_break(pc, ERTS_BPF_TIME_TRACE);
+ return bp ? bp->time : 0;
}
-static BpData *get_break(Process *p, BeamInstr *pc, BeamInstr break_op) {
- ASSERT(pc[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
- if (! erts_is_native_break(pc)) {
- BpData **rs = (BpData **) pc[-4];
- BpData *bd = NULL, *ebd = NULL;
-
- if (! rs) {
- return NULL;
- }
-
- bd = ebd = rs[bp_sched2ix_proc(p)];
- ASSERT(bd);
- if (bd->this_instr == break_op) {
- return bd;
- }
-
- bd = bd->next;
- while (bd != ebd) {
- ASSERT(bd);
- if (bd->this_instr == break_op) {
- ASSERT(bd);
- return bd;
- }
- bd = bd->next;
- }
- }
- return NULL;
-}
+static GenericBpData*
+check_break(BeamInstr *pc, Uint break_flags)
+{
+ GenericBp* g = (GenericBp *) pc[-4];
-static BpData *is_break(BeamInstr *pc, BeamInstr break_op) {
- BpData **rs;
- BpData *bd = NULL, *ebd = NULL;
ASSERT(pc[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
-
if (erts_is_native_break(pc)) {
- return NULL;
- }
- rs = (BpData **) pc[-4];
- if (! rs) {
- return NULL;
- }
-
- bd = ebd = rs[erts_bp_sched2ix()];
- ASSERT(bd);
- if ( (break_op == 0) || (bd->this_instr == break_op)) {
- return bd;
+ return 0;
}
-
- bd = bd->next;
- while (bd != ebd) {
- ASSERT(bd);
- if (bd->this_instr == break_op) {
- ASSERT(bd);
- return bd;
+ if (g) {
+ GenericBpData* bp = &g->data[erts_active_bp_ix()];
+ ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
+ if (bp->flags & break_flags) {
+ return bp;
}
- bd = bd->next;
}
- return NULL;
+ return 0;
}
diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h
index 167069552f..28aaaa462a 100644
--- a/erts/emulator/beam/beam_bp.h
+++ b/erts/emulator/beam/beam_bp.h
@@ -25,77 +25,6 @@
#include "erl_vm.h"
#include "global.h"
-
-
-/* A couple of gotchas:
- *
- * The breakpoint structure from BeamInstr,
- * In beam_emu where the instruction counter pointer, I (or pc),
- * points to the *current* instruction. At that time, if the instruction
- * is a breakpoint instruction the pc looks like the following,
- *
- * I[-5] | op_i_func_info_IaaI | scheduler specific entries
- * I[-4] | BpData** bpa | --> | BpData * bdas1 | ... | BpData * bdasN |
- * I[-3] | Tagged Module | | |
- * I[-2] | Tagged Function | V V
- * I[-1] | Arity | BpData -> BpData -> BpData -> BpData
- * I[0] | The bp instruction | ^ * the bp wheel * |
- * |------------------------------
- *
- * Common struct to all bp_data_*
- *
- * 1) The type of bp_data structure in the ring is deduced from the
- * orig_instr field of the structure _before_ in the ring, except for
- * the first structure in the ring that has its instruction in
- * pc[0] of the code to execute.
- * This is valid as long as you don't search for the function while it is
- * being executed by something else. Or is in the middle of its rotation for
- * any other reason.
- * A key, the bp beam instruction, is included for this reason.
- *
- * 2) pc[-4][sched_id - 1] points to the _last_ structure in the ring before the
- * breakpoints are being executed.
- *
- * So, as an example, when a breakpointed function starts to execute,
- * the first instruction that is a breakpoint instruction at pc[0] finds
- * its data at ((BpData **) pc[-4][sched_id - 1])->next and has to cast that pointer
- * to the correct bp_data type.
-*/
-
-typedef struct bp_data {
- struct bp_data *next; /* Doubly linked ring pointers */
- struct bp_data *prev; /* -"- */
- BeamInstr orig_instr; /* The original instruction to execute */
- BeamInstr this_instr; /* key */
-} BpData;
-/*
-** All the following bp_data_.. structs must begin the same way
-*/
-
-typedef struct bp_data_trace {
- struct bp_data *next;
- struct bp_data *prev;
- BeamInstr orig_instr;
- BeamInstr this_instr; /* key */
- Binary *match_spec;
- Eterm tracer_pid;
-} BpDataTrace;
-
-typedef struct bp_data_debug {
- struct bp_data *next;
- struct bp_data *prev;
- BeamInstr orig_instr;
- BeamInstr this_instr; /* key */
-} BpDataDebug;
-
-typedef struct bp_data_count { /* Call count */
- struct bp_data *next;
- struct bp_data *prev;
- BeamInstr orig_instr;
- BeamInstr this_instr; /* key */
- erts_smp_atomic_t acount;
-} BpDataCount;
-
typedef struct {
Eterm pid;
Sint count;
@@ -110,13 +39,9 @@ typedef struct {
} bp_time_hash_t;
typedef struct bp_data_time { /* Call time */
- struct bp_data *next;
- struct bp_data *prev;
- BeamInstr orig_instr;
- BeamInstr this_instr; /* key */
- Uint pause;
- Uint n;
- bp_time_hash_t *hash;
+ Uint n;
+ bp_time_hash_t *hash;
+ erts_refc_t refc;
} BpDataTime;
typedef struct {
@@ -126,64 +51,42 @@ typedef struct {
BeamInstr *pc;
} process_breakpoint_time_t; /* used within psd */
-extern erts_smp_spinlock_t erts_bp_lock;
+typedef struct {
+ erts_smp_atomic_t acount;
+ erts_refc_t refc;
+} BpCount;
+
+typedef struct {
+ erts_smp_atomic_t pid;
+ erts_refc_t refc;
+} BpMetaPid;
+
+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 */
+ BpCount* count; /* For call count */
+ BpDataTime* time; /* For time trace */
+} GenericBpData;
+
+#define ERTS_NUM_BP_IX 2
+
+typedef struct generic_bp {
+ BeamInstr orig_instr;
+ GenericBpData data[ERTS_NUM_BP_IX];
+} GenericBp;
#define ERTS_BP_CALL_TIME_SCHEDULE_IN (0)
#define ERTS_BP_CALL_TIME_SCHEDULE_OUT (1)
#define ERTS_BP_CALL_TIME_SCHEDULE_EXITING (2)
-#define ERTS_BP_CALL_TIME_CALL (0)
-#define ERTS_BP_CALL_TIME_RETURN (1)
-#define ERTS_BP_CALL_TIME_TAIL_CALL (2)
-
-#ifdef ERTS_SMP
-#define ErtsSmpBPLock(BDC) erts_smp_spin_lock(&erts_bp_lock)
-#define ErtsSmpBPUnlock(BDC) erts_smp_spin_unlock(&erts_bp_lock)
-#else
-#define ErtsSmpBPLock(BDC)
-#define ErtsSmpBPUnlock(BDC)
-#endif
-
#ifdef ERTS_SMP
#define bp_sched2ix_proc(p) ((p)->scheduler_data->no - 1)
#else
#define bp_sched2ix_proc(p) (0)
#endif
-#define ErtsCountBreak(p, pc,instr_result) \
-do { \
- BpData **bds = (BpData **) (pc)[-4]; \
- BpDataCount *bdc = NULL; \
- Uint ix = bp_sched2ix_proc( (p) ); \
- erts_aint_t count = 0; \
- \
- ASSERT((pc)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); \
- ASSERT(bds); \
- bdc = (BpDataCount *) bds[ix]; \
- bdc = (BpDataCount *) bdc->next; \
- ASSERT(bdc); \
- bds[ix] = (BpData *) bdc; \
- count = erts_smp_atomic_read_nob(&bdc->acount); \
- if (count >= 0) erts_smp_atomic_inc_nob(&bdc->acount); \
- *(instr_result) = bdc->orig_instr; \
-} while (0)
-
-#define ErtsBreakSkip(p, pc,instr_result) \
-do { \
- BpData **bds = (BpData **) (pc)[-4]; \
- BpData *bd = NULL; \
- Uint ix = bp_sched2ix_proc( (p) ); \
- \
- ASSERT((pc)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); \
- ASSERT(bds); \
- bd = bds[ix]; \
- ASSERT(bd); \
- bd = bd->next; \
- ASSERT(bd); \
- bds[ix] = bd; \
- *(instr_result) = bd->orig_instr; \
-} while (0)
-
enum erts_break_op{
erts_break_nop = 0, /* Must be false */
erts_break_set = !0, /* Must be true */
@@ -191,7 +94,17 @@ enum erts_break_op{
erts_break_stop
};
+typedef Uint32 ErtsBpIndex;
+typedef struct {
+ BeamInstr* pc;
+ Module* mod;
+} BpFunction;
+
+typedef struct {
+ Uint matched; /* Number matched */
+ BpFunction* matching; /* Matching functions */
+} BpFunctions;
/*
** Function interface exported from beam_bp.c
@@ -199,49 +112,66 @@ enum erts_break_op{
void erts_bp_init(void);
-int erts_set_trace_break(Eterm mfa[3], int specified, Binary *match_spec,
- Eterm tracer_pid);
-int erts_clear_trace_break(Eterm mfa[3], int specified);
-int erts_set_mtrace_break(Eterm mfa[3], int specified, Binary *match_spec,
+void erts_prepare_bp_staging(void);
+void erts_commit_staged_bp(void);
+
+ERTS_GLB_INLINE ErtsBpIndex erts_active_bp_ix(void);
+ERTS_GLB_INLINE ErtsBpIndex erts_staging_bp_ix(void);
+
+void erts_bp_match_functions(BpFunctions* f, Eterm mfa[3], int specified);
+void erts_bp_match_export(BpFunctions* f, Eterm mfa[3], int specified);
+void erts_bp_free_matched_functions(BpFunctions* f);
+
+void erts_install_breakpoints(BpFunctions* f);
+void erts_uninstall_breakpoints(BpFunctions* f);
+void erts_consolidate_bp_data(BpFunctions* f, int local);
+void erts_consolidate_bif_bp_data(void);
+
+void erts_set_trace_break(BpFunctions *f, Binary *match_spec);
+void erts_clear_trace_break(BpFunctions *f);
+
+void erts_set_call_trace_bif(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);
-int erts_clear_mtrace_break(Eterm mfa[3], int specified);
+void erts_clear_mtrace_break(BpFunctions *f);
void erts_set_mtrace_bif(BeamInstr *pc, Binary *match_spec,
Eterm tracer_pid);
void erts_clear_mtrace_bif(BeamInstr *pc);
-int erts_set_debug_break(Eterm mfa[3], int specified);
-int erts_clear_debug_break(Eterm mfa[3], int specified);
-int erts_set_count_break(Eterm mfa[3], int specified, enum erts_break_op);
-int erts_clear_count_break(Eterm mfa[3], int specified);
+void erts_set_debug_break(BpFunctions *f);
+void erts_clear_debug_break(BpFunctions *f);
+void erts_set_count_break(BpFunctions *f, enum erts_break_op);
+void erts_clear_count_break(BpFunctions *f);
-int erts_clear_break(Eterm mfa[3], int specified);
+
+void erts_clear_all_breaks(BpFunctions* f);
int erts_clear_module_break(Module *modp);
-int erts_clear_function_break(Module *modp, BeamInstr *pc);
+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 erts_bif_mtrace(Process *p, BeamInstr *pc, Eterm *args,
- int local, Eterm *tracer_pid);
-int erts_is_trace_break(BeamInstr *pc, Binary **match_spec_ret,
- Eterm *tracer_pid_ret);
+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);
int erts_is_mtrace_bif(BeamInstr *pc, Binary **match_spec_ret,
Eterm *tracer_pid_ret);
int erts_is_native_break(BeamInstr *pc);
-int erts_is_count_break(BeamInstr *pc, Sint *count_ret);
+int erts_is_count_break(BeamInstr *pc, Uint *count_ret);
int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *call_time);
-void erts_trace_time_break(Process *p, BeamInstr *pc, BpDataTime *bdt, Uint type);
+void erts_trace_time_call(Process* c_p, BeamInstr* pc, BpDataTime* bdt);
+void erts_trace_time_return(Process* c_p, BeamInstr* pc);
void erts_schedule_time_break(Process *p, Uint out);
-int erts_set_time_break(Eterm mfa[3], int specified, enum erts_break_op);
-int erts_clear_time_break(Eterm mfa[3], int specified);
+void erts_set_time_break(BpFunctions *f, enum erts_break_op);
+void erts_clear_time_break(BpFunctions *f);
int erts_is_time_trace_bif(Process *p, BeamInstr *pc, Eterm *call_time);
void erts_set_time_trace_bif(BeamInstr *pc, enum erts_break_op);
void erts_clear_time_trace_bif(BeamInstr *pc);
-BpData *erts_get_time_break(Process *p, BeamInstr *pc);
BeamInstr *erts_find_local_func(Eterm mfa[3]);
@@ -258,6 +188,19 @@ ERTS_GLB_INLINE Uint erts_bp_sched2ix(void)
return 0;
#endif
}
+
+extern erts_smp_atomic32_t erts_active_bp_index;
+extern erts_smp_atomic32_t erts_staging_bp_index;
+
+ERTS_GLB_INLINE ErtsBpIndex erts_active_bp_ix(void)
+{
+ return erts_smp_atomic32_read_nob(&erts_active_bp_index);
+}
+
+ERTS_GLB_INLINE ErtsBpIndex erts_staging_bp_ix(void)
+{
+ return erts_smp_atomic32_read_nob(&erts_staging_bp_index);
+}
#endif
#endif /* _BEAM_BP_H */
diff --git a/erts/emulator/beam/beam_catches.c b/erts/emulator/beam/beam_catches.c
index 406ef1db5f..92f7ffe5a2 100644
--- a/erts/emulator/beam/beam_catches.c
+++ b/erts/emulator/beam/beam_catches.c
@@ -31,78 +31,144 @@ typedef struct {
unsigned cdr;
} beam_catch_t;
-static int free_list;
-static unsigned high_mark;
-static unsigned tabsize;
-static beam_catch_t *beam_catches;
+#ifdef DEBUG
+# define IF_DEBUG(x) x
+#else
+# define IF_DEBUG(x)
+#endif
+
+struct bc_pool {
+ int free_list;
+ unsigned high_mark;
+ unsigned tabsize;
+ beam_catch_t *beam_catches;
+ /*
+ * Note that the 'beam_catches' area is shared by pools. Used slots
+ * are readonly as long as the module is not purgable. The free-list is
+ * protected by the code_ix lock.
+ */
+
+ IF_DEBUG(int is_staging;)
+};
+
+static struct bc_pool bccix[ERTS_NUM_CODE_IX];
void beam_catches_init(void)
{
- tabsize = DEFAULT_TABSIZE;
- free_list = -1;
- high_mark = 0;
+ int i;
+
+ bccix[0].tabsize = DEFAULT_TABSIZE;
+ bccix[0].free_list = -1;
+ bccix[0].high_mark = 0;
+ bccix[0].beam_catches = erts_alloc(ERTS_ALC_T_CODE,
+ sizeof(beam_catch_t)*DEFAULT_TABSIZE);
+ IF_DEBUG(bccix[0].is_staging = 0);
+ for (i=1; i<ERTS_NUM_CODE_IX; i++) {
+ bccix[i] = bccix[i-1];
+ }
+ /* For initial load: */
+ IF_DEBUG(bccix[erts_staging_code_ix()].is_staging = 1);
+}
- beam_catches = erts_alloc(ERTS_ALC_T_CODE, sizeof(beam_catch_t)*DEFAULT_TABSIZE);
+
+static void gc_old_vec(beam_catch_t* vec)
+{
+ int i;
+ for (i=0; i<ERTS_NUM_CODE_IX; i++) {
+ if (bccix[i].beam_catches == vec) {
+ return;
+ }
+ }
+ erts_free(ERTS_ALC_T_CODE, vec);
+}
+
+
+void beam_catches_start_staging(void)
+{
+ ErtsCodeIndex dst = erts_staging_code_ix();
+ ErtsCodeIndex src = erts_active_code_ix();
+ beam_catch_t* prev_vec = bccix[dst].beam_catches;
+
+ ASSERT(!bccix[src].is_staging && !bccix[dst].is_staging);
+
+ bccix[dst] = bccix[src];
+ gc_old_vec(prev_vec);
+ IF_DEBUG(bccix[dst].is_staging = 1);
+}
+
+void beam_catches_end_staging(int commit)
+{
+ IF_DEBUG(bccix[erts_staging_code_ix()].is_staging = 0);
}
unsigned beam_catches_cons(BeamInstr *cp, unsigned cdr)
{
int i;
+ struct bc_pool* p = &bccix[erts_staging_code_ix()];
+ ASSERT(p->is_staging);
/*
* Allocate from free_list while it is non-empty.
* If free_list is empty, allocate at high_mark.
- *
- * This avoids the need to initialise the free list in
- * beam_catches_init(), which would cost O(TABSIZ) time.
*/
- if( free_list >= 0 ) {
- i = free_list;
- free_list = beam_catches[i].cdr;
- } else if( high_mark < tabsize ) {
- i = high_mark;
- high_mark++;
- } else {
- /* No free slots and table is full: realloc table */
- tabsize = 2*tabsize;
- beam_catches = erts_realloc(ERTS_ALC_T_CODE, beam_catches, sizeof(beam_catch_t)*tabsize);
- i = high_mark;
- high_mark++;
+ if (p->free_list >= 0) {
+ i = p->free_list;
+ p->free_list = p->beam_catches[i].cdr;
+ }
+ else {
+ if (p->high_mark >= p->tabsize) {
+ /* No free slots and table is full: realloc table */
+ beam_catch_t* prev_vec = p->beam_catches;
+ unsigned newsize = p->tabsize*2;
+
+ p->beam_catches = erts_alloc(ERTS_ALC_T_CODE,
+ newsize*sizeof(beam_catch_t));
+ sys_memcpy(p->beam_catches, prev_vec,
+ p->tabsize*sizeof(beam_catch_t));
+ gc_old_vec(prev_vec);
+ p->tabsize = newsize;
+ }
+ i = p->high_mark++;
}
- beam_catches[i].cp = cp;
- beam_catches[i].cdr = cdr;
+ p->beam_catches[i].cp = cp;
+ p->beam_catches[i].cdr = cdr;
return i;
}
BeamInstr *beam_catches_car(unsigned i)
{
- if( i >= tabsize ) {
+ struct bc_pool* p = &bccix[erts_active_code_ix()];
+
+ if (i >= p->tabsize ) {
erl_exit(1, "beam_catches_delmod: index %#x is out of range\r\n", i);
}
- return beam_catches[i].cp;
+ return p->beam_catches[i].cp;
}
-void beam_catches_delmod(unsigned head, BeamInstr *code, unsigned code_bytes)
+void beam_catches_delmod(unsigned head, BeamInstr *code, unsigned code_bytes,
+ ErtsCodeIndex code_ix)
{
+ struct bc_pool* p = &bccix[code_ix];
unsigned i, cdr;
+ ASSERT((code_ix == erts_active_code_ix()) != bccix[erts_staging_code_ix()].is_staging);
for(i = head; i != (unsigned)-1;) {
- if( i >= tabsize ) {
+ if (i >= p->tabsize) {
erl_exit(1, "beam_catches_delmod: index %#x is out of range\r\n", i);
}
- if( (char*)beam_catches[i].cp - (char*)code >= code_bytes ) {
+ if( (char*)p->beam_catches[i].cp - (char*)code >= code_bytes ) {
erl_exit(1,
"beam_catches_delmod: item %#x has cp %#lx which is not "
"in module's range [%#lx,%#lx[\r\n",
- i, (long)beam_catches[i].cp,
+ i, (long)p->beam_catches[i].cp,
(long)code, (long)((char*)code + code_bytes));
}
- beam_catches[i].cp = 0;
- cdr = beam_catches[i].cdr;
- beam_catches[i].cdr = free_list;
- free_list = i;
+ p->beam_catches[i].cp = 0;
+ cdr = p->beam_catches[i].cdr;
+ p->beam_catches[i].cdr = p->free_list;
+ p->free_list = i;
i = cdr;
}
}
diff --git a/erts/emulator/beam/beam_catches.h b/erts/emulator/beam/beam_catches.h
index 6223427f0d..b2bd2351a5 100644
--- a/erts/emulator/beam/beam_catches.h
+++ b/erts/emulator/beam/beam_catches.h
@@ -20,12 +20,21 @@
#ifndef __BEAM_CATCHES_H
#define __BEAM_CATCHES_H
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include "sys.h"
+#include "code_ix.h"
+
#define BEAM_CATCHES_NIL (-1)
void beam_catches_init(void);
+void beam_catches_start_staging(void);
+void beam_catches_end_staging(int commit);
unsigned beam_catches_cons(BeamInstr* cp, unsigned cdr);
BeamInstr *beam_catches_car(unsigned i);
-void beam_catches_delmod(unsigned head, BeamInstr* code, unsigned code_bytes);
+void beam_catches_delmod(unsigned head, BeamInstr* code, unsigned code_bytes,
+ ErtsCodeIndex);
#define catch_pc(x) beam_catches_car(catch_val((x)))
diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c
index 8041c92162..a609ed8c71 100644
--- a/erts/emulator/beam/beam_debug.c
+++ b/erts/emulator/beam/beam_debug.c
@@ -84,6 +84,7 @@ erts_debug_breakpoint_2(BIF_ALIST_2)
int i;
int specified = 0;
Eterm res;
+ BpFunctions f;
if (bool != am_true && bool != am_false)
goto error;
@@ -114,18 +115,30 @@ erts_debug_breakpoint_2(BIF_ALIST_2)
mfa[2] = signed_val(mfa[2]);
}
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ ERTS_BIF_YIELD2(bif_export[BIF_erts_debug_breakpoint_2],
+ BIF_P, BIF_ARG_1, BIF_ARG_2);
+ }
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
erts_smp_thr_progress_block();
+ erts_bp_match_functions(&f, mfa, specified);
if (bool == am_true) {
- res = make_small(erts_set_debug_break(mfa, specified));
+ erts_set_debug_break(&f);
+ erts_install_breakpoints(&f);
+ erts_commit_staged_bp();
} else {
- res = make_small(erts_clear_debug_break(mfa, specified));
+ erts_clear_debug_break(&f);
+ erts_commit_staged_bp();
+ erts_uninstall_breakpoints(&f);
}
+ erts_consolidate_bp_data(&f, 1);
+ res = make_small(f.matched);
+ erts_bp_free_matched_functions(&f);
erts_smp_thr_progress_unblock();
erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
-
+ erts_release_code_write_permission();
return res;
error:
@@ -207,6 +220,7 @@ erts_debug_disassemble_1(BIF_ALIST_1)
BIF_RET(am_false);
}
} else if (is_tuple(addr)) {
+ ErtsCodeIndex code_ix;
Module* modp;
Eterm mod;
Eterm name;
@@ -225,14 +239,14 @@ erts_debug_disassemble_1(BIF_ALIST_1)
goto error;
}
arity = signed_val(tp[3]);
- modp = erts_get_module(mod);
+ code_ix = erts_active_code_ix();
+ modp = erts_get_module(mod, code_ix);
/*
* Try the export entry first to allow disassembly of special functions
* such as erts_debug:apply/4. Then search for it in the module.
*/
-
- if ((ep = erts_find_function(mod, name, arity)) != NULL) {
+ if ((ep = erts_find_function(mod, name, arity, code_ix)) != NULL) {
/* XXX: add "&& ep->address != ep->code+3" condition?
* Consider a traced function.
* Its ep will have ep->address == ep->code+3.
@@ -241,9 +255,9 @@ erts_debug_disassemble_1(BIF_ALIST_1)
* But this code_ptr will point to the start of the Export,
* not the function's func_info instruction. BOOM !?
*/
- code_ptr = ((BeamInstr *) ep->address) - 5;
+ code_ptr = ((BeamInstr *) ep->addressv[code_ix]) - 5;
funcinfo = code_ptr+2;
- } else if (modp == NULL || (code_base = modp->code) == NULL) {
+ } else if (modp == NULL || (code_base = modp->curr.code) == NULL) {
BIF_RET(am_undef);
} else {
n = code_base[MI_NUM_FUNCTIONS];
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 6d3b15cd46..7b1ae624ce 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -63,11 +63,7 @@
# define PROCESS_MAIN_CHK_LOCKS(P) \
do { \
if ((P)) { \
- erts_pix_lock_t *pix_lock__ = ERTS_PIX2PIXLOCK(internal_pid_index((P)->id));\
erts_proc_lc_chk_only_proc_main((P)); \
- erts_pix_lock(pix_lock__); \
- ASSERT(0 < (P)->lock.refc && (P)->lock.refc < erts_no_schedulers*5);\
- erts_pix_unlock(pix_lock__); \
} \
else \
erts_lc_check_exact(NULL, 0); \
@@ -221,7 +217,6 @@ BeamInstr beam_continue_exit[1];
BeamInstr* em_call_error_handler;
BeamInstr* em_apply_bif;
-BeamInstr* em_call_traced_function;
/* NOTE These should be the only variables containing trace instructions.
@@ -236,11 +231,6 @@ BeamInstr beam_return_time_trace[1]; /* OpCode(i_return_time_trace) */
/*
- * We should warn only once for tuple funs.
- */
-static erts_smp_atomic_t warned_for_tuple_funs;
-
-/*
* All Beam instructions in numerical order.
*/
@@ -495,7 +485,7 @@ extern int count_instructions;
do { \
if (FCALLS > 0) { \
Eterm* dis_next; \
- SET_I(((Export *) Arg(0))->address); \
+ SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]); \
dis_next = (Eterm *) *I; \
FCALLS--; \
CHECK_ARGS(I); \
@@ -504,7 +494,7 @@ extern int count_instructions;
&& FCALLS > neg_o_reds) { \
goto save_calls1; \
} else { \
- SET_I(((Export *) Arg(0))->address); \
+ SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]); \
CHECK_ARGS(I); \
goto context_switch; \
} \
@@ -965,17 +955,9 @@ static void save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg,
static struct StackTrace * get_trace_from_exc(Eterm exc);
static Eterm make_arglist(Process* c_p, Eterm* reg, int a);
-#if defined(VXWORKS)
-static int init_done;
-#endif
-
void
init_emulator(void)
{
-#if defined(VXWORKS)
- init_done = 0;
-#endif
- erts_smp_atomic_init_nob(&warned_for_tuple_funs, (erts_aint_t) 0);
process_main();
}
@@ -1107,9 +1089,7 @@ dtrace_drvport_str(ErlDrvPort drvport, char *port_buf)
*/
void process_main(void)
{
-#if !defined(VXWORKS)
static int init_done = 0;
-#endif
Process* c_p = NULL;
int reds_used;
#ifdef DEBUG
@@ -1507,7 +1487,7 @@ void process_main(void)
*/
#ifdef USE_VM_CALL_PROBES
if (DTRACE_ENABLED(global_function_entry)) {
- BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address);
+ BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->addressv[erts_active_code_ix()]);
DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]);
}
#endif
@@ -1522,7 +1502,7 @@ void process_main(void)
SET_CP(c_p, I+2);
#ifdef USE_VM_CALL_PROBES
if (DTRACE_ENABLED(global_function_entry)) {
- BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address);
+ BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->addressv[erts_active_code_ix()]);
DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]);
}
#endif
@@ -1535,7 +1515,7 @@ void process_main(void)
OpCase(i_call_ext_only_e):
#ifdef USE_VM_CALL_PROBES
if (DTRACE_ENABLED(global_function_entry)) {
- BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address);
+ BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->addressv[erts_active_code_ix()]);
DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]);
}
#endif
@@ -1826,13 +1806,12 @@ void process_main(void)
msgp = PEEK_MESSAGE(c_p);
if (msgp)
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- else {
+ else
#endif
+ {
SET_I((BeamInstr *) Arg(0));
Goto(*I); /* Jump to a wait or wait_timeout instruction */
-#ifdef ERTS_SMP
}
-#endif
}
ErtsMoveMsgAttachmentIntoProc(msgp, c_p, E, HTOP, FCALLS,
{
@@ -2061,11 +2040,11 @@ void process_main(void)
OpCase(wait_f):
wait2: {
- ASSERT(!ERTS_PROC_IS_EXITING(c_p));
c_p->i = (BeamInstr *) Arg(0); /* L1 */
SWAPOUT;
c_p->arity = 0;
- c_p->status = P_WAITING;
+ 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;
goto do_schedule;
@@ -3144,10 +3123,6 @@ void process_main(void)
c_p->arg_reg[0] = r(0);
SWAPOUT;
c_p->i = I;
- erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_STATUS);
- if (c_p->status != P_SUSPENDED)
- erts_add_to_runq(c_p);
- erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_STATUS);
goto do_schedule1;
}
@@ -4580,64 +4555,6 @@ void process_main(void)
* Trace and debugging support.
*/
- /*
- * At this point, I points to the code[3] in the export entry for
- * a trace-enabled function.
- *
- * code[0]: Module
- * code[1]: Function
- * code[2]: Arity
- * code[3]: &&call_traced_function
- * code[4]: Address of function.
- */
- OpCase(call_traced_function): {
- if (IS_TRACED_FL(c_p, F_TRACE_CALLS)) {
- unsigned offset = offsetof(Export, code) + 3*sizeof(BeamInstr);
- Export* ep = (Export *) (((char *)I)-offset);
- Uint32 flags;
-
- SWAPOUT;
- reg[0] = r(0);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
- flags = erts_call_trace(c_p, ep->code, ep->match_prog_set, reg,
- 0, &c_p->tracer_proc);
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
- ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- ASSERT(!ERTS_PROC_IS_EXITING(c_p));
- SWAPIN;
-
- if (flags & MATCH_SET_RX_TRACE) {
- ASSERT(c_p->htop <= E && E <= c_p->hend);
- if (E - 3 < HTOP) {
- /* SWAPOUT, SWAPIN was done and r(0) was saved above */
- PROCESS_MAIN_CHK_LOCKS(c_p);
- FCALLS -= erts_garbage_collect(c_p, 3, reg, ep->code[2]);
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- r(0) = reg[0];
- SWAPIN;
- }
- E -= 3;
- ASSERT(c_p->htop <= E && E <= c_p->hend);
- ASSERT(is_CP((BeamInstr)(ep->code)));
- ASSERT(is_internal_pid(c_p->tracer_proc) ||
- is_internal_port(c_p->tracer_proc));
- E[2] = make_cp(c_p->cp); /* Code in lower range on halfword */
- E[1] = am_true; /* Process tracer */
- E[0] = make_cp(ep->code);
- c_p->cp = (flags & MATCH_SET_EXCEPTION_TRACE)
- ? beam_exception_trace : beam_return_trace;
- erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- c_p->trace_flags |= F_EXCEPTION_TRACE;
- erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- }
- SET_I((BeamInstr *)Arg(0));
- Dispatch();
- }
-
OpCase(return_trace): {
BeamInstr* code = (BeamInstr *) (UWord) E[0];
@@ -4652,80 +4569,22 @@ void process_main(void)
Goto(*I);
}
- OpCase(i_count_breakpoint): {
+ OpCase(i_generic_breakpoint): {
BeamInstr real_I;
-
- ErtsCountBreak(c_p, (BeamInstr *) I, &real_I);
+ ASSERT(I[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
+ SWAPOUT;
+ reg[0] = r(0);
+ real_I = erts_generic_breakpoint(c_p, I, reg);
+ r(0) = reg[0];
+ SWAPIN;
ASSERT(VALID_INSTR(real_I));
Goto(real_I);
}
- /* need to send mfa instead of bdt pointer
- * the pointer might be deallocated.
- */
-
- OpCase(i_time_breakpoint): {
- BeamInstr real_I;
- BpData **bds = (BpData **) (I)[-4];
- BpDataTime *bdt = NULL;
- Uint ix = 0;
-#ifdef ERTS_SMP
- ix = c_p->scheduler_data->no - 1;
-#else
- ix = 0;
-#endif
- bdt = (BpDataTime *)bds[ix];
-
- ASSERT((I)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
- ASSERT(bdt);
- bdt = (BpDataTime *) bdt->next;
- ASSERT(bdt);
- bds[ix] = (BpData *) bdt;
- real_I = bdt->orig_instr;
- ASSERT(VALID_INSTR(real_I));
-
- if (IS_TRACED_FL(c_p, F_TRACE_CALLS) && !(bdt->pause)) {
- if ( (*(c_p->cp) == (BeamInstr) OpCode(i_return_time_trace)) ||
- (*(c_p->cp) == (BeamInstr) OpCode(return_trace)) ||
- (*(c_p->cp) == (BeamInstr) OpCode(i_return_to_trace))) {
- /* This _IS_ a tail recursive call */
- SWAPOUT;
- erts_trace_time_break(c_p, I, bdt, ERTS_BP_CALL_TIME_TAIL_CALL);
- SWAPIN;
- } else {
- SWAPOUT;
- erts_trace_time_break(c_p, I, bdt, ERTS_BP_CALL_TIME_CALL);
-
- /* r register needs to be copied to the array
- * for the garbage collector
- */
- ASSERT(c_p->htop <= E && E <= c_p->hend);
- if (E - 2 < HTOP) {
- reg[0] = r(0);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- FCALLS -= erts_garbage_collect(c_p, 2, reg, I[-1]);
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- r(0) = reg[0];
- }
- SWAPIN;
-
- ASSERT(c_p->htop <= E && E <= c_p->hend);
-
- E -= 2;
- E[0] = make_cp(I);
- E[1] = make_cp(c_p->cp); /* original return address */
- c_p->cp = beam_return_time_trace;
- }
- }
-
- Goto(real_I);
- }
-
OpCase(i_return_time_trace): {
BeamInstr *pc = (BeamInstr *) (UWord) E[0];
SWAPOUT;
- erts_trace_time_break(c_p, pc, NULL, ERTS_BP_CALL_TIME_RETURN);
+ erts_trace_time_return(c_p, pc);
SWAPIN;
c_p->cp = NULL;
SET_I((BeamInstr *) cp_val(E[1]));
@@ -4733,114 +4592,6 @@ void process_main(void)
Goto(*I);
}
- OpCase(i_trace_breakpoint):
- if (! IS_TRACED_FL(c_p, F_TRACE_CALLS)) {
- BeamInstr real_I;
-
- ErtsBreakSkip(c_p, (BeamInstr *) I, &real_I);
- Goto(real_I);
- }
- /* Fall through to next case */
- OpCase(i_mtrace_breakpoint): {
- BeamInstr real_I;
- Uint32 flags;
- Eterm tracer_pid;
- Uint* cpp;
- int return_to_trace = 0, need = 0;
- flags = 0;
- SWAPOUT;
- reg[0] = r(0);
-
- if (*(c_p->cp) == (BeamInstr) OpCode(return_trace)) {
- cpp = &E[2];
- } else if (*(c_p->cp) == (BeamInstr) OpCode(i_return_to_trace)) {
- return_to_trace = !0;
- cpp = &E[0];
- } else if (*(c_p->cp) == (BeamInstr) OpCode(i_return_time_trace)) {
- return_to_trace = !0;
- cpp = &E[0];
- } else {
- cpp = NULL;
- }
- if (cpp) {
- /* This _IS_ a tail recursive call, if there are
- * return_trace and/or i_return_to_trace stackframes
- * on the stack, they are not intermixed with y registers
- */
- BeamInstr *cp_save = c_p->cp;
- for (;;) {
- ASSERT(is_CP(*cpp));
- if (*cp_val(*cpp) == (BeamInstr) OpCode(return_trace)) {
- cpp += 3;
- } else if (*cp_val(*cpp) == (BeamInstr) OpCode(i_return_to_trace)) {
- return_to_trace = !0;
- cpp += 1;
- } else if (*cp_val(*cpp) == (BeamInstr) OpCode(i_return_time_trace)) {
- cpp += 2;
- } else
- break;
- }
- c_p->cp = (BeamInstr *) cp_val(*cpp);
- ASSERT(is_CP(*cpp));
- ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
- real_I = erts_trace_break(c_p, I, reg, &flags, &tracer_pid);
- ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
- SWAPIN; /* Needed by shared heap. */
- c_p->cp = cp_save;
- } else {
- ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
- real_I = erts_trace_break(c_p, I, reg, &flags, &tracer_pid);
- ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
- SWAPIN; /* Needed by shared heap. */
- }
-
- ASSERT(!ERTS_PROC_IS_EXITING(c_p));
-
- if ((flags & MATCH_SET_RETURN_TO_TRACE) && !return_to_trace) {
- need += 1;
- }
- if (flags & MATCH_SET_RX_TRACE) {
- need += 3;
- }
- if (need) {
- ASSERT(c_p->htop <= E && E <= c_p->hend);
- if (E - need < HTOP) {
- /* SWAPOUT was done and r(0) was saved above */
- PROCESS_MAIN_CHK_LOCKS(c_p);
- FCALLS -= erts_garbage_collect(c_p, need, reg, I[-1]);
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- r(0) = reg[0];
- SWAPIN;
- }
- }
- if ((flags & MATCH_SET_RETURN_TO_TRACE) && !return_to_trace) {
- E -= 1;
- ASSERT(c_p->htop <= E && E <= c_p->hend);
- E[0] = make_cp(c_p->cp);
- c_p->cp = (BeamInstr *) beam_return_to_trace;
- }
- if (flags & MATCH_SET_RX_TRACE) {
- E -= 3;
- 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));
- E[2] = make_cp(c_p->cp);
- E[1] = tracer_pid;
- E[0] = make_cp(I - 3); /* We ARE at the beginning of an
- instruction,
- the funcinfo is above i. */
- c_p->cp =
- (flags & MATCH_SET_EXCEPTION_TRACE)
- ? beam_exception_trace : beam_return_trace;
- erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- c_p->trace_flags |= F_EXCEPTION_TRACE;
- erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- Goto(real_I);
- }
-
OpCase(i_return_to_trace): {
if (IS_TRACED_FL(c_p, F_TRACE_RETURN_TO)) {
Uint *cpp = (Uint*) E;
@@ -5110,9 +4861,6 @@ void process_main(void)
c_p->arity = 1; /* One living register (the 'true' return value) */
SWAPOUT;
c_p->i = I + 1; /* Next instruction */
- erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_STATUS);
- erts_add_to_runq(c_p);
- erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_STATUS);
c_p->current = NULL;
goto do_schedule;
}
@@ -5193,7 +4941,6 @@ void process_main(void)
#endif /* NO_JUMP_TABLE */
em_call_error_handler = OpCode(call_error_handler);
- em_call_traced_function = OpCode(call_traced_function);
em_apply_bif = OpCode(apply_bif);
beam_apply[0] = (BeamInstr) OpCode(i_apply);
@@ -5234,7 +4981,7 @@ void process_main(void)
save_calls(c_p, (Export *) Arg(0));
- SET_I(((Export *) Arg(0))->address);
+ SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]);
dis_next = (Eterm *) *I;
FCALLS--;
@@ -5911,7 +5658,8 @@ call_error_handler(Process* p, BeamInstr* fi, Eterm* reg, Eterm func)
/*
* Search for the error_handler module.
*/
- ep = erts_find_function(erts_proc_get_error_handler(p), func, 3);
+ ep = erts_find_function(erts_proc_get_error_handler(p), func, 3,
+ erts_active_code_ix());
if (ep == NULL) { /* No error handler */
p->current = fi;
p->freason = EXC_UNDEF;
@@ -5941,7 +5689,7 @@ call_error_handler(Process* p, BeamInstr* fi, Eterm* reg, Eterm func)
reg[0] = fi[0];
reg[1] = fi[1];
reg[2] = args;
- return ep->address;
+ return ep->addressv[erts_active_code_ix()];
}
@@ -5955,7 +5703,7 @@ apply_setup_error_handler(Process* p, Eterm module, Eterm function, Uint arity,
* there is no error handler module.
*/
- if ((ep = erts_find_export_entry(erts_proc_get_error_handler(p),
+ if ((ep = erts_active_export_entry(erts_proc_get_error_handler(p),
am_undefined_function, 3)) == NULL) {
return NULL;
} else {
@@ -6062,7 +5810,7 @@ apply(Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg)
* Note: All BIFs have export entries; thus, no special case is needed.
*/
- if ((ep = erts_find_export_entry(module, function, arity)) == NULL) {
+ if ((ep = erts_active_export_entry(module, function, arity)) == NULL) {
if ((ep = apply_setup_error_handler(p, module, function, arity, reg)) == NULL) goto error;
} else if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) {
save_calls(p, ep);
@@ -6070,11 +5818,11 @@ apply(Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg)
#ifdef USE_VM_CALL_PROBES
if (DTRACE_ENABLED(global_function_entry)) {
- BeamInstr *fptr = (BeamInstr *) ep->address;
+ BeamInstr *fptr = (BeamInstr *) ep->addressv[erts_active_code_ix()];
DTRACE_GLOBAL_CALL(p, (Eterm)fptr[-3], (Eterm)fptr[-2], (Uint)fptr[-1]);
}
#endif
- return ep->address;
+ return ep->addressv[erts_active_code_ix()];
}
static BeamInstr*
@@ -6116,7 +5864,7 @@ fixed_apply(Process* p, Eterm* reg, Uint arity)
* Note: All BIFs have export entries; thus, no special case is needed.
*/
- if ((ep = erts_find_export_entry(module, function, arity)) == NULL) {
+ if ((ep = erts_active_export_entry(module, function, arity)) == NULL) {
if ((ep = apply_setup_error_handler(p, module, function, arity, reg)) == NULL)
goto error;
} else if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) {
@@ -6125,11 +5873,11 @@ fixed_apply(Process* p, Eterm* reg, Uint arity)
#ifdef USE_VM_CALL_PROBES
if (DTRACE_ENABLED(global_function_entry)) {
- BeamInstr *fptr = (BeamInstr *) ep->address;
+ BeamInstr *fptr = (BeamInstr *) ep->addressv[erts_active_code_ix()];
DTRACE_GLOBAL_CALL(p, (Eterm)fptr[-3], (Eterm)fptr[-2], (Uint)fptr[-1]);
}
#endif
- return ep->address;
+ return ep->addressv[erts_active_code_ix()];
}
int
@@ -6206,9 +5954,7 @@ erts_hibernate(Process* c_p, Eterm module, Eterm function, Eterm args, Eterm* re
*/
erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p);
- if (c_p->msg.len > 0) {
- erts_add_to_runq(c_p);
- } else {
+ if (!c_p->msg.len) {
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
c_p->fvalue = NIL;
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -6216,14 +5962,12 @@ erts_hibernate(Process* c_p, Eterm module, Eterm function, Eterm args, Eterm* re
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
- ASSERT(!ERTS_PROC_IS_EXITING(c_p));
#ifdef ERTS_SMP
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p);
- if (c_p->msg.len > 0)
- erts_add_to_runq(c_p);
- else
+ if (!c_p->msg.len)
#endif
- c_p->status = P_WAITING;
+ 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_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
c_p->current = bif_export[BIF_hibernate_3]->code;
@@ -6240,7 +5984,6 @@ call_fun(Process* p, /* Current process. */
Eterm fun = reg[arity];
Eterm hdr;
int i;
- Eterm function;
Eterm* hp;
if (!is_boxed(fun)) {
@@ -6311,7 +6054,7 @@ call_fun(Process* p, /* Current process. */
Export* ep;
Module* modp;
Eterm module;
-
+ ErtsCodeIndex code_ix = erts_active_code_ix();
/*
* No arity. There is no module loaded that defines the fun,
@@ -6319,9 +6062,9 @@ 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)) != NULL && modp->code != NULL) {
+ if ((modp = erts_get_module(module, code_ix)) != NULL
+ && modp->curr.code != NULL) {
/*
* There is a module loaded, but obviously the fun is not
* defined in it. We must not call the error_handler
@@ -6336,7 +6079,7 @@ call_fun(Process* p, /* Current process. */
*/
ep = erts_find_function(erts_proc_get_error_handler(p),
- am_undefined_lambda, 3);
+ am_undefined_lambda, 3, code_ix);
if (ep == NULL) { /* No error handler */
p->current = NULL;
p->freason = EXC_UNDEF;
@@ -6346,7 +6089,7 @@ call_fun(Process* p, /* Current process. */
reg[1] = fun;
reg[2] = args;
reg[3] = NIL;
- return ep->address;
+ return ep->addressv[erts_active_code_ix()];
}
}
} else if (is_export_header(hdr)) {
@@ -6358,7 +6101,7 @@ call_fun(Process* p, /* Current process. */
if (arity == actual_arity) {
DTRACE_GLOBAL_CALL(p, ep->code[0], ep->code[1], (Uint)ep->code[2]);
- return ep->address;
+ return ep->addressv[erts_active_code_ix()];
} else {
/*
* Wrong arity. First build a list of the arguments.
@@ -6378,63 +6121,6 @@ call_fun(Process* p, /* Current process. */
p->fvalue = TUPLE2(hp, fun, args);
return NULL;
}
- } else if (hdr == make_arityval(2)) {
- Eterm* tp;
- Export* ep;
- Eterm module;
-
- tp = tuple_val(fun);
- module = tp[1];
- function = tp[2];
- if (!is_atom(module) || !is_atom(function)) {
- goto badfun;
- }
-
- /*
- * If this is the first time a tuple fun is used,
- * send a warning to the logger.
- */
- if (erts_smp_atomic_xchg_nob(&warned_for_tuple_funs,
- (erts_aint_t) 1) == 0) {
- erts_dsprintf_buf_t* dsbufp;
-
- dsbufp = erts_create_logger_dsbuf();
- erts_dsprintf(dsbufp, "Call to tuple fun {%T,%T}.\n\n"
- "Tuple funs are deprecated and will be removed "
- "in R16. Use \"fun M:F/A\" instead, for example "
- "\"fun %T:%T/%d\".\n\n"
- "(This warning will only be shown the first time "
- "a tuple fun is called.)\n",
- module, function, module, function, arity);
- erts_send_warning_to_logger(p->group_leader, dsbufp);
- }
-
- if ((ep = erts_find_export_entry(module, function, arity)) == NULL) {
- ep = erts_find_export_entry(erts_proc_get_error_handler(p),
- am_undefined_function, 3);
- if (ep == NULL) {
- p->freason = EXC_UNDEF;
- return 0;
- }
- if (is_non_value(args)) {
- Uint sz = 2 * arity;
- if (HeapWordsLeft(p) < sz) {
- erts_garbage_collect(p, sz, reg, arity);
- }
- hp = HEAP_TOP(p);
- HEAP_TOP(p) += sz;
- args = NIL;
- while (arity-- > 0) {
- args = CONS(hp, reg[arity], args);
- hp += 2;
- }
- }
- reg[0] = module;
- reg[1] = function;
- reg[2] = args;
- }
- DTRACE_GLOBAL_CALL(p, module, function, arity);
- return ep->address;
} else {
badfun:
p->current = NULL;
@@ -6537,7 +6223,8 @@ erts_is_builtin(Eterm Mod, Eterm Name, int arity)
if ((ep = export_get(&e)) == NULL) {
return 0;
}
- return ep->address == ep->code+3 && (ep->code[3] == (BeamInstr) em_apply_bif);
+ return ep->addressv[erts_active_code_ix()] == ep->code+3
+ && (ep->code[3] == (BeamInstr) em_apply_bif);
}
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index dd788df6e4..25ae480dc7 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -352,27 +352,6 @@ typedef struct LoaderState {
int loc_size; /* Size of location info in bytes (2/4) */
} LoaderState;
-/*
- * 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
-
-#define LINE_INVALID_LOCATION (0)
-
-/*
- * Macros for manipulating locations.
- */
-
-#define IS_VALID_LOCATION(File, Line) \
- ((unsigned) (File) < 255 && (unsigned) (Line) < ((1 << 24) - 1))
-#define MAKE_LOCATION(File, Line) (((File) << 24) | (Line))
-#define LOC_FILE(Loc) ((Loc) >> 24)
-#define LOC_LINE(Loc) ((Loc) & ((1 << 24)-1))
-
#define GetTagAndValue(Stp, Tag, Val) \
do { \
BeamInstr __w; \
@@ -496,7 +475,8 @@ typedef struct LoaderState {
} while (0)
-static void free_state(LoaderState* stp);
+static void free_loader_state(Binary* magic);
+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);
@@ -507,6 +487,7 @@ static int verify_chunks(LoaderState* stp);
static int load_atom_table(LoaderState* stp);
static int load_import_table(LoaderState* stp);
static int read_export_table(LoaderState* stp);
+static int is_bif(Eterm mod, Eterm func, unsigned arity);
static int read_lambda_table(LoaderState* stp);
static int read_literal_table(LoaderState* stp);
static int read_line_table(LoaderState* stp);
@@ -548,22 +529,9 @@ static Eterm native_addresses(Process* p, Eterm mod);
int patch_funentries(Eterm Patchlist);
int patch(Eterm Addresses, Uint fe);
static int safe_mul(UWord a, UWord b, UWord* resp);
-static void lookup_loc(FunctionInfo* fi, BeamInstr* pc,
- BeamInstr* modp, int idx);
-
static int must_swap_floats;
-/*
- * The following variables keep a sorted list of address ranges for
- * each module. It allows us to quickly find a function given an
- * instruction pointer.
- */
-Range* modules = NULL; /* Sorted lists of module addresses. */
-int num_loaded_modules; /* Number of loaded modules. */
-int allocated_modules; /* Number of slots allocated. */
-Range* mid_module = NULL; /* Cached search start point */
-
Uint erts_total_code_size;
/**********************************************************************/
@@ -579,11 +547,7 @@ void init_load(void)
f.fd = 1.0;
must_swap_floats = (f.fw[0] == 0);
- allocated_modules = 128;
- modules = (Range *) erts_alloc(ERTS_ALC_T_MODULE_REFS,
- allocated_modules*sizeof(Range));
- mid_module = modules;
- num_loaded_modules = 0;
+ erts_init_ranges();
}
static void
@@ -595,7 +559,7 @@ define_file(LoaderState* stp, char* name, int idx)
}
Eterm
-erts_load_module(Process *c_p,
+erts_preload_module(Process *c_p,
ErtsProcLocks c_p_locks,
Eterm group_leader, /* Group leader or NIL if none. */
Eterm* modp, /*
@@ -605,15 +569,16 @@ erts_load_module(Process *c_p,
byte* code, /* Points to the code to load */
Uint size) /* Size of code to load. */
{
- LoaderState* stp = erts_alloc_loader_state();
+ Binary* magic = erts_alloc_loader_state();
Eterm retval;
- retval = erts_prepare_loading(stp, c_p, group_leader, modp,
+ ASSERT(!erts_initialized);
+ retval = erts_prepare_loading(magic, c_p, group_leader, modp,
code, size);
if (retval != NIL) {
return retval;
}
- return erts_finish_loading(stp, c_p, c_p_locks, modp);
+ return erts_finish_loading(magic, c_p, c_p_locks, modp);
}
/* #define LOAD_MEMORY_HARD_DEBUG 1*/
@@ -629,11 +594,13 @@ extern void check_allocated_block(Uint type, void *blk);
#endif
Eterm
-erts_prepare_loading(LoaderState* stp, Process *c_p, Eterm group_leader,
+erts_prepare_loading(Binary* magic, Process *c_p, Eterm group_leader,
Eterm* modp, byte* code, Uint unloaded_size)
{
Eterm retval = am_badfile;
+ LoaderState* stp;
+ stp = ERTS_MAGIC_BIN_DATA(magic);
stp->module = *modp;
stp->group_leader = group_leader;
@@ -666,7 +633,7 @@ erts_prepare_loading(LoaderState* stp, Process *c_p, Eterm group_leader,
/*
* Initialize code area.
*/
- stp->code_buffer_size = erts_next_heap_size(2048 + stp->num_functions, 0);
+ stp->code_buffer_size = 2048 + stp->num_functions;
stp->code = (BeamInstr *) erts_alloc(ERTS_ALC_T_CODE,
sizeof(BeamInstr) * stp->code_buffer_size);
@@ -679,8 +646,6 @@ erts_prepare_loading(LoaderState* stp, Process *c_p, Eterm group_leader,
stp->code[MI_COMPILE_PTR] = 0;
stp->code[MI_COMPILE_SIZE] = 0;
stp->code[MI_COMPILE_SIZE_ON_HEAP] = 0;
- stp->code[MI_NUM_BREAKPOINTS] = 0;
-
/*
* Read the atom table.
@@ -774,23 +739,24 @@ erts_prepare_loading(LoaderState* stp, Process *c_p, Eterm group_leader,
load_error:
if (retval != NIL) {
- free_state(stp);
+ free_loader_state(magic);
}
return retval;
}
Eterm
-erts_finish_loading(LoaderState* stp, Process* c_p,
+erts_finish_loading(Binary* magic, Process* c_p,
ErtsProcLocks c_p_locks, Eterm* modp)
{
Eterm retval;
+ LoaderState* stp = ERTS_MAGIC_BIN_DATA(magic);
/*
* No other process may run since we will update the export
* table which is not protected by any locks.
*/
- ERTS_SMP_LC_ASSERT(erts_initialized == 0 ||
+ ERTS_SMP_LC_ASSERT(erts_initialized == 0 || erts_is_code_ix_locked() ||
erts_smp_thr_progress_is_blocking());
/*
@@ -811,7 +777,6 @@ erts_finish_loading(LoaderState* stp, Process* c_p,
* exported and imported functions. This can't fail.
*/
- erts_export_consolidate();
CHKBLK(ERTS_ALC_T_CODE,stp->code);
final_touch(stp);
@@ -837,16 +802,20 @@ erts_finish_loading(LoaderState* stp, Process* c_p,
}
load_error:
- free_state(stp);
+ free_loader_state(magic);
return retval;
}
-LoaderState*
+Binary*
erts_alloc_loader_state(void)
{
LoaderState* stp;
+ Binary* magic;
- stp = erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(LoaderState));
+ magic = erts_create_magic_binary(sizeof(LoaderState),
+ loader_state_dtor);
+ erts_refc_inc(&magic->refc, 1);
+ stp = ERTS_MAGIC_BIN_DATA(magic);
stp->bin = NULL;
stp->function = THE_NON_VALUE; /* Function not known yet */
stp->arity = 0;
@@ -875,76 +844,123 @@ erts_alloc_loader_state(void)
stp->line_instr = 0;
stp->func_line = 0;
stp->fname = 0;
- return stp;
+ return magic;
+}
+
+/*
+ * Return the module name (a tagged atom) for the prepared code
+ * in the magic binary, or NIL if the binary does not contain
+ * prepared code.
+ */
+Eterm
+erts_module_for_prepared_code(Binary* magic)
+{
+ LoaderState* stp;
+
+ if (ERTS_MAGIC_BIN_DESTRUCTOR(magic) != loader_state_dtor) {
+ return NIL;
+ }
+ stp = ERTS_MAGIC_BIN_DATA(magic);
+ if (stp->code != 0) {
+ return stp->module;
+ } else {
+ return NIL;
+ }
}
static void
-free_state(LoaderState* stp)
+free_loader_state(Binary* magic)
{
+ loader_state_dtor(magic);
+ if (erts_refc_dectest(&magic->refc, 0) == 0) {
+ erts_bin_free(magic);
+ }
+}
+
+/*
+ * This destructor function can safely be called multiple times.
+ */
+static void
+loader_state_dtor(Binary* magic)
+{
+ LoaderState* stp = ERTS_MAGIC_BIN_DATA(magic);
+
if (stp->bin != 0) {
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->labels != NULL) {
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->labels);
+ if (stp->labels != 0) {
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->labels);
+ stp->labels = 0;
}
- if (stp->atom != NULL) {
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->atom);
+ if (stp->atom != 0) {
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->atom);
+ stp->atom = 0;
}
- if (stp->import != NULL) {
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->import);
+ if (stp->import != 0) {
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->import);
+ stp->import = 0;
}
- if (stp->export != NULL) {
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->export);
+ if (stp->export != 0) {
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->export);
+ stp->export = 0;
}
if (stp->lambdas != stp->def_lambdas) {
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->lambdas);
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->lambdas);
+ stp->lambdas = stp->def_lambdas;
}
- if (stp->literals != NULL) {
+ if (stp->literals != 0) {
int i;
for (i = 0; i < stp->num_literals; i++) {
- if (stp->literals[i].heap != NULL) {
- erts_free(ERTS_ALC_T_LOADER_TMP,
+ if (stp->literals[i].heap != 0) {
+ erts_free(ERTS_ALC_T_PREPARED_CODE,
(void *) stp->literals[i].heap);
+ stp->literals[i].heap = 0;
}
}
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->literals);
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->literals);
+ stp->literals = 0;
}
- while (stp->literal_patches != NULL) {
+ while (stp->literal_patches != 0) {
LiteralPatch* next = stp->literal_patches->next;
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->literal_patches);
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->literal_patches);
stp->literal_patches = next;
}
- while (stp->string_patches != NULL) {
+ while (stp->string_patches != 0) {
StringPatch* next = stp->string_patches->next;
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->string_patches);
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->string_patches);
stp->string_patches = next;
}
- while (stp->genop_blocks) {
- GenOpBlock* next = stp->genop_blocks->next;
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->genop_blocks);
- stp->genop_blocks = next;
- }
if (stp->line_item != 0) {
- erts_free(ERTS_ALC_T_LOADER_TMP, stp->line_item);
+ erts_free(ERTS_ALC_T_PREPARED_CODE, stp->line_item);
+ stp->line_item = 0;
}
if (stp->line_instr != 0) {
- erts_free(ERTS_ALC_T_LOADER_TMP, stp->line_instr);
+ erts_free(ERTS_ALC_T_PREPARED_CODE, stp->line_instr);
+ stp->line_instr = 0;
}
if (stp->func_line != 0) {
- erts_free(ERTS_ALC_T_LOADER_TMP, stp->func_line);
+ erts_free(ERTS_ALC_T_PREPARED_CODE, stp->func_line);
+ stp->func_line = 0;
}
if (stp->fname != 0) {
- erts_free(ERTS_ALC_T_LOADER_TMP, stp->fname);
+ erts_free(ERTS_ALC_T_PREPARED_CODE, stp->fname);
+ stp->fname = 0;
}
- erts_free(ERTS_ALC_T_LOADER_TMP, stp);
+ /*
+ * The following data items should have been freed earlier.
+ */
+
+ ASSERT(stp->genop_blocks == 0);
}
static Eterm
@@ -954,7 +970,6 @@ insert_new_code(Process *c_p, ErtsProcLocks c_p_locks,
{
Module* modp;
Eterm retval;
- int i;
if ((retval = beam_make_current_old(c_p, c_p_locks, module)) != NIL) {
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
@@ -971,30 +986,15 @@ insert_new_code(Process *c_p, ErtsProcLocks c_p_locks,
erts_total_code_size += size;
modp = erts_put_module(module);
- modp->code = code;
- modp->code_length = size;
- modp->catches = BEAM_CATCHES_NIL; /* Will be filled in later. */
+ modp->curr.code = code;
+ modp->curr.code_length = size;
+ modp->curr.catches = BEAM_CATCHES_NIL; /* Will be filled in later. */
/*
- * Update address table (used for finding a function from a PC value).
+ * Update ranges (used for finding a function from a PC value).
*/
- if (num_loaded_modules == allocated_modules) {
- allocated_modules *= 2;
- modules = (Range *) erts_realloc(ERTS_ALC_T_MODULE_REFS,
- (void *) modules,
- allocated_modules * sizeof(Range));
- }
- for (i = num_loaded_modules; i > 0; i--) {
- if (code > modules[i-1].start) {
- break;
- }
- modules[i] = modules[i-1];
- }
- modules[i].start = code;
- modules[i].end = (BeamInstr *) (((byte *)code) + size);
- num_loaded_modules++;
- mid_module = &modules[num_loaded_modules/2];
+ erts_update_ranges(code, size);
return NIL;
}
@@ -1217,9 +1217,8 @@ load_atom_table(LoaderState* stp)
GetInt(stp, 4, stp->num_atoms);
stp->num_atoms++;
- stp->atom = erts_alloc(ERTS_ALC_T_LOADER_TMP,
- erts_next_heap_size((stp->num_atoms*sizeof(Eterm)),
- 0));
+ stp->atom = erts_alloc(ERTS_ALC_T_PREPARED_CODE,
+ stp->num_atoms*sizeof(Eterm));
/*
* Read all atoms.
@@ -1263,10 +1262,8 @@ load_import_table(LoaderState* stp)
int i;
GetInt(stp, 4, stp->num_imports);
- stp->import = erts_alloc(ERTS_ALC_T_LOADER_TMP,
- erts_next_heap_size((stp->num_imports *
- sizeof(ImportEntry)),
- 0));
+ stp->import = erts_alloc(ERTS_ALC_T_PREPARED_CODE,
+ stp->num_imports * sizeof(ImportEntry));
for (i = 0; i < stp->num_imports; i++) {
int n;
Eterm mod;
@@ -1296,7 +1293,7 @@ load_import_table(LoaderState* stp)
* If the export entry refers to a BIF, get the pointer to
* the BIF function.
*/
- if ((e = erts_find_export_entry(mod, func, arity)) != NULL) {
+ if ((e = erts_active_export_entry(mod, func, arity)) != NULL) {
if (e->code[3] == (BeamInstr) em_apply_bif) {
stp->import[i].bf = (BifFunction) e->code[4];
if (func == am_load_nif && mod == am_erlang && arity == 2) {
@@ -1315,16 +1312,8 @@ load_import_table(LoaderState* stp)
static int
read_export_table(LoaderState* stp)
{
- static struct {
- Eterm mod;
- Eterm func;
- int arity;
- } allow_redef[] = {
- /* The BIFs that are allowed to be redefined by Erlang code */
- {am_erlang,am_apply,2},
- {am_erlang,am_apply,3},
- };
int i;
+ BeamInstr* address;
GetInt(stp, 4, stp->num_exps);
if (stp->num_exps > stp->num_functions) {
@@ -1332,7 +1321,7 @@ read_export_table(LoaderState* stp)
stp->num_exps, stp->num_functions);
}
stp->export
- = (ExportEntry *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ = (ExportEntry *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
(stp->num_exps * sizeof(ExportEntry)));
for (i = 0; i < stp->num_exps; i++) {
@@ -1340,7 +1329,6 @@ read_export_table(LoaderState* stp)
Uint value;
Eterm func;
Uint arity;
- Export* e;
GetInt(stp, 4, n);
GetAtom(stp, n, func);
@@ -1358,29 +1346,34 @@ read_export_table(LoaderState* stp)
if (value == 0) {
LoadError2(stp, "export table entry %d: label %d not resolved", i, n);
}
- stp->export[i].address = stp->code + value;
+ stp->export[i].address = address = stp->code + value;
/*
- * Check that we are not redefining a BIF (except the ones allowed to
- * redefine).
+ * Find out if there is a BIF with the same name.
*/
- if ((e = erts_find_export_entry(stp->module, func, arity)) != NULL) {
- if (e->code[3] == (BeamInstr) em_apply_bif) {
- int j;
- for (j = 0; j < sizeof(allow_redef)/sizeof(allow_redef[0]); j++) {
- if (stp->module == allow_redef[j].mod &&
- func == allow_redef[j].func &&
- arity == allow_redef[j].arity) {
- break;
- }
- }
- if (j == sizeof(allow_redef)/sizeof(allow_redef[0])) {
- LoadError2(stp, "exported function %T/%d redefines BIF",
- func, arity);
- }
- }
+ if (!is_bif(stp->module, func, arity)) {
+ continue;
+ }
+
+ /*
+ * This is a stub for a BIF.
+ *
+ * It should not be exported, and the information in its
+ * func_info instruction should be invalidated so that it
+ * can be filtered out by module_info(functions) and by
+ * any other functions that walk through all local functions.
+ */
+
+ if (stp->labels[n].patches) {
+ LoadError3(stp, "there are local calls to the stub for "
+ "the BIF %T:%T/%d",
+ stp->module, func, arity);
}
+ stp->export[i].address = NULL;
+ address[-1] = 0;
+ address[-2] = NIL;
+ address[-3] = NIL;
}
return 1;
@@ -1388,15 +1381,39 @@ read_export_table(LoaderState* stp)
return 0;
}
+
+static int
+is_bif(Eterm mod, Eterm func, unsigned arity)
+{
+ Export* e = erts_active_export_entry(mod, func, arity);
+ if (e == NULL) {
+ return 0;
+ }
+ if (e->code[3] != (BeamInstr) em_apply_bif) {
+ return 0;
+ }
+ if (mod == am_erlang && func == am_apply && arity == 3) {
+ /*
+ * erlang:apply/3 is a special case -- it is implemented
+ * as an instruction and it is OK to redefine it.
+ */
+ return 0;
+ }
+ return 1;
+}
+
static int
read_lambda_table(LoaderState* stp)
{
int i;
GetInt(stp, 4, stp->num_lambdas);
- stp->lambdas_allocated = stp->num_lambdas;
- stp->lambdas = (Lambda *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
- stp->num_lambdas * sizeof(Lambda));
+ if (stp->num_lambdas > stp->lambdas_allocated) {
+ ASSERT(stp->lambdas == stp->def_lambdas);
+ stp->lambdas_allocated = stp->num_lambdas;
+ stp->lambdas = (Lambda *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
+ stp->num_lambdas * sizeof(Lambda));
+ }
for (i = 0; i < stp->num_lambdas; i++) {
Uint n;
Uint32 Index;
@@ -1446,7 +1463,7 @@ read_literal_table(LoaderState* stp)
stp->file_p = uncompressed;
stp->file_left = (unsigned) uncompressed_sz;
GetInt(stp, 4, stp->num_literals);
- stp->literals = (Literal *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ stp->literals = (Literal *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
stp->num_literals * sizeof(Literal));
stp->allocated_literals = stp->num_literals;
@@ -1466,7 +1483,7 @@ read_literal_table(LoaderState* stp)
if ((heap_size = erts_decode_ext_size(p, sz)) < 0) {
LoadError1(stp, "literal %d: bad external format", i);
}
- hp = stp->literals[i].heap = erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ hp = stp->literals[i].heap = erts_alloc(ERTS_ALC_T_PREPARED_CODE,
heap_size*sizeof(Eterm));
stp->literals[i].off_heap.first = 0;
stp->literals[i].off_heap.overhead = 0;
@@ -1538,7 +1555,7 @@ read_line_table(LoaderState* stp)
*/
num_line_items++;
- lp = (BeamInstr *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ lp = (BeamInstr *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
num_line_items * sizeof(BeamInstr));
stp->line_item = lp;
stp->num_line_items = num_line_items;
@@ -1594,7 +1611,7 @@ read_line_table(LoaderState* stp)
*/
if (stp->num_fnames != 0) {
- stp->fname = (Eterm *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ stp->fname = (Eterm *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
stp->num_fnames *
sizeof(Eterm));
for (i = 0; i < stp->num_fnames; i++) {
@@ -1610,11 +1627,11 @@ read_line_table(LoaderState* stp)
/*
* Allocate the arrays to be filled while code is being loaded.
*/
- stp->line_instr = (LineInstr *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ stp->line_instr = (LineInstr *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
stp->num_line_instrs *
sizeof(LineInstr));
stp->current_li = 0;
- stp->func_line = (int *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ stp->func_line = (int *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
stp->num_functions *
sizeof(int));
@@ -1677,7 +1694,7 @@ read_code_header(LoaderState* stp)
* Initialize label table.
*/
- stp->labels = (Label *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ stp->labels = (Label *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
stp->num_labels * sizeof(Label));
for (i = 0; i < stp->num_labels; i++) {
stp->labels[i].value = 0;
@@ -1703,9 +1720,9 @@ read_code_header(LoaderState* stp)
#define CodeNeed(w) do { \
ASSERT(ci <= code_buffer_size); \
if (code_buffer_size < ci+(w)) { \
- code_buffer_size = erts_next_heap_size(ci+(w), 0); \
- stp->code = code \
- = (BeamInstr *) erts_realloc(ERTS_ALC_T_CODE, \
+ code_buffer_size = 2*ci+(w); \
+ stp->code = code = \
+ (BeamInstr *) erts_realloc(ERTS_ALC_T_CODE, \
(void *) code, \
code_buffer_size * sizeof(BeamInstr)); \
} \
@@ -1731,6 +1748,7 @@ load_code(LoaderState* stp)
GenOp* last_op = NULL;
GenOp** last_op_next = NULL;
int arity;
+ int retval = 1;
/*
* The size of the loaded func_info instruction is needed
@@ -2457,7 +2475,11 @@ load_code(LoaderState* stp)
case op_int_code_end:
stp->code_buffer_size = code_buffer_size;
stp->ci = ci;
- return 1;
+ stp->function = THE_NON_VALUE;
+ stp->genop = NULL;
+ stp->specific_op = -1;
+ retval = 1;
+ goto cleanup;
}
/*
@@ -2471,9 +2493,20 @@ load_code(LoaderState* stp)
}
}
-
load_error:
- return 0;
+ retval = 0;
+
+ cleanup:
+ /*
+ * Clean up everything that is not needed any longer.
+ */
+
+ while (stp->genop_blocks) {
+ GenOpBlock* next = stp->genop_blocks->next;
+ erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->genop_blocks);
+ stp->genop_blocks = next;
+ }
+ return retval;
}
@@ -4265,24 +4298,31 @@ final_touch(LoaderState* stp)
index = next;
}
modp = erts_put_module(stp->module);
- modp->catches = catches;
+ modp->curr.catches = catches;
/*
* Export functions.
*/
for (i = 0; i < stp->num_exps; i++) {
- Export* ep = erts_export_put(stp->module, stp->export[i].function,
- stp->export[i].arity);
+ Export* ep;
+ BeamInstr* address = stp->export[i].address;
+
+ if (address == NULL) {
+ /* Skip stub for a BIF */
+ continue;
+ }
+ ep = erts_export_put(stp->module, stp->export[i].function,
+ stp->export[i].arity);
if (!on_load) {
- ep->address = stp->export[i].address;
+ ep->addressv[erts_staging_code_ix()] = address;
} else {
/*
* Don't make any of the exported functions
* callable yet.
*/
- ep->address = ep->code+3;
- ep->code[4] = (BeamInstr) stp->export[i].address;
+ ep->addressv[erts_staging_code_ix()] = ep->code+3;
+ ep->code[4] = (BeamInstr) address;
}
}
@@ -4926,7 +4966,7 @@ new_label(LoaderState* stp)
int num = stp->num_labels;
stp->num_labels++;
- stp->labels = (Label *) erts_realloc(ERTS_ALC_T_LOADER_TMP,
+ stp->labels = (Label *) erts_realloc(ERTS_ALC_T_PREPARED_CODE,
(void *) stp->labels,
stp->num_labels * sizeof(Label));
stp->labels[num].value = 0;
@@ -4937,7 +4977,8 @@ new_label(LoaderState* stp)
static void
new_literal_patch(LoaderState* stp, int pos)
{
- LiteralPatch* p = erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(LiteralPatch));
+ LiteralPatch* p = erts_alloc(ERTS_ALC_T_PREPARED_CODE,
+ sizeof(LiteralPatch));
p->pos = pos;
p->next = stp->literal_patches;
stp->literal_patches = p;
@@ -4946,7 +4987,7 @@ new_literal_patch(LoaderState* stp, int pos)
static void
new_string_patch(LoaderState* stp, int pos)
{
- StringPatch* p = erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(StringPatch));
+ StringPatch* p = erts_alloc(ERTS_ALC_T_PREPARED_CODE, sizeof(StringPatch));
p->pos = pos;
p->next = stp->string_patches;
stp->string_patches = p;
@@ -4964,14 +5005,14 @@ new_literal(LoaderState* stp, Eterm** hpp, Uint heap_size)
ASSERT(stp->num_literals == 0);
stp->allocated_literals = 8;
need = stp->allocated_literals * sizeof(Literal);
- stp->literals = (Literal *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ stp->literals = (Literal *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
need);
} else if (stp->allocated_literals <= stp->num_literals) {
Uint need;
stp->allocated_literals *= 2;
need = stp->allocated_literals * sizeof(Literal);
- stp->literals = (Literal *) erts_realloc(ERTS_ALC_T_LOADER_TMP,
+ stp->literals = (Literal *) erts_realloc(ERTS_ALC_T_PREPARED_CODE,
(void *) stp->literals,
need);
}
@@ -4980,7 +5021,7 @@ new_literal(LoaderState* stp, Eterm** hpp, Uint heap_size)
lit = stp->literals + stp->num_literals;
lit->offset = 0;
lit->heap_size = heap_size;
- lit->heap = erts_alloc(ERTS_ALC_T_LOADER_TMP, heap_size*sizeof(Eterm));
+ lit->heap = erts_alloc(ERTS_ALC_T_PREPARED_CODE, heap_size*sizeof(Eterm));
lit->term = make_boxed(lit->heap);
lit->off_heap.first = 0;
lit->off_heap.overhead = 0;
@@ -4999,7 +5040,7 @@ erts_module_info_0(Process* p, Eterm module)
return THE_NON_VALUE;
}
- if (erts_get_module(module) == NULL) {
+ if (erts_get_module(module, erts_active_code_ix()) == NULL) {
return THE_NON_VALUE;
}
@@ -5054,32 +5095,43 @@ functions_in_module(Process* p, /* Process whose heap to use. */
BeamInstr* code;
int i;
Uint num_functions;
+ Uint need;
Eterm* hp;
+ Eterm* hp_end;
Eterm result = NIL;
if (is_not_atom(mod)) {
return THE_NON_VALUE;
}
- modp = erts_get_module(mod);
+ modp = erts_get_module(mod, erts_active_code_ix());
if (modp == NULL) {
return THE_NON_VALUE;
}
- code = modp->code;
+ code = modp->curr.code;
num_functions = code[MI_NUM_FUNCTIONS];
- hp = HAlloc(p, 5*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];
Eterm name = (Eterm) func_info[3];
int arity = (int) func_info[4];
Eterm tuple;
- ASSERT(is_atom(name));
- tuple = TUPLE2(hp, name, make_small(arity));
- hp += 3;
- result = CONS(hp, tuple, result);
- hp += 2;
+ /*
+ * If the function name is [], this entry is a stub for
+ * a BIF that should be ignored.
+ */
+ ASSERT(is_atom(name) || is_nil(name));
+ if (is_atom(name)) {
+ tuple = TUPLE2(hp, name, make_small(arity));
+ hp += 3;
+ result = CONS(hp, tuple, result);
+ hp += 2;
+ }
}
+ HRelease(p, hp_end, hp);
return result;
}
@@ -5106,12 +5158,12 @@ native_addresses(Process* p, Eterm mod)
return THE_NON_VALUE;
}
- modp = erts_get_module(mod);
+ modp = erts_get_module(mod, erts_active_code_ix());
if (modp == NULL) {
return THE_NON_VALUE;
}
- code = modp->code;
+ code = modp->curr.code;
num_functions = code[MI_NUM_FUNCTIONS];
need = (6+BIG_UINT_HEAP_SIZE)*num_functions;
hp = HAlloc(p, need);
@@ -5122,9 +5174,11 @@ native_addresses(Process* p, Eterm mod)
int arity = (int) func_info[4];
Eterm tuple;
- ASSERT(is_atom(name));
+ ASSERT(is_atom(name) || is_nil(name)); /* [] if BIF stub */
if (func_info[1] != 0) {
- Eterm addr = erts_bld_uint(&hp, NULL, func_info[1]);
+ Eterm addr;
+ ASSERT(is_atom(name));
+ addr = erts_bld_uint(&hp, NULL, func_info[1]);
tuple = erts_bld_tuple(&hp, NULL, 3, name, make_small(arity), addr);
result = erts_bld_cons(&hp, NULL, tuple, result);
}
@@ -5149,18 +5203,20 @@ exported_from_module(Process* p, /* Process whose heap to use. */
Eterm* hp = NULL;
Eterm* hend = NULL;
Eterm result = NIL;
+ ErtsCodeIndex code_ix;
if (is_not_atom(mod)) {
return THE_NON_VALUE;
}
- for (i = 0; i < export_list_size(); i++) {
- Export* ep = export_list(i);
+ code_ix = erts_active_code_ix();
+ for (i = 0; i < export_list_size(code_ix); i++) {
+ Export* ep = export_list(i,code_ix);
if (ep->code[0] == mod) {
Eterm tuple;
- if (ep->address == ep->code+3 &&
+ if (ep->addressv[code_ix] == ep->code+3 &&
ep->code[3] == (BeamInstr) em_call_error_handler) {
/* There is a call to the function, but it does not exist. */
continue;
@@ -5204,11 +5260,11 @@ attributes_for_module(Process* p, /* Process whose heap to use. */
return THE_NON_VALUE;
}
- modp = erts_get_module(mod);
+ modp = erts_get_module(mod, erts_active_code_ix());
if (modp == NULL) {
return THE_NON_VALUE;
}
- code = modp->code;
+ code = modp->curr.code;
ext = (byte *) code[MI_ATTR_PTR];
if (ext != NULL) {
hp = HAlloc(p, code[MI_ATTR_SIZE_ON_HEAP]);
@@ -5244,11 +5300,11 @@ compilation_info_for_module(Process* p, /* Process whose heap to use. */
return THE_NON_VALUE;
}
- modp = erts_get_module(mod);
+ modp = erts_get_module(mod, erts_active_code_ix());
if (modp == NULL) {
return THE_NON_VALUE;
}
- code = modp->code;
+ code = modp->curr.code;
ext = (byte *) code[MI_COMPILE_PTR];
if (ext != NULL) {
hp = HAlloc(p, code[MI_COMPILE_SIZE_ON_HEAP]);
@@ -5263,113 +5319,6 @@ compilation_info_for_module(Process* p, /* Process whose heap to use. */
}
/*
- * Find a function from the given pc and fill information in
- * the FunctionInfo struct. If the full_info is non-zero, fill
- * in all available information (including location in the
- * source code). If no function is found, the 'current' field
- * will be set to NULL.
- */
-
-void
-erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info)
-{
- Range* low = modules;
- Range* high = low + num_loaded_modules;
- Range* mid = mid_module;
-
- fi->current = NULL;
- fi->needed = 5;
- fi->loc = LINE_INVALID_LOCATION;
- while (low < high) {
- if (pc < mid->start) {
- high = mid;
- } else if (pc > mid->end) {
- low = mid + 1;
- } else {
- BeamInstr** low1 = (BeamInstr **) (mid->start + MI_FUNCTIONS);
- BeamInstr** high1 = low1 + mid->start[MI_NUM_FUNCTIONS];
- BeamInstr** mid1;
-
- while (low1 < high1) {
- mid1 = low1 + (high1-low1) / 2;
- if (pc < mid1[0]) {
- high1 = mid1;
- } else if (pc < mid1[1]) {
- mid_module = mid;
- fi->current = mid1[0]+2;
- if (full_info) {
- BeamInstr** fp = (BeamInstr **) (mid->start +
- MI_FUNCTIONS);
- int idx = mid1 - fp;
- lookup_loc(fi, pc, mid->start, idx);
- }
- return;
- } else {
- low1 = mid1 + 1;
- }
- }
- return;
- }
- mid = low + (high-low) / 2;
- }
-}
-
-static void
-lookup_loc(FunctionInfo* fi, BeamInstr* orig_pc, BeamInstr* modp, int idx)
-{
- Eterm* line = (Eterm *) modp[MI_LINE_TABLE];
- Eterm* low;
- Eterm* high;
- Eterm* mid;
- Eterm pc;
-
- if (line == 0) {
- 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];
- 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];
-
- if (line[MI_LINE_LOC_SIZE] == 2) {
- Uint16* loc_table =
- (Uint16 *) (BeamInstr) line[MI_LINE_LOC_TAB];
- fi->loc = loc_table[index];
- } else {
- Uint32* loc_table =
- (Uint32 *) (BeamInstr) line[MI_LINE_LOC_TAB];
- ASSERT(line[MI_LINE_LOC_SIZE] == 4);
- fi->loc = loc_table[index];
- }
- if (fi->loc == LINE_INVALID_LOCATION) {
- return;
- }
- fi->needed += 3+2+3+2;
- file = LOC_FILE(fi->loc);
- if (file == 0) {
- /* Special case: Module name with ".erl" appended */
- Atom* mod_atom = atom_tab(atom_val(fi->current[0]));
- fi->needed += 2*(mod_atom->len+4);
- } else {
- Atom* ap = atom_tab(atom_val((fi->fname_ptr)[file-1]));
- fi->needed += 2*ap->len;
- }
- return;
- } else {
- low = mid + 1;
- }
- }
-}
-
-/*
* Build a single {M,F,A,Loction} item to be part of
* a stack trace.
*/
@@ -5449,6 +5398,7 @@ code_get_chunk_2(BIF_ALIST_2)
Process* p = BIF_P;
Eterm Bin = BIF_ARG_1;
Eterm Chunk = BIF_ARG_2;
+ Binary* magic = 0;
LoaderState* stp;
Uint chunk = 0;
ErlSubBin* sb;
@@ -5461,12 +5411,13 @@ code_get_chunk_2(BIF_ALIST_2)
Eterm real_bin;
byte* temp_alloc = NULL;
- stp = erts_alloc_loader_state();
+ magic = erts_alloc_loader_state();
+ stp = ERTS_MAGIC_BIN_DATA(magic);
if ((start = erts_get_aligned_binary_bytes(Bin, &temp_alloc)) == NULL) {
error:
erts_free_aligned_binary_bytes(temp_alloc);
- if (stp) {
- free_state(stp);
+ if (magic) {
+ free_loader_state(magic);
}
BIF_ERROR(p, BADARG);
}
@@ -5511,7 +5462,7 @@ code_get_chunk_2(BIF_ALIST_2)
done:
erts_free_aligned_binary_bytes(temp_alloc);
- free_state(stp);
+ free_loader_state(magic);
return res;
}
@@ -5524,14 +5475,16 @@ code_module_md5_1(BIF_ALIST_1)
{
Process* p = BIF_P;
Eterm Bin = BIF_ARG_1;
+ Binary* magic;
LoaderState* stp;
byte* bytes;
byte* temp_alloc = NULL;
Eterm res;
- stp = erts_alloc_loader_state();
+ magic = erts_alloc_loader_state();
+ stp = ERTS_MAGIC_BIN_DATA(magic);
if ((bytes = erts_get_aligned_binary_bytes(Bin, &temp_alloc)) == NULL) {
- free_state(stp);
+ free_loader_state(magic);
BIF_ERROR(p, BADARG);
}
stp->module = THE_NON_VALUE; /* Suppress diagnostiscs */
@@ -5545,7 +5498,7 @@ code_module_md5_1(BIF_ALIST_1)
done:
erts_free_aligned_binary_bytes(temp_alloc);
- free_state(stp);
+ free_loader_state(magic);
return res;
}
@@ -5574,7 +5527,8 @@ 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. */
- BeamInstr* size_word) /* Where to store size of info. */
+ BeamInstr* size_word, /* Where to store size into info. */
+ BeamInstr* size_on_heap_word) /* Where to store size on heap. */
{
Sint decoded_size;
Uint size = stp->chunks[chunk].size;
@@ -5585,7 +5539,8 @@ stub_copy_info(LoaderState* stp,
if (decoded_size < 0) {
return 0;
}
- *size_word = decoded_size;
+ *size_word = (BeamInstr) size;
+ *size_on_heap_word = decoded_size;
}
return info + size;
}
@@ -5601,7 +5556,7 @@ stub_read_export_table(LoaderState* stp)
stp->num_exps, stp->num_functions);
}
stp->export
- = (ExportEntry *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ = (ExportEntry *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
stp->num_exps * sizeof(ExportEntry));
for (i = 0; i < stp->num_exps; i++) {
@@ -5627,20 +5582,29 @@ stub_final_touch(LoaderState* stp, BeamInstr* fp)
{
int i;
int n = stp->num_exps;
+ Eterm mod = fp[2];
Eterm function = fp[3];
int arity = fp[4];
#ifdef HIPE
Lambda* lp;
#endif
+ if (is_bif(mod, function, arity)) {
+ fp[1] = 0;
+ fp[2] = 0;
+ fp[3] = 0;
+ fp[4] = 0;
+ return;
+ }
+
/*
* Test if the function should be exported.
*/
for (i = 0; i < n; i++) {
if (stp->export[i].function == function && stp->export[i].arity == arity) {
- Export* ep = erts_export_put(fp[2], function, arity);
- ep->address = fp+5;
+ Export* ep = erts_export_put(mod, function, arity);
+ ep->addressv[erts_staging_code_ix()] = fp+5;
return;
}
}
@@ -5819,6 +5783,7 @@ patch_funentries(Eterm Patchlist)
Eterm
erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
{
+ Binary* magic;
LoaderState* stp;
BeamInstr Funcs;
BeamInstr Patchlist;
@@ -5840,7 +5805,8 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
* Must initialize stp->lambdas here because the error handling code
* at label 'error' uses it.
*/
- stp = erts_alloc_loader_state();
+ magic = erts_alloc_loader_state();
+ stp = ERTS_MAGIC_BIN_DATA(magic);
if (is_not_atom(Mod)) {
goto error;
@@ -5920,7 +5886,6 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
code[MI_COMPILE_PTR] = 0;
code[MI_COMPILE_SIZE] = 0;
code[MI_COMPILE_SIZE_ON_HEAP] = 0;
- code[MI_NUM_BREAKPOINTS] = 0;
code[MI_LITERALS_START] = 0;
code[MI_LITERALS_END] = 0;
code[MI_LITERALS_OFF_HEAP] = 0;
@@ -5997,12 +5962,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_ON_HEAP);
+ code+MI_ATTR_PTR,
+ code+MI_ATTR_SIZE,
+ code+MI_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_ON_HEAP);
+ code+MI_COMPILE_PTR,
+ code+MI_COMPILE_SIZE,
+ code+MI_COMPILE_SIZE_ON_HEAP);
if (info == NULL) {
goto error;
}
@@ -6028,13 +5997,13 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
if (patch_funentries(Patchlist)) {
erts_free_aligned_binary_bytes(temp_alloc);
- free_state(stp);
+ free_loader_state(magic);
return Mod;
}
error:
erts_free_aligned_binary_bytes(temp_alloc);
- free_state(stp);
+ free_loader_state(magic);
BIF_ERROR(p, BADARG);
}
@@ -6051,3 +6020,4 @@ static int safe_mul(UWord a, UWord b, UWord* resp)
return (res / b) == a;
}
}
+
diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h
index 997ba197db..1048f258a5 100644
--- a/erts/emulator/beam/beam_load.h
+++ b/erts/emulator/beam/beam_load.h
@@ -49,11 +49,6 @@ extern void** beam_ops;
extern BeamInstr beam_debug_apply[];
extern BeamInstr* em_call_error_handler;
extern BeamInstr* em_apply_bif;
-extern BeamInstr* em_call_traced_function;
-typedef struct {
- BeamInstr* start; /* Pointer to start of module. */
- BeamInstr* end; /* Points one word beyond last function in module. */
-} Range;
/*
* The following variables keep a sorted list of address ranges for
@@ -61,11 +56,6 @@ typedef struct {
* instruction pointer.
*/
-extern Range* modules;
-extern int num_loaded_modules;
-extern int allocated_modules;
-extern Range* mid_module;
-
/* Total code size in bytes */
extern Uint erts_total_code_size;
/*
@@ -94,27 +84,22 @@ extern Uint erts_total_code_size;
#define MI_COMPILE_SIZE_ON_HEAP 6
/*
- * Number of breakpoints in module is stored in this word
- */
-#define MI_NUM_BREAKPOINTS 7
-
-/*
* Literal area (constant pool).
*/
-#define MI_LITERALS_START 8
-#define MI_LITERALS_END 9
-#define MI_LITERALS_OFF_HEAP 10
+#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 11
+#define MI_ON_LOAD_FUNCTION_PTR 10
/*
* Pointer to the line table (or NULL if none).
*/
-#define MI_LINE_TABLE 12
+#define MI_LINE_TABLE 11
/*
* Start of function pointer table. This table contains pointers to
@@ -125,5 +110,27 @@ extern Uint erts_total_code_size;
* this table.
*/
-#define MI_FUNCTIONS 13
+#define MI_FUNCTIONS 12
+
+/*
+ * 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
+
+#define LINE_INVALID_LOCATION (0)
+
+/*
+ * Macros for manipulating locations.
+ */
+
+#define IS_VALID_LOCATION(File, Line) \
+ ((unsigned) (File) < 255 && (unsigned) (Line) < ((1 << 24) - 1))
+#define MAKE_LOCATION(File, Line) (((File) << 24) | (Line))
+#define LOC_FILE(Loc) ((Loc) >> 24)
+#define LOC_LINE(Loc) ((Loc) & ((1 << 24)-1))
+
#endif /* _BEAM_LOAD_H */
diff --git a/erts/emulator/beam/beam_ranges.c b/erts/emulator/beam/beam_ranges.c
new file mode 100644
index 0000000000..0f2d5d0c2a
--- /dev/null
+++ b/erts/emulator/beam/beam_ranges.c
@@ -0,0 +1,349 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2012. 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
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_vm.h"
+#include "global.h"
+#include "beam_load.h"
+
+typedef struct {
+ BeamInstr* start; /* Pointer to start of module. */
+ erts_smp_atomic_t end; /* (BeamInstr*) Points one word beyond last function in module. */
+} Range;
+
+/* Range 'end' needs to be atomic as we purge module
+ by setting end=start in active code_ix */
+#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);
+
+/*
+ * The following variables keep a sorted list of address ranges for
+ * each module. It allows us to quickly find a function given an
+ * instruction pointer.
+ */
+struct ranges {
+ Range* modules; /* Sorted lists of module addresses. */
+ Sint n; /* Number of range entries. */
+ Sint allocated; /* Number of allocated entries. */
+ erts_smp_atomic_t mid; /* Cached search start point */
+};
+static struct ranges r[ERTS_NUM_CODE_IX];
+static erts_smp_atomic_t mem_used;
+
+#ifdef HARD_DEBUG
+static void check_consistency(struct ranges* p)
+{
+ int i;
+
+ ASSERT(p->n <= p->allocated);
+ ASSERT((Uint)(p->mid - p->modules) < p->n ||
+ (p->mid == p->modules && p->n == 0));
+ for (i = 0; i < p->n; i++) {
+ ASSERT(p->modules[i].start <= RANGE_END(&p->modules[i]));
+ ASSERT(!i || RANGE_END(&p->modules[i-1]) < p->modules[i].start);
+ }
+}
+# define CHECK(r) check_consistency(r)
+#else
+# define CHECK(r)
+#endif /* HARD_DEBUG */
+
+
+void
+erts_init_ranges(void)
+{
+ Sint i;
+
+ erts_smp_atomic_init_nob(&mem_used, 0);
+ for (i = 0; i < ERTS_NUM_CODE_IX; i++) {
+ r[i].modules = 0;
+ r[i].n = 0;
+ r[i].allocated = 0;
+ erts_smp_atomic_init_nob(&r[i].mid, 0);
+ }
+}
+
+void
+erts_start_staging_ranges(void)
+{
+ ErtsCodeIndex dst = erts_staging_code_ix();
+
+ 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;
+ }
+}
+
+void
+erts_end_staging_ranges(int commit)
+{
+ ErtsCodeIndex dst = erts_staging_code_ix();
+
+ if (commit && r[dst].modules == NULL) {
+ Sint i;
+ Sint n;
+
+ /* No modules added, just clone src and remove purged code. */
+ ErtsCodeIndex src = erts_active_code_ix();
+
+ 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;
+ 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++;
+ }
+ }
+ r[dst].n = n;
+ erts_smp_atomic_set_nob(&r[dst].mid,
+ (erts_aint_t) (r[dst].modules + n / 2));
+ }
+}
+
+void
+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.
+ */
+ 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);
+ }
+ 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]);
+}
+
+void
+erts_remove_from_ranges(BeamInstr* code)
+{
+ Range* rp = find_range(code);
+ erts_smp_atomic_set_nob(&rp->end, (erts_aint_t)rp->start);
+}
+
+UWord
+erts_ranges_sz(void)
+{
+ return erts_smp_atomic_read_nob(&mem_used) * sizeof(Range);
+}
+
+/*
+ * Find a function from the given pc and fill information in
+ * the FunctionInfo struct. If the full_info is non-zero, fill
+ * in all available information (including location in the
+ * source code). If no function is found, the 'current' field
+ * will be set to NULL.
+ */
+
+void
+erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info)
+{
+ BeamInstr** low;
+ BeamInstr** high;
+ BeamInstr** mid;
+ Range* rp;
+
+ fi->current = NULL;
+ fi->needed = 5;
+ fi->loc = LINE_INVALID_LOCATION;
+ rp = find_range(pc);
+ if (rp == 0) {
+ return;
+ }
+
+ low = (BeamInstr **) (rp->start + MI_FUNCTIONS);
+ high = low + rp->start[MI_NUM_FUNCTIONS];
+ while (low < high) {
+ mid = low + (high-low) / 2;
+ if (pc < mid[0]) {
+ high = mid;
+ } else if (pc < mid[1]) {
+ fi->current = mid[0]+2;
+ if (full_info) {
+ BeamInstr** fp = (BeamInstr **) (rp->start +
+ MI_FUNCTIONS);
+ int idx = mid - fp;
+ lookup_loc(fi, pc, rp->start, idx);
+ }
+ return;
+ } else {
+ low = mid + 1;
+ }
+ }
+}
+
+static Range*
+find_range(BeamInstr* pc)
+{
+ ErtsCodeIndex active = erts_active_code_ix();
+ Range* low = r[active].modules;
+ Range* high = low + r[active].n;
+ Range* mid = (Range *) erts_smp_atomic_read_nob(&r[active].mid);
+
+ CHECK(&r[active]);
+ while (low < high) {
+ if (pc < mid->start) {
+ high = mid;
+ } else if (pc > RANGE_END(mid)) {
+ low = mid + 1;
+ } else {
+ erts_smp_atomic_set_nob(&r[active].mid, (erts_aint_t) mid);
+ return mid;
+ }
+ mid = low + (high-low) / 2;
+ }
+ return 0;
+}
+
+static void
+lookup_loc(FunctionInfo* fi, BeamInstr* orig_pc, BeamInstr* modp, int idx)
+{
+ Eterm* line = (Eterm *) modp[MI_LINE_TABLE];
+ Eterm* low;
+ Eterm* high;
+ Eterm* mid;
+ Eterm pc;
+
+ if (line == 0) {
+ 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];
+ 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];
+
+ if (line[MI_LINE_LOC_SIZE] == 2) {
+ Uint16* loc_table =
+ (Uint16 *) (BeamInstr) line[MI_LINE_LOC_TAB];
+ fi->loc = loc_table[index];
+ } else {
+ Uint32* loc_table =
+ (Uint32 *) (BeamInstr) line[MI_LINE_LOC_TAB];
+ ASSERT(line[MI_LINE_LOC_SIZE] == 4);
+ fi->loc = loc_table[index];
+ }
+ if (fi->loc == LINE_INVALID_LOCATION) {
+ return;
+ }
+ fi->needed += 3+2+3+2;
+ file = LOC_FILE(fi->loc);
+ if (file == 0) {
+ /* Special case: Module name with ".erl" appended */
+ Atom* mod_atom = atom_tab(atom_val(fi->current[0]));
+ fi->needed += 2*(mod_atom->len+4);
+ } else {
+ Atom* ap = atom_tab(atom_val((fi->fname_ptr)[file-1]));
+ fi->needed += 2*ap->len;
+ }
+ return;
+ } else {
+ low = mid + 1;
+ }
+ }
+}
diff --git a/erts/emulator/beam/benchmark.c b/erts/emulator/beam/benchmark.c
index 7ac14b8e8b..8613131176 100644
--- a/erts/emulator/beam/benchmark.c
+++ b/erts/emulator/beam/benchmark.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2012. 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/beam/benchmark.h b/erts/emulator/beam/benchmark.h
index 003e821bce..766edaac42 100644
--- a/erts/emulator/beam/benchmark.h
+++ b/erts/emulator/beam/benchmark.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2012. 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/beam/bif.c b/erts/emulator/beam/bif.c
index fc00b42454..1cdce49eef 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -233,15 +233,17 @@ BIF_RETTYPE link_1(BIF_ALIST_1)
BIF_ERROR(BIF_P, BADARG);
- res_no_proc:
- if (BIF_P->flags & F_TRAPEXIT) {
- ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN;
- erts_deliver_exit_message(BIF_ARG_1, BIF_P, &locks, am_noproc, NIL);
- erts_smp_proc_unlock(BIF_P, ~ERTS_PROC_LOCK_MAIN & locks);
- BIF_RET(am_true);
+res_no_proc: {
+ erts_aint32_t state = erts_smp_atomic32_read_nob(&BIF_P->state);
+ if (state & ERTS_PSFLG_TRAP_EXIT) {
+ ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN;
+ erts_deliver_exit_message(BIF_ARG_1, BIF_P, &locks, am_noproc, NIL);
+ erts_smp_proc_unlock(BIF_P, ~ERTS_PROC_LOCK_MAIN & locks);
+ BIF_RET(am_true);
+ }
+ else
+ BIF_ERROR(BIF_P, EXC_NOPROC);
}
- else
- BIF_ERROR(BIF_P, EXC_NOPROC);
}
#define ERTS_DEMONITOR_FALSE 2
@@ -1103,8 +1105,9 @@ BIF_RETTYPE hibernate_3(BIF_ALIST_3)
if (erts_hibernate(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, reg)) {
/*
- * If hibernate succeeded, TRAP. The process will be suspended
- * if status is P_WAITING or continue (if any message was in the queue).
+ * If hibernate succeeded, TRAP. The process will be wait in a
+ * hibernated state if its state is inactive (!ERTS_PSFLG_ACTIVE);
+ * otherwise, continue executing (if any message was in the queue).
*/
BIF_TRAP_CODE_PTR_(BIF_P, BIF_P->i);
}
@@ -1403,9 +1406,8 @@ BIF_RETTYPE exit_2(BIF_ALIST_2)
}
else {
rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- rp = erts_pid2proc_opt(BIF_P, ERTS_PROC_LOCK_MAIN,
- BIF_ARG_1, rp_locks,
- ERTS_P2P_FLG_SMP_INC_REFC);
+ rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
+ BIF_ARG_1, rp_locks);
if (!rp) {
BIF_RET(am_true);
}
@@ -1427,8 +1429,6 @@ BIF_RETTYPE exit_2(BIF_ALIST_2)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
- if (rp != BIF_P)
- erts_smp_proc_dec_refc(rp);
#endif
/*
* We may have exited ourselves and may have to take action.
@@ -1502,14 +1502,13 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
BIF_RET(old_value);
}
else if (BIF_ARG_1 == am_priority) {
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_STATUS);
old_value = erts_set_process_priority(BIF_P, BIF_ARG_2);
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS);
if (old_value == THE_NON_VALUE)
goto error;
BIF_RET(old_value);
}
else if (BIF_ARG_1 == am_trap_exit) {
+ erts_aint32_t state;
Uint trap_exit;
if (BIF_ARG_2 == am_true) {
trap_exit = 1;
@@ -1524,59 +1523,52 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
* For more info, see implementation of erts_send_exit_signal().
*/
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_STATUS);
+ ERTS_SMP_LC_ASSERT((ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS)
+ & erts_proc_lc_my_proc_locks(BIF_P));
ERTS_SMP_BIF_CHK_PENDING_EXIT(BIF_P,
ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
- old_value = ERTS_PROC_IS_TRAPPING_EXITS(BIF_P) ? am_true : am_false;
- if (trap_exit) {
- ERTS_PROC_SET_TRAP_EXIT(BIF_P);
- } else {
- ERTS_PROC_UNSET_TRAP_EXIT(BIF_P);
- }
+ if (trap_exit)
+ state = erts_smp_atomic32_read_bor_nob(&BIF_P->state,
+ ERTS_PSFLG_TRAP_EXIT);
+ else
+ state = erts_smp_atomic32_read_band_nob(&BIF_P->state,
+ ~ERTS_PSFLG_TRAP_EXIT);
+ old_value = (state & ERTS_PSFLG_TRAP_EXIT) ? am_true : am_false;
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS);
BIF_RET(old_value);
}
else if (BIF_ARG_1 == am_scheduler) {
- int yield;
- ErtsRunQueue *old;
- ErtsRunQueue *new;
+ ErtsRunQueue *old, *new, *curr;
Sint sched;
+ erts_aint32_t state;
+
if (!is_small(BIF_ARG_2))
goto error;
sched = signed_val(BIF_ARG_2);
if (sched < 0 || erts_no_schedulers < sched)
goto error;
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_STATUS);
- old = BIF_P->bound_runq;
-#ifdef ERTS_SMP
- ASSERT(!old || old == BIF_P->run_queue);
-#endif
- new = !sched ? NULL : erts_schedid2runq(sched);
-#ifndef ERTS_SMP
- yield = 0;
-#else
- if (new == old)
- yield = 0;
+
+ if (sched == 0) {
+ new = NULL;
+ state = erts_smp_atomic32_read_band_mb(&BIF_P->state,
+ ~ERTS_PSFLG_BOUND);
+ }
else {
- ErtsRunQueue *curr = BIF_P->run_queue;
- if (!new)
- erts_smp_runq_lock(curr);
- else
- erts_smp_runqs_lock(curr, new);
- yield = new && BIF_P->run_queue != new;
-#endif
- BIF_P->bound_runq = new;
+ new = erts_schedid2runq(sched);
#ifdef ERTS_SMP
- if (new)
- BIF_P->run_queue = new;
- if (!new)
- erts_smp_runq_unlock(curr);
- else
- erts_smp_runqs_unlock(curr, new);
- }
+ erts_atomic_set_nob(&BIF_P->run_queue, (erts_aint_t) new);
#endif
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS);
+ state = erts_smp_atomic32_read_bor_mb(&BIF_P->state,
+ ERTS_PSFLG_BOUND);
+ }
+
+ curr = ERTS_GET_SCHEDULER_DATA_FROM_PROC(BIF_P)->run_queue;
+ old = (ERTS_PSFLG_BOUND & state) ? curr : NULL;
+
+ ASSERT(!old || old == curr);
+
old_value = old ? make_small(old->ix+1) : make_small(0);
- if (yield)
+ if (new && new != curr)
ERTS_BIF_YIELD_RETURN_X(BIF_P, old_value, am_scheduler);
else
BIF_RET(old_value);
@@ -1826,8 +1818,7 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend) {
if (internal_pid_index(to) >= erts_max_processes)
return SEND_BADARG;
- rp = erts_pid2proc_opt(p, ERTS_PROC_LOCK_MAIN,
- to, 0, ERTS_P2P_FLG_SMP_INC_REFC);
+ rp = erts_proc_lookup_raw(to);
if (!rp) {
ERTS_SMP_ASSERT_IS_NOT_EXITING(p);
@@ -1865,7 +1856,7 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend) {
}
erts_whereis_name(p, ERTS_PROC_LOCK_MAIN,
to,
- &rp, 0, ERTS_P2P_FLG_SMP_INC_REFC,
+ &rp, 0, 0,
&pt);
if (pt) {
@@ -2017,7 +2008,7 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend) {
erts_whereis_name(p, ERTS_PROC_LOCK_MAIN,
tp[1],
- &rp, 0, ERTS_P2P_FLG_SMP_INC_REFC,
+ &rp, 0, 0,
&pt);
if (pt) {
portid = pt->id;
@@ -2060,23 +2051,15 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend) {
rp_locks |= ERTS_PROC_LOCK_MAIN;
#endif
/* send to local process */
- erts_send_message(p, rp, &rp_locks, msg, 0);
- if (!erts_use_sender_punish)
+ res = erts_send_message(p, rp, &rp_locks, msg, 0);
+ if (erts_use_sender_punish)
+ res *= 4;
+ else
res = 0;
- else {
-#ifdef ERTS_SMP
- res = rp->msg_inq.len*4;
- if (ERTS_PROC_LOCK_MAIN & rp_locks)
- res += rp->msg.len*4;
-#else
- res = rp->msg.len*4;
-#endif
- }
erts_smp_proc_unlock(rp,
p == rp
? (rp_locks & ~ERTS_PROC_LOCK_MAIN)
: rp_locks);
- erts_smp_proc_dec_refc(rp);
return res;
}
}
@@ -2861,7 +2844,7 @@ BIF_RETTYPE float_to_list_1(BIF_ALIST_1)
if (is_not_float(BIF_ARG_1))
BIF_ERROR(BIF_P, BADARG);
GET_DOUBLE(BIF_ARG_1, f);
- if ((i = sys_double_to_chars(f.fd, fbuf)) <= 0)
+ if ((i = sys_double_to_chars(f.fd, fbuf, sizeof(fbuf))) <= 0)
BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
need = i*2;
hp = HAlloc(BIF_P, need);
@@ -3486,7 +3469,7 @@ BIF_RETTYPE garbage_collect_1(BIF_ALIST_1)
if (rp == ERTS_PROC_LOCK_BUSY)
ERTS_BIF_YIELD1(bif_export[BIF_garbage_collect_1], BIF_P, BIF_ARG_1);
#else
- rp = erts_pid2proc(BIF_P, 0, BIF_ARG_1, 0);
+ rp = erts_proc_lookup(BIF_ARG_1);
#endif
if (!rp)
BIF_RET(am_false);
@@ -3780,7 +3763,8 @@ BIF_RETTYPE function_exported_3(BIF_ALIST_3)
is_not_small(BIF_ARG_3)) {
BIF_ERROR(BIF_P, BADARG);
}
- if (erts_find_function(BIF_ARG_1, BIF_ARG_2, signed_val(BIF_ARG_3)) == NULL) {
+ if (erts_find_function(BIF_ARG_1, BIF_ARG_2, signed_val(BIF_ARG_3),
+ erts_active_code_ix()) == NULL) {
BIF_RET(am_false);
}
BIF_RET(am_true);
@@ -4216,8 +4200,8 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
erts_smp_thr_progress_block();
for (i = 0; i < erts_max_processes; i++) {
- if (process_tab[i] != (Process*) 0) {
- Process* p = process_tab[i];
+ Process *p = erts_pix2proc(i);
+ if (p) {
#ifdef USE_VM_PROBES
p->seq_trace_token = (p->dt_utag != NIL) ? am_have_dt_utag : NIL;
#else
@@ -4513,6 +4497,21 @@ erts_bif_prep_await_proc_exit_apply_trap(Process *c_p,
Export bif_return_trap_export;
+void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a,
+ Eterm (*bif)(BIF_ALIST_0))
+{
+ int i;
+ sys_memset((void *) ep, 0, sizeof(Export));
+ for (i=0; i<ERTS_NUM_CODE_IX; i++) {
+ ep->addressv[i] = &ep->code[3];
+ }
+ ep->code[0] = m;
+ ep->code[1] = f;
+ ep->code[2] = a;
+ ep->code[3] = (BeamInstr) em_apply_bif;
+ ep->code[4] = (BeamInstr) bif;
+}
+
void erts_init_bif(void)
{
reference0 = 0;
@@ -4528,17 +4527,13 @@ void erts_init_bif(void)
* yield the calling process traps to. The only thing it does:
* return the value passed as argument.
*/
- sys_memset((void *) &bif_return_trap_export, 0, sizeof(Export));
- bif_return_trap_export.address = &bif_return_trap_export.code[3];
- bif_return_trap_export.code[0] = am_erlang;
- bif_return_trap_export.code[1] = am_bif_return_trap;
+ erts_init_trap_export(&bif_return_trap_export, am_erlang, am_bif_return_trap,
#ifdef DEBUG
- bif_return_trap_export.code[2] = 2;
+ 2
#else
- bif_return_trap_export.code[2] = 1;
+ 1
#endif
- bif_return_trap_export.code[3] = (BeamInstr) em_apply_bif;
- bif_return_trap_export.code[4] = (BeamInstr) &bif_return_trap;
+ , &bif_return_trap);
flush_monitor_message_trap = erts_export_put(am_erlang,
am_flush_monitor_message,
diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h
index d20089a9fb..7cb2c78815 100644
--- a/erts/emulator/beam/bif.h
+++ b/erts/emulator/beam/bif.h
@@ -125,7 +125,7 @@ do { \
#define ERTS_BIF_PREP_TRAP0(Ret, Trap, Proc) \
do { \
(Proc)->arity = 0; \
- (Proc)->i = (BeamInstr*) ((Trap)->address); \
+ (Proc)->i = (BeamInstr*) ((Trap)->addressv[erts_active_code_ix()]); \
(Proc)->freason = TRAP; \
(Ret) = THE_NON_VALUE; \
} while (0)
@@ -135,7 +135,7 @@ do { \
Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
(Proc)->arity = 1; \
reg[0] = (Eterm) (A0); \
- (Proc)->i = (BeamInstr*) ((Trap)->address); \
+ (Proc)->i = (BeamInstr*) ((Trap)->addressv[erts_active_code_ix()]); \
(Proc)->freason = TRAP; \
(Ret) = THE_NON_VALUE; \
} while (0)
@@ -146,7 +146,7 @@ do { \
(Proc)->arity = 2; \
reg[0] = (Eterm) (A0); \
reg[1] = (Eterm) (A1); \
- (Proc)->i = (BeamInstr*) ((Trap)->address); \
+ (Proc)->i = (BeamInstr*) ((Trap)->addressv[erts_active_code_ix()]); \
(Proc)->freason = TRAP; \
(Ret) = THE_NON_VALUE; \
} while (0)
@@ -158,7 +158,7 @@ do { \
reg[0] = (Eterm) (A0); \
reg[1] = (Eterm) (A1); \
reg[2] = (Eterm) (A2); \
- (Proc)->i = (BeamInstr*) ((Trap)->address); \
+ (Proc)->i = (BeamInstr*) ((Trap)->addressv[erts_active_code_ix()]); \
(Proc)->freason = TRAP; \
(Ret) = THE_NON_VALUE; \
} while (0)
@@ -170,13 +170,13 @@ do { \
reg[0] = (Eterm) (A0); \
reg[1] = (Eterm) (A1); \
reg[2] = (Eterm) (A2); \
- (Proc)->i = (BeamInstr*) ((Trap)->address); \
+ (Proc)->i = (BeamInstr*) ((Trap)->addressv[erts_active_code_ix()]); \
(Proc)->freason = TRAP; \
} while (0)
#define BIF_TRAP0(p, Trap_) do { \
(p)->arity = 0; \
- (p)->i = (BeamInstr*) ((Trap_)->address); \
+ (p)->i = (BeamInstr*) ((Trap_)->addressv[erts_active_code_ix()]); \
(p)->freason = TRAP; \
return THE_NON_VALUE; \
} while(0)
@@ -185,7 +185,7 @@ do { \
Eterm* reg = ERTS_PROC_GET_SCHDATA((p))->x_reg_array; \
(p)->arity = 1; \
reg[0] = (A0); \
- (p)->i = (BeamInstr*) ((Trap_)->address); \
+ (p)->i = (BeamInstr*) ((Trap_)->addressv[erts_active_code_ix()]); \
(p)->freason = TRAP; \
return THE_NON_VALUE; \
} while(0)
@@ -195,7 +195,7 @@ do { \
(p)->arity = 2; \
reg[0] = (A0); \
reg[1] = (A1); \
- (p)->i = (BeamInstr*) ((Trap_)->address); \
+ (p)->i = (BeamInstr*) ((Trap_)->addressv[erts_active_code_ix()]); \
(p)->freason = TRAP; \
return THE_NON_VALUE; \
} while(0)
@@ -206,7 +206,7 @@ do { \
reg[0] = (A0); \
reg[1] = (A1); \
reg[2] = (A2); \
- (p)->i = (BeamInstr*) ((Trap_)->address); \
+ (p)->i = (BeamInstr*) ((Trap_)->addressv[erts_active_code_ix()]); \
(p)->freason = TRAP; \
return THE_NON_VALUE; \
} while(0)
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 797bce43ab..f7dad2767f 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -142,8 +142,6 @@ bif erlang:list_to_pid/1
bif 'erl.lang.proc':string_to_pid/1 ebif_string_to_pid_1 list_to_pid_1
bif erlang:list_to_tuple/1
bif 'erl.lang.tuple':from_list/1 ebif_list_to_tuple_1
-bif erlang:load_module/2
-bif 'erl.system.code':load/2 ebif_load_module_2
bif erlang:loaded/0
bif 'erl.system.code':loaded/0 ebif_loaded_0
bif erlang:localtime/0
@@ -829,6 +827,13 @@ bif erlang:dt_restore_tag/1
bif erlang:dt_prepend_vm_tag_data/1
bif erlang:dt_append_vm_tag_data/1
+
+#
+# New in R16B.
+#
+bif erlang:prepare_loading/2
+bif erlang:finish_loading/1
+
#
# Obsolete
#
diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c
index 6f5020dc14..376201c309 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -71,9 +71,10 @@ process_info(int to, void *to_arg)
{
int i;
for (i = 0; i < erts_max_processes; i++) {
- if ((process_tab[i] != NULL) && (process_tab[i]->i != ENULL)) {
- if (process_tab[i]->status != P_EXITING)
- print_process_info(to, to_arg, process_tab[i]);
+ Process *p = erts_pix2proc(i);
+ if (p && p->i != ENULL) {
+ if (!ERTS_PROC_IS_EXITING(p))
+ print_process_info(to, to_arg, p);
}
}
@@ -89,7 +90,8 @@ process_killer(void)
erts_printf("\n\nProcess Information\n\n");
erts_printf("--------------------------------------------------\n");
for (i = erts_max_processes-1; i >= 0; i--) {
- if (((rp = process_tab[i]) != NULL) && rp->i != ENULL) {
+ rp = erts_pix2proc(i);
+ if (rp && rp->i != ENULL) {
int br;
print_process_info(ERTS_PRINT_STDOUT, NULL, rp);
erts_printf("(k)ill (n)ext (r)eturn:\n");
@@ -97,11 +99,20 @@ process_killer(void)
if ((j = sys_get_key(0)) <= 0)
erl_exit(0, "");
switch(j) {
- case 'k':
- if (rp->status == P_WAITING) {
- ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- erts_smp_proc_inc_refc(rp);
- erts_smp_proc_lock(rp, rp_locks);
+ case 'k': {
+ ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
+ erts_aint32_t state;
+ erts_smp_proc_inc_refc(rp);
+ erts_smp_proc_lock(rp, rp_locks);
+ state = erts_smp_atomic32_read_acqb(&rp->state);
+ if (state & (ERTS_PSFLG_FREE
+ | ERTS_PSFLG_EXITING
+ | ERTS_PSFLG_ACTIVE
+ | ERTS_PSFLG_IN_RUNQ
+ | ERTS_PSFLG_RUNNING)) {
+ erts_printf("Can only kill WAITING processes this way\n");
+ }
+ else {
(void) erts_send_exit_signal(NULL,
NIL,
rp,
@@ -110,12 +121,10 @@ process_killer(void)
NIL,
NULL,
0);
- erts_smp_proc_unlock(rp, rp_locks);
- erts_smp_proc_dec_refc(rp);
}
- else
- erts_printf("Can only kill WAITING processes this way\n");
-
+ erts_smp_proc_unlock(rp, rp_locks);
+ erts_smp_proc_dec_refc(rp);
+ }
case 'n': br = 1; break;
case 'r': return;
default: return;
@@ -180,42 +189,38 @@ static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext)
void
print_process_info(int to, void *to_arg, Process *p)
{
+ time_t approx_started;
int garbing = 0;
int running = 0;
- time_t tmp_t;
struct saved_calls *scb;
+ erts_aint32_t state;
/* display the PID */
erts_print(to, to_arg, "=proc:%T\n", p->id);
/* Display the state */
erts_print(to, to_arg, "State: ");
- switch (p->status) {
- case P_FREE:
+
+ state = erts_smp_atomic32_read_acqb(&p->state);
+ if (state & ERTS_PSFLG_FREE)
erts_print(to, to_arg, "Non Existing\n"); /* Should never happen */
- break;
- case P_RUNABLE:
- erts_print(to, to_arg, "Scheduled\n");
- break;
- case P_WAITING:
- erts_print(to, to_arg, "Waiting\n");
- break;
- case P_SUSPENDED:
- erts_print(to, to_arg, "Suspended\n");
- break;
- case P_RUNNING:
- erts_print(to, to_arg, "Running\n");
- running = 1;
- break;
- case P_EXITING:
+ else if (state & ERTS_PSFLG_EXITING)
erts_print(to, to_arg, "Exiting\n");
- break;
- case P_GARBING:
- erts_print(to, to_arg, "Garbing\n");
+ else if (state & ERTS_PSFLG_GC) {
garbing = 1;
running = 1;
- break;
+ erts_print(to, to_arg, "Garbing\n");
+ }
+ else if (state & ERTS_PSFLG_SUSPENDED)
+ erts_print(to, to_arg, "Suspended\n");
+ else if (state & ERTS_PSFLG_RUNNING) {
+ running = 1;
+ erts_print(to, to_arg, "Running\n");
}
+ else if (state & ERTS_PSFLG_ACTIVE)
+ erts_print(to, to_arg, "Scheduled\n");
+ else
+ erts_print(to, to_arg, "Waiting\n");
/*
* If the process is registered as a global process, display the
@@ -245,8 +250,8 @@ print_process_info(int to, void *to_arg, Process *p)
}
erts_print(to, to_arg, "Spawned by: %T\n", p->parent);
- tmp_t = p->started.tv_sec;
- erts_print(to, to_arg, "Started: %s", ctime(&tmp_t));
+ approx_started = (time_t) p->approx_started;
+ erts_print(to, to_arg, "Started: %s", ctime(&approx_started));
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p);
erts_print(to, to_arg, "Message queue length: %d\n", p->msg.len);
@@ -377,17 +382,22 @@ loaded(int to, void *to_arg)
int old = 0;
int cur = 0;
BeamInstr* code;
+ Module* modp;
+ ErtsCodeIndex code_ix;
+
+ code_ix = erts_active_code_ix();
+ erts_rlock_old_code(code_ix);
/*
* Calculate and print totals.
*/
- for (i = 0; i < module_code_size(); i++) {
- if (module_code(i) != NULL &&
- ((module_code(i)->code_length != 0) ||
- (module_code(i)->old_code_length != 0))) {
- cur += module_code(i)->code_length;
- if (module_code(i)->old_code_length != 0) {
- old += module_code(i)->old_code_length;
+ 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;
}
}
}
@@ -398,21 +408,22 @@ loaded(int to, void *to_arg)
* Print one line per module.
*/
- for (i = 0; i < module_code_size(); i++) {
+ for (i = 0; i < module_code_size(code_ix); i++) {
+ modp = module_code(i, code_ix);
if (!ERTS_IS_CRASH_DUMPING) {
/*
* Interactive dump; keep it brief.
*/
- if (module_code(i) != NULL &&
- ((module_code(i)->code_length != 0) ||
- (module_code(i)->old_code_length != 0))) {
- erts_print(to, to_arg, "%T", make_atom(module_code(i)->module));
- cur += module_code(i)->code_length;
- erts_print(to, to_arg, " %d", module_code(i)->code_length );
- if (module_code(i)->old_code_length != 0) {
+ 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)",
- module_code(i)->old_code_length );
- old += module_code(i)->old_code_length;
+ modp->old.code_length );
+ old += modp->old.code_length;
}
erts_print(to, to_arg, "\n");
}
@@ -420,15 +431,15 @@ loaded(int to, void *to_arg)
/*
* To crash dump; make it parseable.
*/
- if (module_code(i) != NULL &&
- ((module_code(i)->code_length != 0) ||
- (module_code(i)->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(module_code(i)->module));
+ 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",
- module_code(i)->code_length);
- code = module_code(i)->code;
+ modp->curr.code_length);
+ code = modp->curr.code;
if (code != NULL && code[MI_ATTR_PTR]) {
erts_print(to, to_arg, "Current attributes: ");
dump_attributes(to, to_arg, (byte *) code[MI_ATTR_PTR],
@@ -440,9 +451,9 @@ loaded(int to, void *to_arg)
code[MI_COMPILE_SIZE]);
}
- if (module_code(i)->old_code_length != 0) {
- erts_print(to, to_arg, "Old size: %d\n", module_code(i)->old_code_length);
- code = module_code(i)->old_code;
+ 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 attributes: ");
dump_attributes(to, to_arg, (byte *) code[MI_ATTR_PTR],
@@ -457,6 +468,7 @@ loaded(int to, void *to_arg)
}
}
}
+ erts_runlock_old_code(code_ix);
}
@@ -616,7 +628,8 @@ bin_check(void)
int i, printed = 0;
for (i=0; i < erts_max_processes; i++) {
- if ((rp = process_tab[i]) == NULL)
+ rp = erts_pix2proc(i);
+ if (!rp)
continue;
for (hdr = rp->off_heap.first; hdr; hdr = hdr->next) {
if (hdr->thing_word == HEADER_PROC_BIN) {
@@ -678,7 +691,7 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
erts_sys_prepare_crash_dump();
- if (erts_sys_getenv("ERL_CRASH_DUMP",&dumpnamebuf[0],&dumpnamebufsize) != 0)
+ if (erts_sys_getenv_raw("ERL_CRASH_DUMP",&dumpnamebuf[0],&dumpnamebufsize) != 0)
dumpname = "erl_crash.dump";
else
dumpname = &dumpnamebuf[0];
@@ -704,7 +717,7 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
erts_print_nif_taints(fd, NULL);
erts_fdprintf(fd, "Atoms: %d\n", atom_table_size());
info(fd, NULL); /* General system info */
- if (process_tab != NULL) /* XXX true at init */
+ if (erts_proc.tab)
process_info(fd, NULL); /* Info about each process and port */
db_info(fd, NULL, 0);
erts_print_bif_timer_info(fd, NULL);
diff --git a/erts/emulator/beam/code_ix.c b/erts/emulator/beam/code_ix.c
new file mode 100644
index 0000000000..8025058ee0
--- /dev/null
+++ b/erts/emulator/beam/code_ix.c
@@ -0,0 +1,166 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2012. 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
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "code_ix.h"
+#include "global.h"
+#include "beam_catches.h"
+
+
+
+#if 0
+# define CIX_TRACE(text) erts_fprintf(stderr, "CIX_TRACE: " text " act=%u load=%u\r\n", erts_active_code_ix(), erts_staging_code_ix())
+#else
+# define CIX_TRACE(text)
+#endif
+
+erts_smp_atomic32_t the_active_code_index;
+erts_smp_atomic32_t the_staging_code_index;
+
+static int the_code_ix_lock = 0;
+struct code_ix_queue_item {
+ Process *p;
+ struct code_ix_queue_item* next;
+};
+static struct code_ix_queue_item* the_code_ix_queue = NULL;
+static erts_smp_mtx_t the_code_ix_queue_lock;
+
+#ifdef ERTS_ENABLE_LOCK_CHECK
+static erts_tsd_key_t has_code_write_permission;
+#endif
+
+void erts_code_ix_init(void)
+{
+ /* We start emulator by initializing preloaded modules
+ * single threaded with active and staging set both to zero.
+ * Preloading is finished by a commit that will set things straight.
+ */
+ erts_smp_atomic32_init_nob(&the_active_code_index, 0);
+ erts_smp_atomic32_init_nob(&the_staging_code_index, 0);
+ erts_smp_mtx_init(&the_code_ix_queue_lock, "code_ix_queue");
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_tsd_key_create(&has_code_write_permission);
+#endif
+ CIX_TRACE("init");
+}
+
+void erts_start_staging_code_ix(void)
+{
+ beam_catches_start_staging();
+ export_start_staging();
+ module_start_staging();
+ erts_start_staging_ranges();
+ CIX_TRACE("start");
+}
+
+
+void erts_end_staging_code_ix(void)
+{
+ beam_catches_end_staging(1);
+ export_end_staging(1);
+ module_end_staging(1);
+ erts_end_staging_ranges(1);
+ CIX_TRACE("end");
+}
+
+void erts_commit_staging_code_ix(void)
+{
+ ErtsCodeIndex ix;
+ /* We need to this lock as we are now making the staging export table active */
+ export_staging_lock();
+ ix = erts_staging_code_ix();
+ erts_smp_atomic32_set_nob(&the_active_code_index, ix);
+ ix = (ix + 1) % ERTS_NUM_CODE_IX;
+ erts_smp_atomic32_set_nob(&the_staging_code_index, ix);
+ export_staging_unlock();
+ CIX_TRACE("activate");
+}
+
+void erts_abort_staging_code_ix(void)
+{
+ beam_catches_end_staging(0);
+ export_end_staging(0);
+ module_end_staging(0);
+ erts_end_staging_ranges(0);
+ CIX_TRACE("abort");
+}
+
+
+/*
+ * Calller _must_ yield if we return 0
+ */
+int erts_try_seize_code_write_permission(Process* c_p)
+{
+ int success;
+#ifdef ERTS_SMP
+ ASSERT(!erts_smp_thr_progress_is_blocking()); /* to avoid deadlock */
+#endif
+
+ erts_smp_mtx_lock(&the_code_ix_queue_lock);
+ success = !the_code_ix_lock;
+ if (success) {
+ the_code_ix_lock = 1;
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_tsd_set(has_code_write_permission, (void *) 1);
+#endif
+ }
+ else { /* Already locked */
+ struct code_ix_queue_item* qitem;
+ qitem = erts_alloc(ERTS_ALC_T_CODE_IX_LOCK_Q, sizeof(*qitem));
+ qitem->p = c_p;
+ erts_smp_proc_inc_refc(c_p);
+ qitem->next = the_code_ix_queue;
+ the_code_ix_queue = qitem;
+ erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
+ }
+ erts_smp_mtx_unlock(&the_code_ix_queue_lock);
+ return success;
+}
+
+void erts_release_code_write_permission(void)
+{
+ ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked());
+ erts_smp_mtx_lock(&the_code_ix_queue_lock);
+ while (the_code_ix_queue != NULL) { /* unleash the entire herd */
+ struct code_ix_queue_item* qitem = the_code_ix_queue;
+ erts_smp_proc_lock(qitem->p, ERTS_PROC_LOCK_STATUS);
+ if (!ERTS_PROC_IS_EXITING(qitem->p)) {
+ erts_resume(qitem->p, ERTS_PROC_LOCK_STATUS);
+ }
+ erts_smp_proc_unlock(qitem->p, ERTS_PROC_LOCK_STATUS);
+ the_code_ix_queue = qitem->next;
+ erts_smp_proc_dec_refc(qitem->p);
+ erts_free(ERTS_ALC_T_CODE_IX_LOCK_Q, qitem);
+ }
+ the_code_ix_lock = 0;
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_tsd_set(has_code_write_permission, (void *) 0);
+#endif
+ erts_smp_mtx_unlock(&the_code_ix_queue_lock);
+}
+
+#ifdef ERTS_ENABLE_LOCK_CHECK
+int erts_is_code_ix_locked(void)
+{
+ return the_code_ix_lock && erts_tsd_get(has_code_write_permission);
+}
+#endif
diff --git a/erts/emulator/beam/code_ix.h b/erts/emulator/beam/code_ix.h
new file mode 100644
index 0000000000..6b2680044e
--- /dev/null
+++ b/erts/emulator/beam/code_ix.h
@@ -0,0 +1,141 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2012. 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
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/* Description:
+ * This is the interface that facilitate changing the beam code
+ * (load,upgrade,delete) while allowing executing Erlang processes to
+ * access the code without any locks or other expensive memory barriers.
+ *
+ * The basic idea is to maintain several "logical copies" of the code. These
+ * copies are identified by a global 'code index', an integer of 0, 1 or 2.
+ * The code index is used as argument to code access structures like
+ * export, module, beam_catches, beam_ranges.
+ *
+ * The current 'active' code index is used to access the current running
+ * code. The 'staging' code index is used by the process that performs
+ * a code change operation. When a code change operation completes
+ * succesfully, the staging code index becomes the new active code index.
+ *
+ * The third code index is not explicitly used. It can be thought of as
+ * the "previous active" or the "next staging" index. It is needed to make
+ * sure that we do not reuse a code index for staging until we are sure
+ * that no executing BIFs are still referring it.
+ * We could get by with only two (0 and 1), but that would require that we
+ * must wait for all schedulers to re-schedule before each code change
+ * operation can start staging.
+ *
+ * Note that the 'code index' is very loosely coupled to the concept of
+ * 'current' and 'old' module versions. You can almost say that they are
+ * orthogonal to each other. Code index is an emulator global concept while
+ * 'current' and 'old' is specific for each module.
+ */
+
+#ifndef __CODE_IX_H__
+#define __CODE_IX_H__
+
+#ifndef __SYS_H__
+# ifdef HAVE_CONFIG_H
+# include "config.h"
+# endif
+# include "sys.h"
+#endif
+struct process;
+
+
+#define ERTS_NUM_CODE_IX 3
+typedef unsigned ErtsCodeIndex;
+
+
+/* Called once at emulator initialization.
+ */
+void erts_code_ix_init(void);
+
+/* Return active code index.
+ * Is guaranteed to be valid until the calling BIF returns.
+ * To get a consistent view of the code, only one call to erts_active_code_ix()
+ * should be made and the returned ix reused within the same BIF call.
+ */
+ERTS_GLB_INLINE
+ErtsCodeIndex erts_active_code_ix(void);
+
+/* Return staging code ix.
+ * Only used by a process performing code loading/upgrading/deleting/purging.
+ * code_ix must be locked.
+ */
+ERTS_GLB_INLINE
+ErtsCodeIndex erts_staging_code_ix(void);
+
+/* Try seize exclusive code write permission. Needed for code staging.
+ * Main process lock (only) must be held.
+ * System thread progress must not be blocked.
+ * Caller is suspended and *must* yield if 0 is returned.
+ */
+int erts_try_seize_code_write_permission(struct process*);
+
+/* Release code write permission.
+ * Will resume any suspended waiters.
+ */
+void erts_release_code_write_permission(void);
+
+/* Prepare the "staging area" to be a complete copy of the active code.
+ * Code write permission must have been seized.
+ * 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);
+
+/* End the staging.
+ * Preceded by "start" and must be followed by "commit".
+ */
+void erts_end_staging_code_ix(void);
+
+/* Set staging code index as new active code index.
+ * Preceded by "end".
+ */
+void erts_commit_staging_code_ix(void);
+
+/* Abort the staging.
+ * Preceded by "start".
+ */
+void erts_abort_staging_code_ix(void);
+
+#ifdef ERTS_ENABLE_LOCK_CHECK
+int erts_is_code_ix_locked(void);
+#endif
+
+
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+extern erts_smp_atomic32_t the_active_code_index;
+extern erts_smp_atomic32_t the_staging_code_index;
+
+ERTS_GLB_INLINE ErtsCodeIndex erts_active_code_ix(void)
+{
+ return erts_smp_atomic32_read_nob(&the_active_code_index);
+}
+ERTS_GLB_INLINE ErtsCodeIndex erts_staging_code_ix(void)
+{
+ return erts_smp_atomic32_read_nob(&the_staging_code_index);
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+#endif /* !__CODE_IX_H__ */
+
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index 01fd63d5d9..28c4621ff2 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -968,16 +968,16 @@ erts_dsig_send_group_leader(ErtsDSigData *dsdp, Eterm leader, Eterm remote)
#define VALGRIND_PRINTF_XML VALGRIND_PRINTF
#endif
-# define PURIFY_MSG(msg) \
- do { \
- char buf__[1]; size_t bufsz__ = sizeof(buf__); \
- if (erts_sys_getenv("VALGRIND_LOG_XML", buf__, &bufsz__) >= 0) { \
- VALGRIND_PRINTF_XML("<erlang_error_log>" \
- "%s, line %d: %s</erlang_error_log>\n", \
- __FILE__, __LINE__, msg); \
- } else { \
- VALGRIND_PRINTF("%s, line %d: %s", __FILE__, __LINE__, msg); \
- } \
+# define PURIFY_MSG(msg) \
+ do { \
+ char buf__[1]; size_t bufsz__ = sizeof(buf__); \
+ if (erts_sys_getenv_raw("VALGRIND_LOG_XML", buf__, &bufsz__) >= 0) { \
+ VALGRIND_PRINTF_XML("<erlang_error_log>" \
+ "%s, line %d: %s</erlang_error_log>\n", \
+ __FILE__, __LINE__, msg); \
+ } else { \
+ VALGRIND_PRINTF("%s, line %d: %s", __FILE__, __LINE__, msg); \
+ } \
} while (0)
#else
# define PURIFY_MSG(msg)
@@ -1312,7 +1312,7 @@ int erts_net_message(Port *prt,
if (is_not_pid(from) || is_not_atom(to)){
goto invalid_message;
}
- rp = erts_whereis_process(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC);
+ rp = erts_whereis_process(NULL, 0, to, 0, 0);
if (rp) {
Uint xsize = (type == DOP_REG_SEND
? 0
@@ -1338,7 +1338,6 @@ int erts_net_message(Port *prt,
erts_queue_dist_message(rp, &locks, ede_copy, token);
if (locks)
erts_smp_proc_unlock(rp, locks);
- erts_smp_proc_dec_refc(rp);
}
break;
@@ -1364,7 +1363,7 @@ int erts_net_message(Port *prt,
if (is_not_pid(to)) {
goto invalid_message;
}
- rp = erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC);
+ rp = erts_proc_lookup(to);
if (rp) {
Uint xsize = type == DOP_SEND ? 0 : ERTS_HEAP_FRAG_SIZE(token_size);
ErtsProcLocks locks = 0;
@@ -1388,7 +1387,6 @@ int erts_net_message(Port *prt,
erts_queue_dist_message(rp, &locks, ede_copy, token);
if (locks)
erts_smp_proc_unlock(rp, locks);
- erts_smp_proc_dec_refc(rp);
}
break;
@@ -1544,8 +1542,7 @@ int erts_net_message(Port *prt,
if (is_not_pid(from) || is_not_internal_pid(to)) {
goto invalid_message;
}
- rp = erts_pid2proc_opt(NULL, 0, to, rp_locks,
- ERTS_P2P_FLG_SMP_INC_REFC);
+ rp = erts_pid2proc(NULL, 0, to, rp_locks);
if (rp) {
(void) erts_send_exit_signal(NULL,
from,
@@ -1556,7 +1553,6 @@ int erts_net_message(Port *prt,
NULL,
0);
erts_smp_proc_unlock(rp, rp_locks);
- erts_smp_proc_dec_refc(rp);
}
break;
}
@@ -1973,6 +1969,7 @@ erts_dist_command(Port *prt, int reds_limit)
bw(foq.first->extp, size);
#endif
reds += ERTS_PORT_REDS_DIST_CMD_DATA(size);
+ erts_smp_atomic_add_nob(&erts_bytes_out, size);
fob = foq.first;
obufsize += size_obuf(fob);
foq.first = foq.first->next;
@@ -2056,6 +2053,7 @@ erts_dist_command(Port *prt, int reds_limit)
bw(oq.first->extp, size);
#endif
reds += ERTS_PORT_REDS_DIST_CMD_DATA(size);
+ erts_smp_atomic_add_nob(&erts_bytes_out, size);
fob = oq.first;
obufsize += size_obuf(fob);
oq.first = oq.first->next;
@@ -2242,7 +2240,7 @@ static void doit_print_monitor_info(ErtsMonitor *mon, void *vptdp)
void *arg = ((struct print_to_data *) vptdp)->arg;
Process *rp;
ErtsMonitor *rmon;
- rp = erts_pid2proc_unlocked(mon->pid);
+ rp = erts_proc_lookup(mon->pid);
if (!rp || (rmon = erts_lookup_monitor(rp->monitors, mon->ref)) == NULL) {
erts_print(to, arg, "Warning, stray monitor for: %T\n", mon->pid);
} else if (mon->type == MON_ORIGIN) {
@@ -2281,7 +2279,7 @@ static void doit_print_link_info2(ErtsLink *lnk, void *vpplc)
static void doit_print_link_info(ErtsLink *lnk, void *vptdp)
{
- if (is_internal_pid(lnk->pid) && erts_pid2proc_unlocked(lnk->pid)) {
+ if (is_internal_pid(lnk->pid) && erts_proc_lookup(lnk->pid)) {
PrintLinkContext plc = {(struct print_to_data *) vptdp, lnk->pid};
erts_doforall_links(ERTS_LINK_ROOT(lnk), &doit_print_link_info2, &plc);
}
@@ -2303,7 +2301,7 @@ static void doit_print_nodelink_info(ErtsLink *lnk, void *vpcontext)
{
PrintNodeLinkContext *pcontext = vpcontext;
- if (is_internal_pid(lnk->pid) && erts_pid2proc_unlocked(lnk->pid))
+ if (is_internal_pid(lnk->pid) && erts_proc_lookup(lnk->pid))
erts_print(pcontext->ptd.to, pcontext->ptd.arg,
"Remote monitoring: %T %T\n", lnk->pid, pcontext->sysname);
}
@@ -2451,15 +2449,15 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2)
goto error;
/* Check that all trap functions are defined !! */
- if (dsend2_trap->address == NULL ||
- dsend3_trap->address == NULL ||
+ if (dsend2_trap->addressv[0] == NULL ||
+ dsend3_trap->addressv[0] == NULL ||
/* dsend_nosuspend_trap->address == NULL ||*/
- dlink_trap->address == NULL ||
- dunlink_trap->address == NULL ||
- dmonitor_node_trap->address == NULL ||
- dgroup_leader_trap->address == NULL ||
- dmonitor_p_trap->address == NULL ||
- dexit_trap->address == NULL) {
+ dlink_trap->addressv[0] == NULL ||
+ dunlink_trap->addressv[0] == NULL ||
+ dmonitor_node_trap->addressv[0] == NULL ||
+ dgroup_leader_trap->addressv[0] == NULL ||
+ dmonitor_p_trap->addressv[0] == NULL ||
+ dexit_trap->addressv[0] == NULL) {
goto error;
}
@@ -2706,9 +2704,8 @@ BIF_RETTYPE dist_exit_3(BIF_ALIST_3)
}
else {
lp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- lp = erts_pid2proc_opt(BIF_P, ERTS_PROC_LOCK_MAIN,
- local, lp_locks,
- ERTS_P2P_FLG_SMP_INC_REFC);
+ lp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
+ local, lp_locks);
if (!lp) {
BIF_RET(am_true); /* ignore */
}
@@ -2727,9 +2724,7 @@ BIF_RETTYPE dist_exit_3(BIF_ALIST_3)
lp_locks &= ~ERTS_PROC_LOCK_MAIN;
#endif
erts_smp_proc_unlock(lp, lp_locks);
- if (lp != BIF_P)
- erts_smp_proc_dec_refc(lp);
- else {
+ if (lp == BIF_P) {
/*
* We may have exited current process and may have to take action.
*/
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index 6fce032f9d..3eee53eba3 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -1675,7 +1675,7 @@ erts_alc_fatal_error(int error, int func, ErtsAlcType_t n, ...)
t_str = type_no_str(n);
if (!t_str) {
- sprintf(buf, "%d", (int) n);
+ erts_snprintf(buf, sizeof(buf), "%d", (int) n);
t_str = buf;
}
@@ -2182,9 +2182,9 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
if (want.code) {
size.code = module_table_sz();
size.code += export_table_sz();
- size.code += export_list_size() * sizeof(Export);
+ size.code += export_entries_sz();
size.code += erts_fun_table_sz();
- size.code += allocated_modules*sizeof(Range);
+ size.code += erts_ranges_sz();
size.code += erts_total_code_size;
}
@@ -2300,11 +2300,7 @@ erts_allocated_areas(int *print_to_p, void *print_to_arg, void *proc)
values[i].name = "static";
values[i].ui[0] =
erts_max_ports*sizeof(Port) /* Port table */
- + erts_timer_wheel_memory_size() /* Timer wheel */
-#ifdef SYS_TMP_BUF_SIZE
- + SYS_TMP_BUF_SIZE /* tmp_buf in sys on vxworks & ose */
-#endif
- ;
+ + erts_timer_wheel_memory_size(); /* Timer wheel */
i++;
erts_atom_get_text_space_sizes(&reserved_atom_space, &atom_space);
@@ -2332,7 +2328,7 @@ erts_allocated_areas(int *print_to_p, void *print_to_arg, void *proc)
values[i].arity = 2;
values[i].name = "export_list";
- values[i].ui[0] = export_list_size() * sizeof(Export);
+ values[i].ui[0] = export_entries_sz();
i++;
values[i].arity = 2;
@@ -2347,7 +2343,7 @@ erts_allocated_areas(int *print_to_p, void *print_to_arg, void *proc)
values[i].arity = 2;
values[i].name = "module_refs";
- values[i].ui[0] = allocated_modules*sizeof(Range);
+ values[i].ui[0] = erts_ranges_sz();
i++;
values[i].arity = 2;
@@ -3576,12 +3572,12 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func)
ftype = type_no_str(found_type);
if (!ftype) {
- sprintf(fbuf, "%d", (int) found_type);
+ erts_snprintf(fbuf, sizeof(fbuf), "%d", (int) found_type);
ftype = fbuf;
}
otype = type_no_str(n);
if (!otype) {
- sprintf(obuf, "%d", (int) n);
+ erts_snprintf(obuf, sizeof(obuf), "%d", (int) n);
otype = obuf;
}
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index d4ef9cc553..0a4407f009 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -164,6 +164,7 @@ type MSG_REF FIXED_SIZE PROCESSES msg_ref
type MSG_ROOTS TEMPORARY PROCESSES msg_roots
type ROOTSET TEMPORARY PROCESSES root_set
type LOADER_TMP TEMPORARY CODE loader_tmp
+type PREPARED_CODE SHORT_LIVED CODE prepared_code
type BIF_TIMER_TABLE LONG_LIVED SYSTEM bif_timer_table
type SL_BIF_TIMER SHORT_LIVED PROCESSES bif_timer_sl
type LL_BIF_TIMER STANDARD PROCESSES bif_timer_ll
@@ -263,6 +264,8 @@ 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
+type CODE_IX_LOCK_Q SHORT_LIVED SYSTEM code_ix_lock_q
+type PROC_INTERVAL LONG_LIVED SYSTEM process_interval
+if threads_no_smp
# Need thread safe allocs, but std_alloc and fix_alloc are not;
@@ -291,7 +294,6 @@ type PORT_LOCK STANDARD SYSTEM port_lock
type DRIVER_LOCK STANDARD SYSTEM driver_lock
type XPORTS_LIST SHORT_LIVED SYSTEM extra_port_list
type PROC_LCK_WTR LONG_LIVED SYSTEM proc_lock_waiter
-type PROC_LCK_QS LONG_LIVED SYSTEM proc_lock_queues
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
@@ -322,6 +324,8 @@ type SL_PTIMER SHORT_LIVED SYSTEM ptimer_sl
type LL_PTIMER STANDARD SYSTEM ptimer_ll
type SYS_MSG_Q SHORT_LIVED PROCESSES system_messages_queue
type FP_EXCEPTION LONG_LIVED SYSTEM fp_exception
+type LL_MPATHS LONG_LIVED SYSTEM ll_migration_paths
+type SL_MPATHS SHORT_LIVED SYSTEM sl_migration_paths
+endif
+if hipe
@@ -415,28 +419,4 @@ type CON_VPRINTF_BUF TEMPORARY SYSTEM con_vprintf_buf
+endif
-+if vxworks
-
-type SYS_TMP_BUF LONG_LIVED SYSTEM sys_tmp_buf
-type PEND_DATA SYSTEM SYSTEM pending_data
-type FD_TAB LONG_LIVED SYSTEM fd_tab
-type FD_ENTRY_BUF SYSTEM SYSTEM fd_entry_buf
-
-+endif
-
-+if ose
-
-type SYS_TMP_BUF LONG_LIVED SYSTEM sys_tmp_buf
-type PUTENV_STR SYSTEM SYSTEM putenv_string
-type GETENV_STR SYSTEM SYSTEM getenv_string
-type GETENV_STATE SYSTEM SYSTEM getenv_state
-type SIG_ENTRY SYSTEM SYSTEM sig_entry
-type DRIVER_DATA SYSTEM SYSTEM driver_data
-type PGM_TAB SYSTEM SYSTEM pgm_tab
-type PGM_ENTRY SYSTEM SYSTEM pgm_entry
-type PRT_TAB SYSTEM SYSTEM prt_tab
-type PRT_ENTRY SYSTEM SYSTEM prt_entry
-
-+endif
-
# ----------------------------------------------------------------------------
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index a0abd1c405..97ba306a79 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -829,46 +829,83 @@ init_dd_queue(ErtsAllctrDDQueue_t *ddq)
ddq->head.used_marker = 1;
}
-static ERTS_INLINE erts_aint_t
-ddq_managed_thread_enqueue(ErtsAllctrDDQueue_t *ddq, void *ptr)
+static ERTS_INLINE int
+ddq_managed_thread_enqueue(ErtsAllctrDDQueue_t *ddq, void *ptr, int cinit)
{
- erts_aint_t ilast, itmp;
- ErtsAllctrDDBlock_t *this = ptr;
+ erts_aint_t itmp;
+ ErtsAllctrDDBlock_t *enq, *this = ptr;
erts_atomic_init_nob(&this->atmc_next, ERTS_AINT_NULL);
-
/* Enqueue at end of list... */
- ilast = erts_atomic_read_nob(&ddq->tail.data.last);
- while (1) {
- ErtsAllctrDDBlock_t *last = (ErtsAllctrDDBlock_t *) ilast;
- itmp = erts_atomic_cmpxchg_mb(&last->atmc_next,
- (erts_aint_t) this,
- ERTS_AINT_NULL);
- if (itmp == ERTS_AINT_NULL)
- break;
- ilast = itmp;
+ enq = (ErtsAllctrDDBlock_t *) erts_atomic_read_nob(&ddq->tail.data.last);
+ itmp = erts_atomic_cmpxchg_relb(&enq->atmc_next,
+ (erts_aint_t) this,
+ ERTS_AINT_NULL);
+ if (itmp == ERTS_AINT_NULL) {
+ /* We are required to move last pointer */
+#ifdef DEBUG
+ ASSERT(ERTS_AINT_NULL == erts_atomic_read_nob(&this->atmc_next));
+ ASSERT(((erts_aint_t) enq)
+ == erts_atomic_xchg_relb(&ddq->tail.data.last,
+ (erts_aint_t) this));
+#else
+ erts_atomic_set_relb(&ddq->tail.data.last, (erts_aint_t) this);
+#endif
+ return 1;
}
+ else {
+ /*
+ * We *need* to insert element somewhere in between the
+ * last element we read earlier and the actual last element.
+ */
+ int i = cinit;
- /* Move last pointer forward... */
- while (1) {
- if (erts_atomic_read_rb(&this->atmc_next) != ERTS_AINT_NULL) {
- /* Someone else will move it forward */
- return erts_atomic_read_rb(&ddq->tail.data.last);
+ while (1) {
+ erts_aint_t itmp2;
+ erts_atomic_set_nob(&this->atmc_next, itmp);
+ itmp2 = erts_atomic_cmpxchg_relb(&enq->atmc_next,
+ (erts_aint_t) this,
+ itmp);
+ if (itmp == itmp2)
+ return 0; /* inserted this */
+ if ((i & 1) == 0)
+ itmp = itmp2;
+ else {
+ enq = (ErtsAllctrDDBlock_t *) itmp2;
+ itmp = erts_atomic_read_acqb(&enq->atmc_next);
+ ASSERT(itmp != ERTS_AINT_NULL);
+ }
+ i++;
}
- itmp = erts_atomic_cmpxchg_mb(&ddq->tail.data.last,
- (erts_aint_t) this,
- ilast);
- if (ilast == itmp)
- return (erts_aint_t) this;
- ilast = itmp;
}
}
+static ERTS_INLINE erts_aint_t
+check_insert_marker(ErtsAllctrDDQueue_t *ddq, erts_aint_t ilast)
+{
+ if (!ddq->head.used_marker
+ && ddq->head.unref_end == (ErtsAllctrDDBlock_t *) ilast) {
+ erts_aint_t itmp;
+ ErtsAllctrDDBlock_t *last = (ErtsAllctrDDBlock_t *) ilast;
+
+ erts_atomic_init_nob(&ddq->tail.data.marker.atmc_next, ERTS_AINT_NULL);
+ itmp = erts_atomic_cmpxchg_relb(&last->atmc_next,
+ (erts_aint_t) &ddq->tail.data.marker,
+ ERTS_AINT_NULL);
+ if (itmp == ERTS_AINT_NULL) {
+ ilast = (erts_aint_t) &ddq->tail.data.marker;
+ ddq->head.used_marker = !0;
+ erts_atomic_set_relb(&ddq->tail.data.last, ilast);
+ }
+ }
+ return ilast;
+}
+
static ERTS_INLINE int
-ddq_enqueue(ErtsAlcType_t type, ErtsAllctrDDQueue_t *ddq, void *ptr)
+ddq_enqueue(ErtsAlcType_t type, ErtsAllctrDDQueue_t *ddq, void *ptr, int cinit)
{
- erts_aint_t ilast;
+ int last_elem;
int um_refc_ix = 0;
int managed_thread = erts_thr_progress_is_managed_thread();
if (!managed_thread) {
@@ -884,11 +921,11 @@ ddq_enqueue(ErtsAlcType_t type, ErtsAllctrDDQueue_t *ddq, void *ptr)
}
}
- ilast = ddq_managed_thread_enqueue(ddq, ptr);
+ last_elem = ddq_managed_thread_enqueue(ddq, ptr, cinit);
if (!managed_thread)
erts_atomic_dec_relb(&ddq->tail.data.um_refc[um_refc_ix]);
- return ilast == (erts_aint_t) ptr;
+ return last_elem;
}
static ERTS_INLINE void *
@@ -934,20 +971,16 @@ ddq_check_incoming(ErtsAllctrDDQueue_t *ddq)
int um_refc_ix;
ddq->head.next.thr_progress_reached = 1;
um_refc_ix = ddq->head.next.um_refc_ix;
- if (erts_atomic_read_acqb(&ddq->tail.data.um_refc[um_refc_ix]) == 0) {
+ if (erts_atomic_read_nob(&ddq->tail.data.um_refc[um_refc_ix]) == 0) {
/* Move unreferenced end pointer forward... */
+ ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore);
+
ddq->head.unref_end = ddq->head.next.unref_end;
- if (!ddq->head.used_marker
- && ddq->head.unref_end == (ErtsAllctrDDBlock_t *) ilast) {
- ddq->head.used_marker = 1;
- ilast = ddq_managed_thread_enqueue(ddq, &ddq->tail.data.marker);
- }
+ ilast = check_insert_marker(ddq, ilast);
- if (ddq->head.unref_end == (ErtsAllctrDDBlock_t *) ilast)
- ERTS_THR_MEMORY_BARRIER;
- else {
+ if (ddq->head.unref_end != (ErtsAllctrDDBlock_t *) ilast) {
ddq->head.next.unref_end = (ErtsAllctrDDBlock_t *) ilast;
ddq->head.next.thr_progress = erts_thr_progress_later(NULL);
erts_atomic32_set_relb(&ddq->tail.data.um_refc_ix,
@@ -1092,12 +1125,15 @@ handle_delayed_dealloc(Allctr_t *allctr,
}
static ERTS_INLINE void
-enqueue_dealloc_other_instance(ErtsAlcType_t type, Allctr_t *allctr, void *ptr)
+enqueue_dealloc_other_instance(ErtsAlcType_t type,
+ Allctr_t *allctr,
+ void *ptr,
+ int cinit)
{
if (allctr->fix)
((UWord *) ptr)[ERTS_ALCU_DD_FIX_TYPE_OFFS] = (UWord) type;
- if (ddq_enqueue(type, &allctr->dd.q, ptr))
+ if (ddq_enqueue(type, &allctr->dd.q, ptr, cinit))
erts_alloc_notify_delayed_dealloc(allctr->ix);
}
@@ -3613,7 +3649,11 @@ erts_alcu_free_thr_pref(ErtsAlcType_t type, void *extra, void *p)
get_pref_allctr(extra, &pref_allctr);
ptr = get_used_allctr(extra, p, &used_allctr, NULL);
if (pref_allctr != used_allctr)
- enqueue_dealloc_other_instance(type, used_allctr, ptr);
+ enqueue_dealloc_other_instance(type,
+ used_allctr,
+ ptr,
+ (used_allctr->dd.ix
+ - pref_allctr->dd.ix));
else {
if (used_allctr->thread_safe)
erts_mtx_lock(&used_allctr->mutex);
@@ -3988,7 +4028,11 @@ realloc_thr_pref(ErtsAlcType_t type, void *extra, void *p, Uint size,
sys_memcpy(res, p, cpy_size);
if (!force_move || used_allctr != pref_allctr)
- enqueue_dealloc_other_instance(type, used_allctr, ptr);
+ enqueue_dealloc_other_instance(type,
+ used_allctr,
+ ptr,
+ (used_allctr->dd.ix
+ - pref_allctr->dd.ix));
else {
do_erts_alcu_free(type, used_allctr, ptr);
ASSERT(pref_allctr == used_allctr);
@@ -4179,6 +4223,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
allctr->dd.use = 1;
init_dd_queue(&allctr->dd.q);
+ allctr->dd.ix = init->ix;
}
else
#endif
diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c
index cb975d64b0..c5f432bea1 100644
--- a/erts/emulator/beam/erl_async.c
+++ b/erts/emulator/beam/erl_async.c
@@ -122,6 +122,8 @@ typedef struct {
#endif
} ErtsAsyncData;
+#if defined(USE_THREADS) && defined(USE_VM_PROBES)
+
/*
* Some compilers, e.g. GCC 4.2.1 and -O3, will optimize away DTrace
* calls if they're the last thing in the function. :-(
@@ -129,6 +131,7 @@ typedef struct {
* https://github.com/memcached/memcached/commit/6298b3978687530bc9d219b6ac707a1b681b2a46
*/
static unsigned gcc_optimizer_hack = 0;
+#endif
int erts_async_max_threads; /* Initialized by erl_init.c */
int erts_async_thread_suggested_stack_size; /* Initialized by erl_init.c */
@@ -281,8 +284,8 @@ static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q)
len = -1;
DTRACE2(aio_pool_add, port_str, len);
}
-#endif
gcc_optimizer_hack++;
+#endif
}
static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q,
@@ -394,6 +397,8 @@ static ERTS_INLINE void call_async_ready(ErtsAsync *a)
}
erts_port_release(p);
}
+ if (a->pdl)
+ driver_pdl_dec_refc(a->pdl);
if (a->hndl)
erts_ddll_dereference_driver(a->hndl);
}
@@ -403,9 +408,6 @@ static ERTS_INLINE void async_reply(ErtsAsync *a, ErtsThrQPrepEnQ_t *prep_enq)
#if ERTS_USE_ASYNC_READY_Q
ErtsAsyncReadyQ *arq;
- if (a->pdl)
- driver_pdl_dec_refc(a->pdl);
-
#if ERTS_ASYNC_PRINT_JOB
erts_fprintf(stderr, "=>> %ld\n", a->async_id);
#endif
@@ -425,8 +427,6 @@ static ERTS_INLINE void async_reply(ErtsAsync *a, ErtsThrQPrepEnQ_t *prep_enq)
#else /* ERTS_USE_ASYNC_READY_Q */
call_async_ready(a);
- if (a->pdl)
- driver_pdl_dec_refc(a->pdl);
erts_free(ERTS_ALC_T_ASYNC, (void *) a);
#endif /* ERTS_USE_ASYNC_READY_Q */
diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c
index cc4f2be8eb..474151d454 100644
--- a/erts/emulator/beam/erl_bif_binary.c
+++ b/erts/emulator/beam/erl_bif_binary.c
@@ -72,52 +72,29 @@ binary_matches(Process *p, Eterm arg1, Eterm arg2, Eterm arg3);
void erts_init_bif_binary(void)
{
- sys_memset((void *) &binary_match_trap_export, 0, sizeof(Export));
- binary_match_trap_export.address = &binary_match_trap_export.code[3];
- binary_match_trap_export.code[0] = am_erlang;
- binary_match_trap_export.code[1] = am_binary_match_trap;
- binary_match_trap_export.code[2] = 3;
- binary_match_trap_export.code[3] = (BeamInstr) em_apply_bif;
- binary_match_trap_export.code[4] = (BeamInstr) &binary_match_trap;
-
- sys_memset((void *) &binary_matches_trap_export, 0, sizeof(Export));
- binary_matches_trap_export.address = &binary_matches_trap_export.code[3];
- binary_matches_trap_export.code[0] = am_erlang;
- binary_matches_trap_export.code[1] = am_binary_matches_trap;
- binary_matches_trap_export.code[2] = 3;
- binary_matches_trap_export.code[3] = (BeamInstr) em_apply_bif;
- binary_matches_trap_export.code[4] = (BeamInstr) &binary_matches_trap;
-
- sys_memset((void *) &binary_longest_prefix_trap_export, 0, sizeof(Export));
- binary_longest_prefix_trap_export.address = &binary_longest_prefix_trap_export.code[3];
- binary_longest_prefix_trap_export.code[0] = am_erlang;
- binary_longest_prefix_trap_export.code[1] = am_binary_longest_prefix_trap;
- binary_longest_prefix_trap_export.code[2] = 3;
- binary_longest_prefix_trap_export.code[3] = (BeamInstr) em_apply_bif;
- binary_longest_prefix_trap_export.code[4] = (BeamInstr) &binary_longest_prefix_trap;
-
- sys_memset((void *) &binary_longest_suffix_trap_export, 0, sizeof(Export));
- binary_longest_suffix_trap_export.address = &binary_longest_suffix_trap_export.code[3];
- binary_longest_suffix_trap_export.code[0] = am_erlang;
- binary_longest_suffix_trap_export.code[1] = am_binary_longest_suffix_trap;
- binary_longest_suffix_trap_export.code[2] = 3;
- binary_longest_suffix_trap_export.code[3] = (BeamInstr) em_apply_bif;
- binary_longest_suffix_trap_export.code[4] = (BeamInstr) &binary_longest_suffix_trap;
-
- sys_memset((void *) &binary_bin_to_list_trap_export, 0, sizeof(Export));
- binary_bin_to_list_trap_export.address = &binary_bin_to_list_trap_export.code[3];
- binary_bin_to_list_trap_export.code[0] = am_erlang;
- binary_bin_to_list_trap_export.code[1] = am_binary_bin_to_list_trap;
- binary_bin_to_list_trap_export.code[2] = 3;
- binary_bin_to_list_trap_export.code[3] = (BeamInstr) em_apply_bif;
- binary_bin_to_list_trap_export.code[4] = (BeamInstr) &binary_bin_to_list_trap;
- sys_memset((void *) &binary_copy_trap_export, 0, sizeof(Export));
- binary_copy_trap_export.address = &binary_copy_trap_export.code[3];
- binary_copy_trap_export.code[0] = am_erlang;
- binary_copy_trap_export.code[1] = am_binary_copy_trap;
- binary_copy_trap_export.code[2] = 2;
- binary_copy_trap_export.code[3] = (BeamInstr) em_apply_bif;
- binary_copy_trap_export.code[4] = (BeamInstr) &binary_copy_trap;
+ 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_longest_prefix_trap_export,
+ am_erlang, am_binary_longest_prefix_trap, 3,
+ &binary_longest_prefix_trap);
+
+ erts_init_trap_export(&binary_longest_suffix_trap_export,
+ am_erlang, am_binary_longest_suffix_trap, 3,
+ &binary_longest_suffix_trap);
+
+ erts_init_trap_export(&binary_bin_to_list_trap_export,
+ am_erlang, am_binary_bin_to_list_trap, 3,
+ &binary_bin_to_list_trap);
+
+ erts_init_trap_export(&binary_copy_trap_export,
+ am_erlang, am_binary_copy_trap, 2,
+ &binary_copy_trap);
max_loop_limit = 0;
return;
diff --git a/erts/emulator/beam/erl_bif_chksum.c b/erts/emulator/beam/erl_bif_chksum.c
index 06b7ffdf32..ff5ce3cc7a 100644
--- a/erts/emulator/beam/erl_bif_chksum.c
+++ b/erts/emulator/beam/erl_bif_chksum.c
@@ -42,16 +42,9 @@ static Export chksum_md5_2_exp;
void erts_init_bif_chksum(void)
{
/* Non visual BIF to trap to. */
- memset(&chksum_md5_2_exp, 0, sizeof(Export));
- chksum_md5_2_exp.address =
- &chksum_md5_2_exp.code[3];
- chksum_md5_2_exp.code[0] = am_erlang;
- chksum_md5_2_exp.code[1] = am_atom_put("md5_trap",8);
- chksum_md5_2_exp.code[2] = 2;
- chksum_md5_2_exp.code[3] =
- (BeamInstr) em_apply_bif;
- chksum_md5_2_exp.code[4] =
- (BeamInstr) &md5_2;
+ erts_init_trap_export(&chksum_md5_2_exp,
+ am_erlang, am_atom_put("md5_trap",8), 2,
+ &md5_2);
}
diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c
index c338ee1c4b..7f7c975e78 100644
--- a/erts/emulator/beam/erl_bif_ddll.c
+++ b/erts/emulator/beam/erl_bif_ddll.c
@@ -368,13 +368,11 @@ BIF_RETTYPE erl_ddll_try_load_3(BIF_ALIST_3)
#endif
for (j = 0; j < erts_max_ports; j++) {
Port* prt = &erts_port[j];
-#ifdef DDLL_SMP
erts_smp_port_state_lock(prt);
-#endif
if (!(prt->status & FREE_PORT_FLAGS) &&
prt->drv_ptr->handle == dh) {
-#if DDLL_SMP
erts_smp_atomic_inc_nob(&prt->refc);
+#if DDLL_SMP
/* Extremely rare spinlock */
while(prt->status & ERTS_PORT_SFLG_INITIALIZING) {
erts_smp_port_state_unlock(prt);
@@ -598,13 +596,11 @@ done:
#endif
for (j = 0; j < erts_max_ports; j++) {
Port* prt = &erts_port[j];
-#if DDLL_SMP
erts_smp_port_state_lock(prt);
-#endif
if (!(prt->status & FREE_PORT_FLAGS)
&& prt->drv_ptr->handle == dh) {
-#if DDLL_SMP
erts_smp_atomic_inc_nob(&prt->refc);
+#if DDLL_SMP
/* Extremely rare spinlock */
while(prt->status & ERTS_PORT_SFLG_INITIALIZING) {
erts_smp_port_state_unlock(prt);
@@ -1060,13 +1056,11 @@ void erts_ddll_proc_dead(Process *p, ErtsProcLocks plocks)
#endif
for (j = 0; j < erts_max_ports; j++) {
Port* prt = &erts_port[j];
-#if DDLL_SMP
erts_smp_port_state_lock(prt);
-#endif
if (!(prt->status & FREE_PORT_FLAGS) &&
prt->drv_ptr->handle == dh) {
-#if DDLL_SMP
erts_smp_atomic_inc_nob(&prt->refc);
+#if DDLL_SMP
while(prt->status & ERTS_PORT_SFLG_INITIALIZING) {
erts_smp_port_state_unlock(prt);
erts_smp_port_state_lock(prt);
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index f56b00287f..a3811ccdb0 100755
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -1338,13 +1338,15 @@ process_info_aux(Process *BIF_P,
hp = HAlloc(BIF_P, 3);
break;
- case am_trap_exit:
+ case am_trap_exit: {
+ erts_aint32_t state = erts_smp_atomic32_read_nob(&rp->state);
hp = HAlloc(BIF_P, 3);
- if (rp->flags & F_TRAPEXIT)
+ if (state & ERTS_PSFLG_TRAP_EXIT)
res = am_true;
else
res = am_false;
break;
+ }
case am_error_handler:
hp = HAlloc(BIF_P, 3);
@@ -1697,7 +1699,7 @@ static int check_if_xml(void)
{
char buf[1];
size_t bufsz = sizeof(buf);
- return erts_sys_getenv("VALGRIND_LOG_XML", buf, &bufsz) >= 0;
+ return erts_sys_getenv_raw("VALGRIND_LOG_XML", buf, &bufsz) >= 0;
}
#else
#define check_if_xml() 0
@@ -3098,15 +3100,13 @@ BIF_RETTYPE is_process_alive_1(BIF_ALIST_1)
if(internal_pid_index(BIF_ARG_1) >= erts_max_processes)
BIF_ERROR(BIF_P, BADARG);
- rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
- BIF_ARG_1, ERTS_PROC_LOCK_STATUS);
+ rp = erts_proc_lookup(BIF_ARG_1);
if (!rp) {
BIF_RET(am_false);
}
else {
- int have_pending_exit = ERTS_PROC_PENDING_EXIT(rp);
- erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
- if (have_pending_exit)
+ if (erts_smp_atomic32_read_acqb(&rp->state)
+ & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING))
ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_false);
else
BIF_RET(am_true);
@@ -3710,9 +3710,8 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
&& is_internal_pid(tp[2])) {
int xres;
ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- Process *rp = erts_pid2proc_opt(BIF_P, ERTS_PROC_LOCK_MAIN,
- tp[2], rp_locks,
- ERTS_P2P_FLG_SMP_INC_REFC);
+ Process *rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
+ tp[2], rp_locks);
if (!rp) {
DECL_AM(dead);
BIF_RET(AM_dead);
@@ -3738,7 +3737,6 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
#endif
erts_smp_proc_unlock(rp, rp_locks);
- erts_smp_proc_dec_refc(rp);
if (xres > 1) {
DECL_AM(message);
BIF_RET(AM_message);
@@ -3949,7 +3947,7 @@ static Eterm lcnt_build_lock_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_t *lock
id = am_atom_put(ltype, strlen(ltype));
} else if (lock->flag & ERTS_LCNT_LT_PROCLOCK) {
/* use registered names as id's for process locks if available */
- proc = erts_pid2proc_unlocked(lock->id);
+ proc = erts_proc_lookup(lock->id);
if (proc && proc->reg) {
id = proc->reg->name;
} else {
diff --git a/erts/emulator/beam/erl_bif_os.c b/erts/emulator/beam/erl_bif_os.c
index 58d48199fa..1062d4379b 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-2010. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2012. 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
@@ -58,7 +58,7 @@ BIF_RETTYPE os_getpid_0(BIF_ALIST_0)
char pid_string[21]; /* enough for a 64 bit number */
int n;
Eterm* hp;
- sys_get_pid(pid_string); /* In sys.c */
+ sys_get_pid(pid_string, sizeof(pid_string)); /* In sys.c */
n = sys_strlen(pid_string);
hp = HAlloc(BIF_P, n*2);
BIF_RET(buf_to_intlist(&hp, pid_string, n, NIL));
@@ -71,15 +71,13 @@ BIF_RETTYPE os_getenv_0(BIF_ALIST_0)
Eterm* hp;
Eterm ret;
Eterm str;
- int len;
init_getenv_state(&state);
ret = NIL;
while ((cp = getenv_string(&state)) != NULL) {
- len = strlen(cp);
- hp = HAlloc(BIF_P, len*2+2);
- str = buf_to_intlist(&hp, cp, len, NIL);
+ str = erts_convert_native_to_filename(BIF_P,(byte *)cp);
+ hp = HAlloc(BIF_P, 2);
ret = CONS(hp, str, ret);
}
@@ -88,32 +86,30 @@ BIF_RETTYPE os_getenv_0(BIF_ALIST_0)
return ret;
}
-
+#define STATIC_BUF_SIZE 1024
BIF_RETTYPE os_getenv_1(BIF_ALIST_1)
{
Process* p = BIF_P;
- Eterm key = BIF_ARG_1;
Eterm str;
- int len, res;
+ Sint len;
+ int res;
char *key_str, *val;
- char buf[1024];
+ char buf[STATIC_BUF_SIZE];
size_t val_size = sizeof(buf);
- len = is_string(key);
- if (!len) {
+ key_str = erts_convert_filename_to_native(BIF_ARG_1,buf,STATIC_BUF_SIZE,
+ ERTS_ALC_T_TMP,1,0,&len);
+
+ if (!key_str) {
BIF_ERROR(p, BADARG);
}
- /* Leave at least one byte in buf for value */
- key_str = len < sizeof(buf)-2 ? &buf[0] : erts_alloc(ERTS_ALC_T_TMP, len+1);
- if (intlist_to_buf(key, key_str, len) != len)
- erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__);
- key_str[len] = '\0';
if (key_str != &buf[0])
val = &buf[0];
else {
- val_size -= len + 1;
- val = &buf[len + 1];
+ /* len includes zero byte */
+ val_size -= len;
+ val = &buf[len];
}
res = erts_sys_getenv(key_str, val, &val_size);
@@ -121,7 +117,6 @@ BIF_RETTYPE os_getenv_1(BIF_ALIST_1)
no_var:
str = am_false;
} else {
- Eterm* hp;
if (res > 0) {
val = erts_alloc(ERTS_ALC_T_TMP, val_size);
while (1) {
@@ -134,9 +129,7 @@ BIF_RETTYPE os_getenv_1(BIF_ALIST_1)
val = erts_realloc(ERTS_ALC_T_TMP, val, val_size);
}
}
- if (val_size)
- hp = HAlloc(p, val_size*2);
- str = buf_to_intlist(&hp, val, val_size, NIL);
+ str = erts_convert_native_to_filename(p,(byte *)val);
}
if (key_str != &buf[0])
erts_free(ERTS_ALC_T_TMP, key_str);
@@ -147,46 +140,43 @@ BIF_RETTYPE os_getenv_1(BIF_ALIST_1)
BIF_RETTYPE os_putenv_2(BIF_ALIST_2)
{
- Process* p = BIF_P;
- Eterm key = BIF_ARG_1;
- Eterm value = BIF_ARG_2;
- char def_buf[1024];
- char *buf = NULL;
- int sep_ix, i, key_len, value_len, tot_len;
- key_len = is_string(key);
- if (!key_len) {
- error:
- if (buf)
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
- BIF_ERROR(p, BADARG);
+ char def_buf_key[STATIC_BUF_SIZE];
+ char def_buf_value[STATIC_BUF_SIZE];
+ char *key_buf, *value_buf;
+
+ key_buf = erts_convert_filename_to_native(BIF_ARG_1,def_buf_key,
+ STATIC_BUF_SIZE,
+ ERTS_ALC_T_TMP,0,0,NULL);
+ if (!key_buf) {
+ BIF_ERROR(BIF_P, BADARG);
}
- if (is_nil(value))
- value_len = 0;
- else {
- value_len = is_string(value);
- if (!value_len)
- goto error;
+ value_buf = erts_convert_filename_to_native(BIF_ARG_2,def_buf_value,
+ STATIC_BUF_SIZE,
+ ERTS_ALC_T_TMP,1,0,
+ NULL);
+ if (!value_buf) {
+ if (key_buf != def_buf_key) {
+ erts_free(ERTS_ALC_T_TMP, key_buf);
+ }
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+
+ if (erts_sys_putenv(key_buf, value_buf)) {
+ if (key_buf != def_buf_key) {
+ erts_free(ERTS_ALC_T_TMP, key_buf);
+ }
+ if (value_buf != def_buf_value) {
+ erts_free(ERTS_ALC_T_TMP, value_buf);
+ }
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ if (key_buf != def_buf_key) {
+ erts_free(ERTS_ALC_T_TMP, key_buf);
}
- tot_len = key_len + 1 + value_len + 1;
- if (tot_len <= sizeof(def_buf))
- buf = &def_buf[0];
- else
- buf = erts_alloc(ERTS_ALC_T_TMP, tot_len);
- i = intlist_to_buf(key, buf, key_len);
- if (i != key_len)
- erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__);
- sep_ix = i;
- buf[i++] = '=';
- if (is_not_nil(value))
- i += intlist_to_buf(value, &buf[i], value_len);
- if (i != key_len + 1 + value_len)
- erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__);
- buf[i] = '\0';
- if (erts_sys_putenv(buf, sep_ix)) {
- goto error;
+ if (value_buf != def_buf_value) {
+ erts_free(ERTS_ALC_T_TMP, value_buf);
}
- if (buf != &def_buf[0])
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
BIF_RET(am_true);
}
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index 2a1b01b107..f9009166c0 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -718,7 +718,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
} else if (option == am_arg0) {
char *a0;
- if ((a0 = erts_convert_filename_to_native(*tp, ERTS_ALC_T_TMP, 1)) == NULL) {
+ if ((a0 = erts_convert_filename_to_native(*tp, NULL, 0, ERTS_ALC_T_TMP, 1, 1, NULL)) == NULL) {
goto badarg;
}
if (opts.argv == NULL) {
@@ -845,7 +845,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
goto badarg;
}
name = tp[1];
- if ((name_buf = erts_convert_filename_to_native(name,ERTS_ALC_T_TMP,0)) == NULL) {
+ if ((name_buf = erts_convert_filename_to_native(name, NULL, 0, ERTS_ALC_T_TMP,0,1, NULL)) == NULL) {
goto badarg;
}
opts.spawn_type = ERTS_SPAWN_EXECUTABLE;
@@ -909,7 +909,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
}
opts.wd = (char *) dir;
} else {
- if ((opts.wd = erts_convert_filename_to_native(edir,ERTS_ALC_T_TMP,0)) == NULL) {
+ if ((opts.wd = erts_convert_filename_to_native(edir, NULL, 0, ERTS_ALC_T_TMP,0,1,NULL)) == NULL) {
goto badarg;
}
}
@@ -1000,7 +1000,7 @@ static char **convert_args(Eterm l)
pp[i++] = erts_default_arg0;
while (is_list(l)) {
str = CAR(list_val(l));
- if ((b = erts_convert_filename_to_native(str,ERTS_ALC_T_TMP,1)) == NULL) {
+ if ((b = erts_convert_filename_to_native(str,NULL,0,ERTS_ALC_T_TMP,1,1,NULL)) == NULL) {
int j;
for (j = 1; j < i; ++j)
erts_free(ERTS_ALC_T_TMP, pp[j]);
@@ -1035,8 +1035,9 @@ static byte* convert_environment(Process* p, Eterm env)
Eterm* hp;
Uint heap_size;
int n;
- Uint size;
+ Sint size;
byte* bytes;
+ int encoding = erts_get_native_filename_encoding();
if ((n = list_length(env)) < 0) {
return NULL;
@@ -1079,7 +1080,8 @@ static byte* convert_environment(Process* p, Eterm env)
if (is_not_nil(env)) {
goto done;
}
- if (erts_iolist_size(all, &size)) {
+
+ if ((size = erts_native_filename_need(all,encoding)) < 0) {
goto done;
}
@@ -1087,7 +1089,7 @@ static byte* convert_environment(Process* p, Eterm env)
* Put the result in a binary (no risk for a memory leak that way).
*/
(void) erts_new_heap_binary(p, NULL, size, &bytes);
- io_list_to_buf(all, (char*)bytes, size);
+ erts_native_filename_put(all,encoding,bytes);
done:
erts_free(ERTS_ALC_T_TMP, temp_heap);
diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c
index 6b843d2e08..b036c5ef5c 100644
--- a/erts/emulator/beam/erl_bif_re.c
+++ b/erts/emulator/beam/erl_bif_re.c
@@ -71,14 +71,9 @@ void erts_init_bif_re(void)
erts_pcre_stack_free = &erts_erts_pcre_stack_free;
default_table = NULL; /* ISO8859-1 default, forced into pcre */
max_loop_limit = CONTEXT_REDS * LOOP_FACTOR;
-
- sys_memset((void *) &re_exec_trap_export, 0, sizeof(Export));
- re_exec_trap_export.address = &re_exec_trap_export.code[3];
- re_exec_trap_export.code[0] = am_erlang;
- re_exec_trap_export.code[1] = am_re_run_trap;
- re_exec_trap_export.code[2] = 3;
- re_exec_trap_export.code[3] = (BeamInstr) em_apply_bif;
- re_exec_trap_export.code[4] = (BeamInstr) &re_exec_trap;
+
+ erts_init_trap_export(&re_exec_trap_export, am_erlang, am_re_run_trap, 3,
+ &re_exec_trap);
grun_trap_exportp = erts_export_put(am_re,am_grun,3);
urun_trap_exportp = erts_export_put(am_re,am_urun,3);
diff --git a/erts/emulator/beam/erl_bif_timer.c b/erts/emulator/beam/erl_bif_timer.c
index d806be0704..525b11f61c 100644
--- a/erts/emulator/beam/erl_bif_timer.c
+++ b/erts/emulator/beam/erl_bif_timer.c
@@ -324,10 +324,9 @@ bif_timer_timeout(ErtsBifTimer* btm)
ASSERT(!erts_get_current_process());
if (btm->flags & BTM_FLG_BYNAME)
- rp = erts_whereis_process(NULL,0,btm->receiver.name,0,ERTS_P2P_FLG_SMP_INC_REFC);
+ rp = erts_whereis_process(NULL, 0, btm->receiver.name, 0, 0);
else {
rp = btm->receiver.proc.ess;
- erts_smp_proc_inc_refc(rp);
unlink_proc(btm);
}
@@ -379,7 +378,6 @@ bif_timer_timeout(ErtsBifTimer* btm)
#endif
);
erts_smp_proc_unlock(rp, rp_locks);
- erts_smp_proc_dec_refc(rp);
}
}
diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c
index 80f774523c..06cec1795d 100644
--- a/erts/emulator/beam/erl_bif_trace.c
+++ b/erts/emulator/beam/erl_bif_trace.c
@@ -42,12 +42,24 @@
#define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
const struct trace_pattern_flags erts_trace_pattern_flags_off = {0, 0, 0, 0, 0};
+
+/*
+ * The following variables are protected by code write permission.
+ */
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 struct { /* Protected by code write permission */
+ int current;
+ int install;
+ int local;
+ BpFunctions f; /* Local functions */
+ BpFunctions e; /* Export entries */
+} finish_bp;
+
static Eterm
trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist);
static BIF_RETTYPE
@@ -60,12 +72,11 @@ 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 int setup_func_trace(Export* ep, void* match_prog);
-static int reset_func_trace(Export* ep);
-static void reset_bif_trace(int bif_index);
-static void setup_bif_trace(int bif_index);
-static void set_trace_bif(int bif_index, void* match_prog);
-static void clear_trace_bif(int bif_index);
+static void reset_bif_trace(void);
+static void setup_bif_trace(void);
+static void install_exp_breakpoints(BpFunctions* f);
+static void uninstall_exp_breakpoints(BpFunctions* f);
+static void clean_export_entries(BpFunctions* f);
void
erts_bif_trace_init(void)
@@ -98,7 +109,7 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
{
DeclareTmpHeap(mfa,3,p); /* Not really heap here, but might be when setting pattern */
int i;
- int matches = 0;
+ int matches = -1;
int specified = 0;
enum erts_break_op on;
Binary* match_prog_set;
@@ -108,8 +119,10 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
Process *meta_tracer_proc = p;
Eterm meta_tracer_pid = p->id;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
+ if (!erts_try_seize_code_write_permission(p)) {
+ ERTS_BIF_YIELD3(bif_export[BIF_trace_pattern_3], p, MFA, Pattern, flaglist);
+ }
+ finish_bp.current = -1;
UseTmpHeap(3,p);
/*
@@ -241,7 +254,6 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
erts_default_meta_match_spec = NULL;
erts_default_meta_tracer_pid = NIL;
}
- MatchSetUnref(match_prog_set);
if (erts_default_trace_pattern_flags.breakpoint &&
flags.breakpoint) {
/* Breakpoint trace -> breakpoint trace */
@@ -297,8 +309,7 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
erts_default_trace_pattern_is_on = !!flags.breakpoint;
}
}
-
- goto done;
+ matches = 0;
} else if (is_tuple(MFA)) {
Eterm *tp = tuple_val(MFA);
if (tp[0] != make_arityval(3)) {
@@ -322,35 +333,37 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
if (is_small(mfa[2])) {
mfa[2] = signed_val(mfa[2]);
}
- } else {
- goto error;
- }
- if (meta_tracer_proc) {
- meta_tracer_proc->trace_flags |= F_TRACER;
- }
+ if (meta_tracer_proc) {
+ meta_tracer_proc->trace_flags |= F_TRACER;
+ }
+ matches = erts_set_trace_pattern(p, mfa, specified,
+ match_prog_set, match_prog_set,
+ on, flags, meta_tracer_pid, 0);
+ }
- matches = erts_set_trace_pattern(mfa, specified,
- match_prog_set, match_prog_set,
- on, flags, meta_tracer_pid);
+ error:
MatchSetUnref(match_prog_set);
-
- done:
UnUseTmpHeap(3,p);
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
-
- return make_small(matches);
- error:
+#ifdef ERTS_SMP
+ if (finish_bp.current >= 0) {
+ ASSERT(matches >= 0);
+ erts_notify_finish_breakpointing(p);
+ erts_suspend(p, ERTS_PROC_LOCK_MAIN, NULL);
+ ERTS_BIF_YIELD_RETURN(p, make_small(matches));
+ }
+#endif
- MatchSetUnref(match_prog_set);
+ erts_release_code_write_permission();
- UnUseTmpHeap(3,p);
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
- BIF_ERROR(p, BADARG);
+ if (matches >= 0) {
+ return make_small(matches);
+ }
+ else {
+ BIF_ERROR(p, BADARG);
+ }
}
void
@@ -360,6 +373,8 @@ erts_get_default_trace_pattern(int *trace_pattern_is_on,
struct trace_pattern_flags *trace_pattern_flags,
Eterm *meta_tracer_pid)
{
+ ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked() ||
+ erts_smp_thr_progress_is_blocking());
if (trace_pattern_is_on)
*trace_pattern_is_on = erts_default_trace_pattern_is_on;
if (match_spec)
@@ -372,7 +387,12 @@ erts_get_default_trace_pattern(int *trace_pattern_is_on,
*meta_tracer_pid = erts_default_meta_tracer_pid;
}
-
+int erts_is_default_trace_enabled(void)
+{
+ ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked() ||
+ erts_smp_thr_progress_is_blocking());
+ return erts_default_trace_pattern_is_on;
+}
Uint
erts_trace_flag2bit(Eterm flag)
@@ -466,6 +486,10 @@ Eterm trace_3(BIF_ALIST_3)
BIF_ERROR(p, BADARG);
}
+ 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);
+ }
+
if (is_nil(tracer) || is_internal_pid(tracer)) {
Process *tracer_proc = erts_pid2proc(p,
ERTS_PROC_LOCK_MAIN,
@@ -653,8 +677,7 @@ Eterm trace_3(BIF_ALIST_3)
if (procs || mods) {
/* tracing of processes */
for (i = 0; i < erts_max_processes; i++) {
- Process* tracee_p = process_tab[i];
-
+ Process* tracee_p = erts_pix2proc(i);
if (! tracee_p)
continue;
if (tracer != NIL) {
@@ -730,6 +753,7 @@ Eterm trace_3(BIF_ALIST_3)
erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
}
#endif
+ erts_release_code_write_permission();
BIF_RET(make_small(matches));
@@ -745,6 +769,7 @@ Eterm trace_3(BIF_ALIST_3)
erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
}
#endif
+ erts_release_code_write_permission();
BIF_ERROR(p, BADARG);
}
@@ -772,8 +797,7 @@ static int port_already_traced(Process *c_p, Port *tracee_port, Eterm tracer)
return 1;
}
else if(is_internal_pid(tracee_port->tracer_proc)) {
- Process *tracer_p = erts_pid2proc(c_p, ERTS_PROC_LOCK_MAIN,
- tracee_port->tracer_proc, 0);
+ Process *tracer_p = erts_proc_lookup(tracee_port->tracer_proc);
if (!tracer_p) {
/* Current trace process now invalid
* - discard it and approve the new. */
@@ -813,8 +837,7 @@ static int already_traced(Process *c_p, Process *tracee_p, Eterm tracer)
return 1;
}
else if(is_internal_pid(tracee_p->tracer_proc)) {
- Process *tracer_p = erts_pid2proc(c_p, ERTS_PROC_LOCK_MAIN,
- tracee_p->tracer_proc, 0);
+ Process *tracer_p = erts_proc_lookup(tracee_p->tracer_proc);
if (!tracer_p) {
/* Current trace process now invalid
* - discard it and approve the new. */
@@ -841,6 +864,11 @@ Eterm trace_info_2(BIF_ALIST_2)
Eterm What = BIF_ARG_1;
Eterm Key = BIF_ARG_2;
Eterm res;
+
+ if (!erts_try_seize_code_write_permission(p)) {
+ ERTS_BIF_YIELD2(bif_export[BIF_trace_info_2], p, What, Key);
+ }
+
if (What == am_on_load) {
res = trace_info_on_load(p, Key);
} else if (is_atom(What) || is_pid(What)) {
@@ -848,8 +876,10 @@ Eterm trace_info_2(BIF_ALIST_2)
} else if (is_tuple(What)) {
res = trace_info_func(p, What, Key);
} else {
+ erts_release_code_write_permission();
BIF_ERROR(p, BADARG);
}
+ erts_release_code_write_permission();
BIF_RET(res);
}
@@ -876,7 +906,7 @@ trace_info_pid(Process* p, Eterm pid_spec, Eterm key)
}
if (is_internal_pid(tracer)) {
- if (!erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, tracer, 0)) {
+ if (!erts_proc_lookup(tracer)) {
reset_tracer:
tracee->trace_flags &= ~TRACEE_FLAGS;
trace_flags = tracee->trace_flags;
@@ -977,64 +1007,54 @@ static int function_is_traced(Process *p,
Binary **ms, /* out */
Binary **ms_meta, /* out */
Eterm *tracer_pid_meta, /* out */
- Sint *count, /* out */
+ Uint *count, /* out */
Eterm *call_time) /* out */
{
Export e;
Export* ep;
- int i;
- BeamInstr *code;
+ BeamInstr* pc;
/* First look for an export entry */
e.code[0] = mfa[0];
e.code[1] = mfa[1];
e.code[2] = mfa[2];
if ((ep = export_get(&e)) != NULL) {
- if (ep->address == ep->code+3 &&
- ep->code[3] != (BeamInstr) em_call_error_handler) {
- if (ep->code[3] == (BeamInstr) em_call_traced_function) {
- *ms = ep->match_prog_set;
+ pc = ep->code+3;
+ if (ep->addressv[erts_active_code_ix()] == pc &&
+ *pc != (BeamInstr) em_call_error_handler) {
+
+ int r = 0;
+
+ ASSERT(*pc == (BeamInstr) em_apply_bif ||
+ *pc == (BeamInstr) BeamOp(op_i_generic_breakpoint));
+
+ if (erts_is_trace_break(pc, ms, 0)) {
return FUNC_TRACE_GLOBAL_TRACE;
}
- if (ep->code[3] == (BeamInstr) em_apply_bif) {
- for (i = 0; i < BIF_SIZE; ++i) {
- if (bif_export[i] == ep) {
- int r = 0;
-
- if (erts_bif_trace_flags[i] & BIF_TRACE_AS_GLOBAL) {
- *ms = ep->match_prog_set;
- return FUNC_TRACE_GLOBAL_TRACE;
- } else {
- if (erts_bif_trace_flags[i] & BIF_TRACE_AS_LOCAL) {
- r |= FUNC_TRACE_LOCAL_TRACE;
- *ms = ep->match_prog_set;
- }
- if (erts_is_mtrace_break(ep->code+3, ms_meta,
- tracer_pid_meta)) {
- r |= FUNC_TRACE_META_TRACE;
- }
- if (erts_is_time_break(p, ep->code+3, call_time)) {
- r |= FUNC_TRACE_TIME_TRACE;
- }
- }
- return r ? r : FUNC_TRACE_UNTRACED;
- }
- }
- erl_exit(1,"Impossible ghost bif encountered in trace_info.");
+
+ if (erts_is_trace_break(pc, ms, 1)) {
+ r |= FUNC_TRACE_LOCAL_TRACE;
+ }
+ if (erts_is_mtrace_break(pc, ms_meta, tracer_pid_meta)) {
+ r |= FUNC_TRACE_META_TRACE;
+ }
+ if (erts_is_time_break(p, pc, call_time)) {
+ r |= FUNC_TRACE_TIME_TRACE;
}
+ return r ? r : FUNC_TRACE_UNTRACED;
}
}
/* OK, now look for breakpoint tracing */
- if ((code = erts_find_local_func(mfa)) != NULL) {
+ if ((pc = erts_find_local_func(mfa)) != NULL) {
int r =
- (erts_is_trace_break(code, ms, NULL)
+ (erts_is_trace_break(pc, ms, 1)
? FUNC_TRACE_LOCAL_TRACE : 0)
- | (erts_is_mtrace_break(code, ms_meta, tracer_pid_meta)
+ | (erts_is_mtrace_break(pc, ms_meta, tracer_pid_meta)
? FUNC_TRACE_META_TRACE : 0)
- | (erts_is_count_break(code, count)
+ | (erts_is_count_break(pc, count)
? FUNC_TRACE_COUNT_TRACE : 0)
- | (erts_is_time_break(p, code, call_time)
+ | (erts_is_time_break(p, pc, call_time)
? FUNC_TRACE_TIME_TRACE : 0);
return r ? r : FUNC_TRACE_UNTRACED;
@@ -1049,7 +1069,7 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key)
Eterm* hp;
DeclareTmpHeap(mfa,3,p); /* Not really heap here, but might be when setting pattern */
Binary *ms = NULL, *ms_meta = NULL;
- Sint count = 0;
+ Uint count = 0;
Eterm traced = am_false;
Eterm match_spec = am_false;
Eterm retval = am_false;
@@ -1137,9 +1157,7 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key)
break;
case am_call_count:
if (r & FUNC_TRACE_COUNT_TRACE) {
- retval = count < 0 ?
- erts_make_integer(-count-1, p) :
- erts_make_integer(count, p);
+ retval = erts_make_integer(count, p);
}
break;
case am_call_time:
@@ -1328,38 +1346,53 @@ trace_info_on_load(Process* p, Eterm key)
#undef FUNC_TRACE_LOCAL_TRACE
int
-erts_set_trace_pattern(Eterm* mfa, int specified,
+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)
+ Eterm meta_tracer_pid, int is_blocking)
{
+ const ErtsCodeIndex code_ix = erts_active_code_ix();
int matches = 0;
int i;
+ int n;
+ BpFunction* fp;
/*
* First work on normal functions (not real BIFs).
*/
-
- for (i = 0; i < export_list_size(); i++) {
- Export* ep = export_list(i);
- int j;
-
- if (ExportIsBuiltIn(ep)) {
- continue;
- }
-
- for (j = 0; j < specified && mfa[j] == ep->code[j]; j++) {
- /* Empty loop body */
- }
- if (j == specified) {
- if (on) {
- if (! flags.breakpoint)
- matches += setup_func_trace(ep, match_prog_set);
- else
- reset_func_trace(ep);
- } else if (! flags.breakpoint) {
- matches += reset_func_trace(ep);
+ erts_bp_match_export(&finish_bp.e, mfa, specified);
+ fp = finish_bp.e.matching;
+ n = finish_bp.e.matched;
+
+ for (i = 0; i < n; i++) {
+ BeamInstr* pc = fp[i].pc;
+ Export* ep = (Export *)(((char *)(pc-3)) - offsetof(Export, code));
+
+ if (on && !flags.breakpoint) {
+ /* Turn on global call tracing */
+ if (ep->addressv[code_ix] != pc) {
+ fp[i].mod->curr.num_traced_exports++;
+#ifdef DEBUG
+ pc[-5] = (BeamInstr) BeamOp(op_i_func_info_IaaI);
+#endif
+ pc[0] = (BeamInstr) BeamOp(op_jump_f);
+ pc[1] = (BeamInstr) ep->addressv[code_ix];
+ }
+ erts_set_call_trace_bif(pc, match_prog_set, 0);
+ if (ep->addressv[code_ix] != pc) {
+ pc[0] = (BeamInstr) BeamOp(op_i_generic_breakpoint);
+ }
+ } else if (!on && flags.breakpoint) {
+ /* Turn off breakpoint tracing -- nothing to do here. */
+ } else {
+ /*
+ * Turn off global tracing, either explicitly or implicitly
+ * before turning on breakpoint tracing.
+ */
+ erts_clear_call_trace_bif(pc, 0);
+ if (pc[0] == (BeamInstr) BeamOp(op_i_generic_breakpoint)) {
+ pc[0] = (BeamInstr) BeamOp(op_jump_f);
}
}
}
@@ -1384,26 +1417,15 @@ erts_set_trace_pattern(Eterm* mfa, int specified,
/* Empty loop body */
}
if (j == specified) {
+ BeamInstr* pc = (BeamInstr *)bif_export[i]->code + 3;
+
if (! flags.breakpoint) { /* Export entry call trace */
if (on) {
- if (erts_bif_trace_flags[i] & BIF_TRACE_AS_META) {
- ASSERT(ExportIsBuiltIn(bif_export[i]));
- erts_clear_mtrace_bif
- ((BeamInstr *)bif_export[i]->code + 3);
- erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_META;
- }
- set_trace_bif(i, match_prog_set);
- erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_LOCAL;
- erts_bif_trace_flags[i] |= BIF_TRACE_AS_GLOBAL;
- setup_bif_trace(i);
+ erts_clear_call_trace_bif(pc, 1);
+ erts_clear_mtrace_bif(pc);
+ erts_set_call_trace_bif(pc, match_prog_set, 0);
} else { /* off */
- if (erts_bif_trace_flags[i] & BIF_TRACE_AS_GLOBAL) {
- clear_trace_bif(i);
- erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_GLOBAL;
- }
- if (! erts_bif_trace_flags[i]) {
- reset_bif_trace(i);
- }
+ erts_clear_call_trace_bif(pc, 0);
}
matches++;
} else { /* Breakpoint call trace */
@@ -1411,52 +1433,33 @@ erts_set_trace_pattern(Eterm* mfa, int specified,
if (on) {
if (flags.local) {
- set_trace_bif(i, match_prog_set);
- erts_bif_trace_flags[i] |= BIF_TRACE_AS_LOCAL;
- erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_GLOBAL;
+ erts_clear_call_trace_bif(pc, 0);
+ erts_set_call_trace_bif(pc, match_prog_set, 1);
m = 1;
}
if (flags.meta) {
- erts_set_mtrace_bif
- ((BeamInstr *)bif_export[i]->code + 3,
- meta_match_prog_set, meta_tracer_pid);
- erts_bif_trace_flags[i] |= BIF_TRACE_AS_META;
- erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_GLOBAL;
+ erts_set_mtrace_bif(pc, meta_match_prog_set,
+ meta_tracer_pid);
m = 1;
}
if (flags.call_time) {
- erts_set_time_trace_bif(bif_export[i]->code + 3, on);
+ erts_set_time_trace_bif(pc, on);
/* I don't want to remove any other tracers */
- erts_bif_trace_flags[i] |= BIF_TRACE_AS_CALL_TIME;
m = 1;
}
- if (erts_bif_trace_flags[i]) {
- setup_bif_trace(i);
- }
} else { /* off */
if (flags.local) {
- if (erts_bif_trace_flags[i] & BIF_TRACE_AS_LOCAL) {
- clear_trace_bif(i);
- erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_LOCAL;
- }
+ erts_clear_call_trace_bif(pc, 1);
m = 1;
}
if (flags.meta) {
- if (erts_bif_trace_flags[i] & BIF_TRACE_AS_META) {
- erts_clear_mtrace_bif
- ((BeamInstr *)bif_export[i]->code + 3);
- erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_META;
- }
+ erts_clear_mtrace_bif(pc);
m = 1;
}
if (flags.call_time) {
- erts_clear_time_trace_bif(bif_export[i]->code + 3);
- erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_CALL_TIME;
+ erts_clear_time_trace_bif(pc);
m = 1;
}
- if (! erts_bif_trace_flags[i]) {
- reset_bif_trace(i);
- }
}
matches += m;
}
@@ -1466,176 +1469,242 @@ erts_set_trace_pattern(Eterm* mfa, int specified,
/*
** So, now for breakpoint tracing
*/
+ erts_bp_match_functions(&finish_bp.f, mfa, specified);
if (on) {
if (! flags.breakpoint) {
- erts_clear_trace_break(mfa, specified);
- erts_clear_mtrace_break(mfa, specified);
- erts_clear_count_break(mfa, specified);
- erts_clear_time_break(mfa, specified);
+ erts_clear_all_breaks(&finish_bp.f);
} else {
- int m = 0;
if (flags.local) {
- m = erts_set_trace_break(mfa, specified, match_prog_set,
- am_true);
+ erts_set_trace_break(&finish_bp.f, match_prog_set);
}
if (flags.meta) {
- m = erts_set_mtrace_break(mfa, specified, meta_match_prog_set,
- meta_tracer_pid);
+ erts_set_mtrace_break(&finish_bp.f, meta_match_prog_set,
+ meta_tracer_pid);
}
if (flags.call_count) {
- m = erts_set_count_break(mfa, specified, on);
+ erts_set_count_break(&finish_bp.f, on);
}
if (flags.call_time) {
- m = erts_set_time_break(mfa, specified, on);
+ erts_set_time_break(&finish_bp.f, on);
}
- /* All assignments to 'm' above should give the same value,
- * so just use the last */
- matches += m;
}
} else {
- int m = 0;
if (flags.local) {
- m = erts_clear_trace_break(mfa, specified);
+ erts_clear_trace_break(&finish_bp.f);
}
if (flags.meta) {
- m = erts_clear_mtrace_break(mfa, specified);
+ erts_clear_mtrace_break(&finish_bp.f);
}
if (flags.call_count) {
- m = erts_clear_count_break(mfa, specified);
+ erts_clear_count_break(&finish_bp.f);
}
if (flags.call_time) {
- m = erts_clear_time_break(mfa, specified);
+ erts_clear_time_break(&finish_bp.f);
}
- /* All assignments to 'm' above should give the same value,
- * so just use the last */
- matches += m;
}
+ finish_bp.current = 0;
+ finish_bp.install = on;
+ finish_bp.local = flags.breakpoint;
+
+#ifdef ERTS_SMP
+ if (is_blocking) {
+ ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
+#endif
+ while (erts_finish_breakpointing()) {
+ /* Empty loop body */
+ }
+#ifdef ERTS_SMP
+ finish_bp.current = -1;
+ }
+#endif
+
+ if (flags.breakpoint) {
+ matches += finish_bp.f.matched;
+ } else {
+ matches += finish_bp.e.matched;
+ }
return matches;
}
-/*
- * Setup function tracing for the given exported function.
- *
- * Return Value: 1 if entry refers to a BIF or loaded function,
- * 0 if the entry refers to a function not loaded.
- */
-
-static int
-setup_func_trace(Export* ep, void* match_prog)
+int
+erts_finish_breakpointing(void)
{
- if (ep->address == ep->code+3) {
- if (ep->code[3] == (BeamInstr) em_call_error_handler) {
- return 0;
- } else if (ep->code[3] == (BeamInstr) em_call_traced_function) {
- MatchSetUnref(ep->match_prog_set);
- ep->match_prog_set = match_prog;
- MatchSetRef(ep->match_prog_set);
- return 1;
- } else {
- /*
- * We ignore apply/3 and anything else.
- */
- return 0;
- }
- }
-
+ ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked());
+
/*
- * Currently no trace support for native code.
+ * Memory barriers will be issued for all processes *before*
+ * each of the stages below. (Unless the other schedulers
+ * are blocked, in which case memory barriers will be issued
+ * when they are awaken.)
*/
- if (erts_is_native_break(ep->address)) {
+
+ switch (finish_bp.current++) {
+ case 0:
+ /*
+ * At this point, in all functions that are to be breakpointed,
+ * a pointer to a GenericBp struct has already been added,
+ *
+ * Insert the new breakpoints (if any) into the
+ * code. Different schedulers may see breakpoint instruction
+ * at different times, but it does not matter since the newly
+ * added breakpoints are disabled.
+ */
+ if (finish_bp.install) {
+ if (finish_bp.local) {
+ erts_install_breakpoints(&finish_bp.f);
+ } else {
+ install_exp_breakpoints(&finish_bp.e);
+ }
+ }
+ setup_bif_trace();
+ return 1;
+ case 1:
+ /*
+ * Switch index for the breakpoint data, activating the staged
+ * data. (Depending on the changes in the breakpoint data,
+ * that could either activate breakpoints or disable
+ * breakpoints.)
+ */
+ erts_commit_staged_bp();
+ return 1;
+ case 2:
+ /*
+ * Remove breakpoints instructions for disabled breakpoints
+ * (if any).
+ */
+ if (finish_bp.install) {
+ if (finish_bp.local) {
+ uninstall_exp_breakpoints(&finish_bp.e);
+ } else {
+ erts_uninstall_breakpoints(&finish_bp.f);
+ }
+ } else {
+ if (finish_bp.local) {
+ erts_uninstall_breakpoints(&finish_bp.f);
+ } else {
+ uninstall_exp_breakpoints(&finish_bp.e);
+ }
+ }
+ reset_bif_trace();
+ return 1;
+ case 3:
+ /*
+ * Now all breakpoints have either been inserted or removed.
+ * For all updated breakpoints, copy the active breakpoint
+ * data to the staged breakpoint data to make them equal
+ * (simplifying for the next time breakpoints are to be
+ * updated). If any breakpoints have been totally disabled,
+ * deallocate the GenericBp structs for them.
+ */
+ erts_consolidate_bif_bp_data();
+ clean_export_entries(&finish_bp.e);
+ erts_consolidate_bp_data(&finish_bp.e, 0);
+ erts_consolidate_bp_data(&finish_bp.f, 1);
+ erts_bp_free_matched_functions(&finish_bp.e);
+ erts_bp_free_matched_functions(&finish_bp.f);
return 0;
+ default:
+ ASSERT(0);
}
-
- ep->code[3] = (BeamInstr) em_call_traced_function;
- ep->code[4] = (BeamInstr) ep->address;
- ep->address = ep->code+3;
- ep->match_prog_set = match_prog;
- MatchSetRef(ep->match_prog_set);
- return 1;
+ return 0;
}
-static void setup_bif_trace(int bif_index) {
- Export *ep = bif_export[bif_index];
-
- ASSERT(ExportIsBuiltIn(ep));
- ASSERT(ep->code[4]);
- ep->code[4] = (BeamInstr) bif_table[bif_index].traced;
-}
+static void
+install_exp_breakpoints(BpFunctions* f)
+{
+ const ErtsCodeIndex code_ix = erts_active_code_ix();
+ BpFunction* fp = f->matching;
+ Uint ne = f->matched;
+ Uint i;
+ Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr);
-static void set_trace_bif(int bif_index, void* match_prog) {
- Export *ep = bif_export[bif_index];
-
-#ifdef HARDDEBUG
- erts_fprintf(stderr, "set_trace_bif: %T:%T/%bpu\n",
- ep->code[0], ep->code[1], ep->code[2]);
-#endif
- ASSERT(ExportIsBuiltIn(ep));
- MatchSetUnref(ep->match_prog_set);
- ep->match_prog_set = match_prog;
- MatchSetRef(ep->match_prog_set);
-}
+ for (i = 0; i < ne; i++) {
+ BeamInstr* pc = fp[i].pc;
+ Export* ep = (Export *) (((char *)pc)-offset);
-/*
- * Reset function tracing for the given exported function.
- *
- * Return Value: 1 if entry refers to a BIF or loaded function,
- * 0 if the entry refers to a function not loaded.
- */
+ ep->addressv[code_ix] = pc;
+ }
+}
-static int
-reset_func_trace(Export* ep)
+static void
+uninstall_exp_breakpoints(BpFunctions* f)
{
- if (ep->address == ep->code+3) {
- if (ep->code[3] == (BeamInstr) em_call_error_handler) {
- return 0;
- } else if (ep->code[3] == (BeamInstr) em_call_traced_function) {
- ep->address = (Uint *) ep->code[4];
- MatchSetUnref(ep->match_prog_set);
- ep->match_prog_set = NULL;
- return 1;
- } else {
- /*
- * We ignore apply/3 and anything else.
- */
- return 0;
+ const ErtsCodeIndex code_ix = erts_active_code_ix();
+ 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);
+
+ if (ep->addressv[code_ix] != pc) {
+ continue;
}
+ ASSERT(*pc == (BeamInstr) BeamOp(op_jump_f));
+ ep->addressv[code_ix] = (BeamInstr *) ep->code[4];
}
-
- /*
- * Currently no trace support for native code.
- */
- if (erts_is_native_break(ep->address)) {
- return 0;
- }
-
- /*
- * Nothing to do, but the export entry matches.
- */
+}
+
+static void
+clean_export_entries(BpFunctions* f)
+{
+ const ErtsCodeIndex code_ix = erts_active_code_ix();
+ BpFunction* fp = f->matching;
+ Uint ne = f->matched;
+ Uint i;
+ Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr);
- return 1;
+ for (i = 0; i < ne; i++) {
+ BeamInstr* pc = fp[i].pc;
+ Export* ep = (Export *) (((char *)pc)-offset);
+
+ if (ep->addressv[code_ix] == pc) {
+ continue;
+ }
+ if (*pc == (BeamInstr) BeamOp(op_jump_f)) {
+ ep->code[3] = (BeamInstr) 0;
+ ep->code[4] = (BeamInstr) 0;
+ }
+ }
}
-static void reset_bif_trace(int bif_index) {
- Export *ep = bif_export[bif_index];
-
- ASSERT(ExportIsBuiltIn(ep));
- ASSERT(ep->code[4]);
- ASSERT(! ep->match_prog_set);
- ASSERT(! erts_is_mtrace_break((BeamInstr *)ep->code+3, NULL, NULL));
- ep->code[4] = (BeamInstr) bif_table[bif_index].f;
+static void
+setup_bif_trace(void)
+{
+ int i;
+
+ for (i = 0; i < BIF_SIZE; ++i) {
+ Export *ep = bif_export[i];
+ GenericBp* g = (GenericBp *) ep->fake_op_func_info_for_hipe[1];
+ if (g) {
+ if (ExportIsBuiltIn(ep)) {
+ ASSERT(ep->code[4]);
+ ep->code[4] = (BeamInstr) bif_table[i].traced;
+ }
+ }
+ }
}
-static void clear_trace_bif(int bif_index) {
- Export *ep = bif_export[bif_index];
-
-#ifdef HARDDEBUG
- erts_fprintf(stderr, "clear_trace_bif: %T:%T/%bpu\n",
- ep->code[0], ep->code[1], ep->code[2]);
-#endif
- ASSERT(ExportIsBuiltIn(ep));
- MatchSetUnref(ep->match_prog_set);
- ep->match_prog_set = NULL;
+static void
+reset_bif_trace(void)
+{
+ int i;
+ ErtsBpIndex active = erts_active_bp_ix();
+
+ for (i = 0; i < BIF_SIZE; ++i) {
+ Export *ep = bif_export[i];
+ BeamInstr* pc = ep->code+3;
+ GenericBp* g = (GenericBp *) pc[-4];
+ if (g && g->data[active].flags == 0) {
+ if (ExportIsBuiltIn(ep)) {
+ ASSERT(ep->code[4]);
+ ep->code[4] = (BeamInstr) bif_table[i].f;
+ }
+ }
+ }
}
/*
@@ -2142,9 +2211,9 @@ BIF_RETTYPE system_profile_2(BIF_ALIST_2)
/* Check if valid process, no locks are taken */
if (is_internal_pid(profiler)) {
- if (internal_pid_index(profiler) >= erts_max_processes) goto error;
- profiler_p = process_tab[internal_pid_index(profiler)];
- if (INVALID_PID(profiler_p, profiler)) goto error;
+ profiler_p = erts_proc_lookup(profiler);
+ if (!profiler_p)
+ goto error;
} else if (is_internal_port(profiler)) {
if (internal_port_index(profiler) >= erts_max_ports) goto error;
profiler_port = &erts_port[internal_port_index(profiler)];
diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c
index 6f7309f493..6bc227eeda 100644
--- a/erts/emulator/beam/erl_bits.c
+++ b/erts/emulator/beam/erl_bits.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2012. 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
@@ -1005,8 +1005,13 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags)
if (is_float(arg)) {
FloatDef *fdp = (FloatDef*)(float_val(arg) + 1);
+#ifdef DOUBLE_MIDDLE_ENDIAN
+ a = fdp->fw[1];
+ b = fdp->fw[0];
+#else
a = fdp->fw[0];
b = fdp->fw[1];
+#endif
} else if (is_small(arg)) {
u.f64 = (double) signed_val(arg);
a = u.i32[0];
@@ -1015,8 +1020,13 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags)
if (big_to_double(arg, &u.f64) < 0) {
return 0;
}
+#ifdef DOUBLE_MIDDLE_ENDIAN
+ a = u.i32[1];
+ b = u.i32[0];
+#else
a = u.i32[0];
b = u.i32[1];
+#endif
} else {
return 0;
}
diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c
index fe3693d0ca..3f90f34736 100644
--- a/erts/emulator/beam/erl_cpu_topology.c
+++ b/erts/emulator/beam/erl_cpu_topology.c
@@ -486,7 +486,7 @@ erts_sched_check_cpu_bind_post_suspend(ErtsSchedulerData *esdp)
erts_thr_set_main_status(1, (int) esdp->no);
/* Make sure we check if we should bind to a cpu or not... */
- esdp->run_queue->flags |= ERTS_RUNQ_FLG_CHK_CPU_BIND;
+ (void) ERTS_RUNQ_FLGS_SET(esdp->run_queue, ERTS_RUNQ_FLG_CHK_CPU_BIND);
}
#endif
@@ -498,9 +498,6 @@ erts_sched_check_cpu_bind(ErtsSchedulerData *esdp)
erts_cpu_groups_map_t *cgm;
erts_cpu_groups_callback_list_t *cgcl;
erts_cpu_groups_callback_call_t *cgcc;
-#ifdef ERTS_SMP
- esdp->run_queue->flags &= ~ERTS_RUNQ_FLG_CHK_CPU_BIND;
-#endif
erts_smp_runq_unlock(esdp->run_queue);
erts_smp_rwmtx_rwlock(&cpuinfo_rwmtx);
cpu_id = scheduler2cpu_map[esdp->no].bind_id;
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index 51bdf53823..4c30905495 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -224,8 +224,9 @@ Export ets_select_continue_exp;
static Export ets_delete_continue_exp;
static void
-free_dbtable(DbTable* tb)
+free_dbtable(void *vtb)
{
+ DbTable *tb = (DbTable *) vtb;
#ifdef HARDDEBUG
if (erts_smp_atomic_read_nob(&tb->common.memory_size) != sizeof(DbTable)) {
erts_fprintf(stderr, "ets: free_dbtable memory remain=%ld fix=%x\n",
@@ -251,20 +252,8 @@ free_dbtable(DbTable* tb)
ASSERT(is_immed(tb->common.heir_data));
erts_db_free(ERTS_ALC_T_DB_TABLE, tb, (void *) tb, sizeof(DbTable));
ERTS_ETS_MISC_MEM_ADD(-sizeof(DbTable));
- ERTS_SMP_MEMORY_BARRIER;
}
-#ifdef ERTS_SMP
-static void
-chk_free_dbtable(void *vtb)
-{
- DbTable * tb = (DbTable *) vtb;
- ERTS_THR_MEMORY_BARRIER;
- if (erts_refc_dectest(&tb->common.ref, 0) == 0)
- free_dbtable(tb);
-}
-#endif
-
static void schedule_free_dbtable(DbTable* tb)
{
/*
@@ -275,15 +264,10 @@ static void schedule_free_dbtable(DbTable* tb)
* need to unlock the table lock after this
* function has returned).
*/
-#ifdef ERTS_SMP
- int scheds = erts_get_max_no_executing_schedulers();
- ASSERT(scheds >= 1);
ASSERT(erts_refc_read(&tb->common.ref, 0) == 0);
- erts_refc_init(&tb->common.ref, scheds);
- erts_schedule_multi_misc_aux_work(0, scheds, chk_free_dbtable, tb);
-#else
- free_dbtable(tb);
-#endif
+ erts_schedule_thr_prgr_later_op(free_dbtable,
+ (void *) tb,
+ &tb->release.data);
}
static ERTS_INLINE void db_init_lock(DbTable* tb, int use_frequent_read_lock,
@@ -542,10 +526,6 @@ static int remove_named_tab(DbTable *tb, int have_lock)
&rwlock);
#ifdef ERTS_SMP
if (!have_lock && erts_smp_rwmtx_tryrwlock(rwlock) == EBUSY) {
- /*
- * We keep our increased refc over this op in order to
- * prevent the table from disapearing.
- */
db_unlock(tb, LCK_WRITE);
erts_smp_rwmtx_rwlock(rwlock);
db_lock(tb, LCK_WRITE);
@@ -1481,7 +1461,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
"** Too many db tables **\n");
free_heir_data(tb);
tb->common.meth->db_free_table(tb);
- free_dbtable(tb);
+ free_dbtable((void *) tb);
BIF_ERROR(BIF_P, SYSTEM_LIMIT);
}
@@ -2816,7 +2796,6 @@ void init_db(void)
{
DbTable init_tb;
int i;
- extern BeamInstr* em_apply_bif;
Eterm *hp;
unsigned bits;
size_t size;
@@ -2850,7 +2829,7 @@ void init_db(void)
else
db_max_tabs = user_requested_db_max_tabs;
- bits = erts_fit_in_bits(db_max_tabs-1);
+ bits = erts_fit_in_bits_int32(db_max_tabs-1);
if (bits > SMALL_BITS) {
erl_exit(1,"Max limit for ets tabled too high %u (max %u).",
db_max_tabs, ((Uint)1)<<SMALL_BITS);
@@ -2949,49 +2928,24 @@ void init_db(void)
}
/* Non visual BIF to trap to. */
- memset(&ets_select_delete_continue_exp, 0, sizeof(Export));
- ets_select_delete_continue_exp.address =
- &ets_select_delete_continue_exp.code[3];
- ets_select_delete_continue_exp.code[0] = am_ets;
- ets_select_delete_continue_exp.code[1] = am_atom_put("delete_trap",11);
- ets_select_delete_continue_exp.code[2] = 1;
- ets_select_delete_continue_exp.code[3] =
- (BeamInstr) em_apply_bif;
- ets_select_delete_continue_exp.code[4] =
- (BeamInstr) &ets_select_delete_1;
+ erts_init_trap_export(&ets_select_delete_continue_exp,
+ am_ets, am_atom_put("delete_trap",11), 1,
+ &ets_select_delete_1);
/* Non visual BIF to trap to. */
- memset(&ets_select_count_continue_exp, 0, sizeof(Export));
- ets_select_count_continue_exp.address =
- &ets_select_count_continue_exp.code[3];
- ets_select_count_continue_exp.code[0] = am_ets;
- ets_select_count_continue_exp.code[1] = am_atom_put("count_trap",11);
- ets_select_count_continue_exp.code[2] = 1;
- ets_select_count_continue_exp.code[3] =
- (BeamInstr) em_apply_bif;
- ets_select_count_continue_exp.code[4] =
- (BeamInstr) &ets_select_count_1;
+ erts_init_trap_export(&ets_select_count_continue_exp,
+ am_ets, am_atom_put("count_trap",11), 1,
+ &ets_select_count_1);
/* Non visual BIF to trap to. */
- memset(&ets_select_continue_exp, 0, sizeof(Export));
- ets_select_continue_exp.address =
- &ets_select_continue_exp.code[3];
- ets_select_continue_exp.code[0] = am_ets;
- ets_select_continue_exp.code[1] = am_atom_put("select_trap",11);
- ets_select_continue_exp.code[2] = 1;
- ets_select_continue_exp.code[3] =
- (BeamInstr) em_apply_bif;
- ets_select_continue_exp.code[4] =
- (BeamInstr) &ets_select_trap_1;
+ erts_init_trap_export(&ets_select_continue_exp,
+ am_ets, am_atom_put("select_trap",11), 1,
+ &ets_select_trap_1);
/* Non visual BIF to trap to. */
- memset(&ets_delete_continue_exp, 0, sizeof(Export));
- ets_delete_continue_exp.address = &ets_delete_continue_exp.code[3];
- ets_delete_continue_exp.code[0] = am_ets;
- ets_delete_continue_exp.code[1] = am_atom_put("delete_trap",11);
- ets_delete_continue_exp.code[2] = 1;
- ets_delete_continue_exp.code[3] = (BeamInstr) em_apply_bif;
- ets_delete_continue_exp.code[4] = (BeamInstr) &ets_delete_trap;
+ erts_init_trap_export(&ets_delete_continue_exp,
+ am_ets, am_atom_put("delete_trap",11), 1,
+ &ets_delete_trap);
hp = ms_delete_all_buff;
ms_delete_all = CONS(hp, am_true, NIL);
@@ -3124,7 +3078,7 @@ retry:
if (to_proc == NULL) {
return 0; /* heir not alive, table still mine */
}
- if (erts_cmp_timeval(&to_proc->started, &tb->common.heir_started) != 0) {
+ if (to_proc->started_interval != tb->common.heir_started_interval) {
erts_smp_proc_unlock(to_proc, to_locks);
return 0; /* heir dead and pid reused, table still mine */
}
@@ -3520,14 +3474,14 @@ static void set_heir(Process* me, DbTable* tb, Eterm heir, UWord heir_data)
return;
}
if (heir == me->id) {
- tb->common.heir_started = me->started;
+ erts_ensure_later_proc_interval(me->started_interval);
+ tb->common.heir_started_interval = me->started_interval;
}
else {
- Process* heir_proc= erts_pid2proc_opt(me, ERTS_PROC_LOCK_MAIN, heir,
- 0, ERTS_P2P_FLG_SMP_INC_REFC);
+ Process* heir_proc= erts_proc_lookup(heir);
if (heir_proc != NULL) {
- tb->common.heir_started = heir_proc->started;
- erts_smp_proc_dec_refc(heir_proc);
+ erts_ensure_later_proc_interval(heir_proc->started_interval);
+ tb->common.heir_started_interval = heir_proc->started_interval;
} else {
tb->common.heir = am_none;
}
diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h
index 2e5deaf338..e9a661efbc 100644
--- a/erts/emulator/beam/erl_db.h
+++ b/erts/emulator/beam/erl_db.h
@@ -27,6 +27,10 @@
#define __DB_H__
#include "sys.h"
+#undef ERL_THR_PROGRESS_TSD_TYPE_ONLY
+#define ERL_THR_PROGRESS_TSD_TYPE_ONLY
+#include "erl_thr_progress.h"
+#undef ERL_THR_PROGRESS_TSD_TYPE_ONLY
#include "bif.h"
#include "erl_db_util.h" /* Flags */
@@ -36,6 +40,11 @@
Uint erts_get_ets_misc_mem_size(void);
+typedef struct {
+ DbTableCommon common;
+ ErtsThrPrgrLaterOp data;
+} DbTableRelease;
+
/*
* So, the structure for a database table, NB this is only
* interesting in db.c.
@@ -44,6 +53,7 @@ union db_table {
DbTableCommon common; /* Any type of db table */
DbTableHash hash; /* Linear hash array specific data */
DbTableTree tree; /* AVL tree specific data */
+ DbTableRelease release;
/*TT*/
};
diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c
index 312050b931..faa7f31d99 100644
--- a/erts/emulator/beam/erl_db_tree.c
+++ b/erts/emulator/beam/erl_db_tree.c
@@ -452,16 +452,8 @@ DbTableMethod db_tree =
void db_initialize_tree(void)
{
- memset(&ets_select_reverse_exp, 0, sizeof(Export));
- ets_select_reverse_exp.address =
- &ets_select_reverse_exp.code[3];
- ets_select_reverse_exp.code[0] = am_ets;
- ets_select_reverse_exp.code[1] = am_reverse;
- ets_select_reverse_exp.code[2] = 3;
- ets_select_reverse_exp.code[3] =
- (BeamInstr) em_apply_bif;
- ets_select_reverse_exp.code[4] =
- (BeamInstr) &ets_select_reverse;
+ erts_init_trap_export(&ets_select_reverse_exp, am_ets, am_reverse, 3,
+ &ets_select_reverse);
return;
};
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index c2f6cfa933..0c9ca83ce4 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -1233,7 +1233,7 @@ static Eterm erts_match_set_run_ets(Process *p, Binary *mpsp,
Eterm ret;
ret = db_prog_match(p, mpsp, args, NULL, NULL, num_args,
- ERTS_PAM_CONTIGUOUS_TUPLE | ERTS_PAM_COPY_RESULT,
+ ERTS_PAM_COPY_RESULT,
return_flags);
#if defined(HARDDEBUG)
if (is_non_value(ret)) {
@@ -2480,7 +2480,7 @@ Eterm db_format_dmc_err_info(Process *p, DMCErrInfo *ei)
vnum = tmp->variable;
}
if (vnum >= 0)
- sprintf(buff,tmp->error_string, vnum);
+ erts_snprintf(buff,sizeof(buff)+20,tmp->error_string, vnum);
else
strcpy(buff,tmp->error_string);
sl = strlen(buff);
@@ -2995,7 +2995,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),make_tuple(bp->debug_clone),bp->debug_clone));
+ ASSERT(eq_rel(make_tuple(hp),NULL,make_tuple(bp->debug_clone),bp->debug_clone));
#endif
return make_tuple(hp);
}
@@ -3018,7 +3018,7 @@ Eterm db_copy_element_from_ets(DbTableCommon* tb, Process* p,
hp += extra;
HRelease(p, endp, hp);
#ifdef DEBUG_CLONE
- ASSERT(eq_rel(copy, obj->debug_clone[pos], obj->debug_clone));
+ ASSERT(eq_rel(copy, NULL, obj->debug_clone[pos], obj->debug_clone));
#endif
return copy;
}
@@ -4485,7 +4485,9 @@ static DMCRet dmc_fun(DMCContext *context,
if (context->err_info != NULL) {
/* Ugly, should define a better RETURN_TERM_ERROR interface... */
char buff[100];
- sprintf(buff, "Function %%T/%d does_not_exist.", (int)a - 1);
+ erts_snprintf(buff, sizeof(buff),
+ "Function %%T/%d does_not_exist.",
+ (int)a - 1);
RETURN_TERM_ERROR(buff, p[1], context, *constant);
} else {
return retFail;
@@ -4500,7 +4502,7 @@ static DMCRet dmc_fun(DMCContext *context,
if (context->err_info != NULL) {
/* Ugly, should define a better RETURN_TERM_ERROR interface... */
char buff[100];
- sprintf(buff,
+ erts_snprintf(buff, sizeof(buff),
"Function %%T/%d cannot be called in this context.",
(int)a - 1);
RETURN_TERM_ERROR(buff, p[1], context, *constant);
@@ -4764,7 +4766,7 @@ static int match_compact(ErlHeapFragment *expr, DMCErrInfo *err_info)
for (j = 0; j < x && DMC_PEEK(heap,j) != n; ++j)
;
ASSERT(j < x);
- sprintf(buff+1,"%u", (unsigned) j);
+ erts_snprintf(buff+1, sizeof(buff) - 1, "%u", (unsigned) j);
/* Yes, writing directly into terms, they ARE off heap */
*p = am_atom_put(buff, strlen(buff));
}
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index 6a96e174e1..dcecc4251a 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -209,7 +209,7 @@ typedef struct db_fixation {
*/
typedef struct db_table_common {
- erts_refc_t ref; /* fixation counter and delete counter */
+ erts_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 */
@@ -219,7 +219,7 @@ typedef struct db_table_common {
Eterm owner; /* Pid of the creator */
Eterm heir; /* Pid of the heir */
UWord heir_data; /* To send in ETS-TRANSFER (is_immed or (DbTerm*) */
- SysTimeval heir_started; /* To further identify the heir */
+ Uint64 heir_started_interval; /* To further identify the heir */
Eterm the_name; /* an atom */
Eterm id; /* atom | integer */
DbTableMethod* meth; /* table methods */
diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c
index 2121f72fd2..22e873afc6 100644
--- a/erts/emulator/beam/erl_debug.c
+++ b/erts/emulator/beam/erl_debug.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2010. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2012. 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/beam/erl_debug.h b/erts/emulator/beam/erl_debug.h
index a028a95fef..af51212281 100644
--- a/erts/emulator/beam/erl_debug.h
+++ b/erts/emulator/beam/erl_debug.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2012. 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/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h
index 1ae9a211d7..771ee46d2b 100644
--- a/erts/emulator/beam/erl_driver.h
+++ b/erts/emulator/beam/erl_driver.h
@@ -87,10 +87,7 @@
#include <stdlib.h>
#include <string.h> /* ssize_t on Mac OS X */
-#if defined(VXWORKS)
-# include <ioLib.h>
-typedef struct iovec SysIOVec;
-#elif defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)
+#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)
#ifndef STATIC_ERLANG_DRIVER
/* Windows dynamic drivers, everything is different... */
#define ERL_DRIVER_TYPES_ONLY
@@ -370,11 +367,7 @@ typedef struct erl_drv_entry {
/* For windows dynamic drivers */
#ifndef ERL_DRIVER_TYPES_ONLY
-#if defined(VXWORKS)
-# define DRIVER_INIT(DRIVER_NAME) \
- ErlDrvEntry* DRIVER_NAME ## _init(void); \
- ErlDrvEntry* DRIVER_NAME ## _init(void)
-#elif defined(__WIN32__)
+#if defined(__WIN32__)
# define DRIVER_INIT(DRIVER_NAME) \
__declspec(dllexport) ErlDrvEntry* driver_init(void); \
__declspec(dllexport) ErlDrvEntry* driver_init(void)
diff --git a/erts/emulator/beam/erl_fun.h b/erts/emulator/beam/erl_fun.h
index 54cfd6aa83..b673ef6b3c 100644
--- a/erts/emulator/beam/erl_fun.h
+++ b/erts/emulator/beam/erl_fun.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2012. 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
@@ -77,8 +77,6 @@ ErlFunEntry* erts_get_fun_entry(Eterm mod, int uniq, int index);
ErlFunEntry* erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index,
byte* uniq, int index, int arity);
-ErlFunEntry* erts_get_fun_entry2(Eterm mod, int old_uniq, int old_index,
- byte* uniq, int index, int arity);
void erts_erase_fun_entry(ErlFunEntry* fe);
void erts_cleanup_funs(ErlFunThing* funp);
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index 52a6e52e6c..6075a527c3 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -357,11 +357,7 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
trace_gc(p, am_gc_start);
}
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- p->gcstatus = p->status;
- p->status = P_GARBING;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
-
+ erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
if (erts_system_monitor_long_gc != 0) {
get_now(&ms1, &s1, &us1);
}
@@ -404,9 +400,8 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
ErtsGcQuickSanityCheck(p);
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- p->status = p->gcstatus;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ 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);
}
@@ -490,10 +485,7 @@ erts_garbage_collect_hibernate(Process* p)
/*
* Preliminaries.
*/
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- p->gcstatus = p->status;
- p->status = P_GARBING;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
ErtsGcQuickSanityCheck(p);
ASSERT(p->mbuf_sz == 0);
ASSERT(p->mbuf == 0);
@@ -604,9 +596,7 @@ erts_garbage_collect_hibernate(Process* p)
ErtsGcQuickSanityCheck(p);
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- p->status = p->gcstatus;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
}
@@ -630,10 +620,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
/*
* Set GC state.
*/
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- p->gcstatus = p->status;
- p->status = P_GARBING;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
/*
* We assume that the caller has already done a major collection
@@ -775,9 +762,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
/*
* Restore status.
*/
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- p->status = p->gcstatus;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
}
static int
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 02164728fe..4b90e5394a 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -55,6 +55,61 @@
#endif
/*
+ * The variables below (prefixed with etp_) are for erts/etc/unix/etp-commands
+ * only. Do not remove even though they aren't used elsewhere in the emulator!
+ */
+#ifdef ERTS_SMP
+const int etp_smp_compiled = 1;
+#else
+const int etp_smp_compiled = 0;
+#endif
+#ifdef USE_THREADS
+const int etp_thread_compiled = 1;
+#else
+const int etp_thread_compiled = 0;
+#endif
+const char etp_erts_version[] = ERLANG_VERSION;
+const char etp_otp_release[] = ERLANG_OTP_RELEASE;
+const char etp_compile_date[] = ERLANG_COMPILE_DATE;
+const char etp_arch[] = ERLANG_ARCHITECTURE;
+#ifdef ERTS_ENABLE_KERNEL_POLL
+const int etp_kernel_poll_support = 1;
+#else
+const int etp_kernel_poll_support = 0;
+#endif
+#if defined(ARCH_64)
+const int etp_arch_bits = 64;
+#elif defined(ARCH_32)
+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
+const int etp_hipe = 0;
+#endif
+#ifdef DEBUG
+const int etp_debug_compiled = 1;
+#else
+const int etp_debug_compiled = 0;
+#endif
+#ifdef ERTS_ENABLE_LOCK_COUNT
+const int etp_lock_count = 1;
+#else
+const int etp_lock_count = 0;
+#endif
+#ifdef ERTS_ENABLE_LOCK_CHECK
+const int etp_lock_check = 1;
+#else
+const int etp_lock_check = 0;
+#endif
+/*
* Note about VxWorks: All variables must be initialized by executable code,
* not by an initializer. Otherwise a new instance of the emulator will
* inherit previous values.
@@ -241,6 +296,7 @@ erl_init(int ncpu)
erts_init_trace();
erts_init_binary();
erts_init_bits();
+ erts_code_ix_init();
erts_init_fun_table();
init_atom_table();
init_export_table();
@@ -289,7 +345,8 @@ erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char**
Eterm env;
start_mod = am_atom_put(modname, sys_strlen(modname));
- if (erts_find_function(start_mod, am_start, 2) == NULL) {
+ if (erts_find_function(start_mod, am_start, 2,
+ erts_active_code_ix()) == NULL) {
erl_exit(5, "No function %s:start/2\n", modname);
}
@@ -388,7 +445,7 @@ load_preloaded(void)
if ((code = sys_preload_begin(&preload_p[i])) == 0)
erl_exit(1, "Failed to find preloaded code for module %s\n",
name);
- res = erts_load_module(NULL, 0, NIL, &module_name, code, length);
+ res = erts_preload_module(NULL, 0, NIL, &module_name, code, length);
sys_preload_end(&preload_p[i]);
if (res != NIL)
erl_exit(1,"Failed loading preloaded module %s (%T)\n",
@@ -555,6 +612,7 @@ early_init(int *argc, char **argv) /*
erts_printf_eterm_func = erts_printf_term;
erts_disable_tolerant_timeofday = 0;
display_items = 200;
+ erts_proc.max = ERTS_DEFAULT_MAX_PROCESSES;
erts_backtrace_depth = DEFAULT_BACKTRACE_SIZE;
erts_async_max_threads = 0;
erts_async_thread_suggested_stack_size = ERTS_ASYNC_THREAD_MIN_STACK_SIZE;
@@ -623,7 +681,7 @@ early_init(int *argc, char **argv) /*
envbufsz = sizeof(envbuf);
- /* erts_sys_getenv() not initialized yet; need erts_sys_getenv__() */
+ /* erts_sys_getenv(_raw)() not initialized yet; need erts_sys_getenv__() */
if (erts_sys_getenv__("ERL_THREAD_POOL_SIZE", envbuf, &envbufsz) == 0)
erts_async_max_threads = atoi(envbuf);
else
@@ -846,13 +904,13 @@ erl_start(int argc, char **argv)
int ncpu = early_init(&argc, argv);
envbufsz = sizeof(envbuf);
- if (erts_sys_getenv(ERL_MAX_ETS_TABLES_ENV, envbuf, &envbufsz) == 0)
+ if (erts_sys_getenv_raw(ERL_MAX_ETS_TABLES_ENV, envbuf, &envbufsz) == 0)
user_requested_db_max_tabs = atoi(envbuf);
else
user_requested_db_max_tabs = 0;
envbufsz = sizeof(envbuf);
- if (erts_sys_getenv("ERL_FULLSWEEP_AFTER", envbuf, &envbufsz) == 0) {
+ if (erts_sys_getenv_raw("ERL_FULLSWEEP_AFTER", envbuf, &envbufsz) == 0) {
Uint16 max_gen_gcs = atoi(envbuf);
erts_smp_atomic32_set_nob(&erts_max_gen_gcs,
(erts_aint32_t) max_gen_gcs);
@@ -1097,7 +1155,7 @@ erl_start(int argc, char **argv)
case 'P':
/* set maximum number of processes */
Parg = get_arg(argv[i]+2, argv[i+1], &i);
- erts_max_processes = atoi(Parg);
+ erts_proc.max = atoi(Parg);
/* Check of result is delayed until later. This is because +R
may be given after +P. */
break;
@@ -1396,10 +1454,10 @@ erl_start(int argc, char **argv)
}
/* Delayed check of +P flag */
- if (erts_max_processes < ERTS_MIN_PROCESSES
- || erts_max_processes > ERTS_MAX_PROCESSES
+ if (erts_proc.max < ERTS_MIN_PROCESSES
+ || erts_proc.max > ERTS_MAX_PROCESSES
|| (erts_use_r9_pids_ports
- && erts_max_processes > ERTS_MAX_R9_PROCESSES)) {
+ && erts_proc.max > ERTS_MAX_R9_PROCESSES)) {
erts_fprintf(stderr, "bad number of processes %s\n", Parg);
erts_usage();
}
@@ -1427,6 +1485,8 @@ erl_start(int argc, char **argv)
erl_init(ncpu);
load_preloaded();
+ erts_end_staging_code_ix();
+ erts_commit_staging_code_ix();
erts_initialized = 1;
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index a0f744be9d..11fed4079d 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -82,8 +82,8 @@ static erts_lc_lock_order_t erts_lock_order[] = {
#ifdef ERTS_SMP
{ "bif_timers", NULL },
{ "reg_tab", NULL },
- { "migration_info_update", NULL },
{ "proc_main", "pid" },
+ { "old_code", "address" },
#ifdef HIPE
{ "hipe_mfait_lock", NULL },
#endif
@@ -93,6 +93,7 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "proc_msgq", "pid" },
{ "dist_entry", "address" },
{ "dist_entry_links", "address" },
+ { "code_ix_queue", NULL },
{ "proc_status", "pid" },
{ "proc_tab", NULL },
{ "ports_snapshot", NULL },
@@ -124,6 +125,7 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "removed_fd_pre_alloc_lock", "address" },
{ "state_prealloc", NULL },
{ "schdlr_sspnd", NULL },
+ { "migration_info_update", NULL },
{ "run_queue", "address" },
{ "cpu_info", NULL },
{ "pollset", "address" },
@@ -175,9 +177,6 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "sched_stat", NULL },
#endif
{ "async_init_mtx", NULL },
-#ifdef ERTS_SMP
- { "proc_lck_qs_alloc", NULL },
-#endif
#ifdef __WIN32__
#ifdef DEBUG
{ "save_ops_lock", NULL },
@@ -446,7 +445,7 @@ print_lock2(char *prefix, Sint16 id, Wterm extra, Uint16 flags, char *suffix)
"%s'%s:%p%s'%s%s",
prefix,
lname,
- boxed_val(extra),
+ _unchecked_boxed_val(extra),
lock_type(flags),
rw_op_str(flags),
suffix);
diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c
index 741c0cb08e..5f75b0ac0b 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-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2012. 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/beam/erl_lock_count.h b/erts/emulator/beam/erl_lock_count.h
index 690551c71f..a4fc91b510 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-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2012. 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/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index 919567ab27..e397f075d1 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -296,36 +296,6 @@ erts_msg_distext2heap(Process *pp,
return THE_NON_VALUE;
}
-static ERTS_INLINE void
-notify_new_message(Process *receiver)
-{
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS
- & erts_proc_lc_my_proc_locks(receiver));
-
- switch (receiver->status) {
- case P_GARBING:
- switch (receiver->gcstatus) {
- case P_SUSPENDED:
- goto suspended;
- case P_WAITING:
- goto waiting;
- default:
- break;
- }
- break;
- case P_SUSPENDED:
- suspended:
- receiver->rstatus = P_RUNABLE;
- break;
- case P_WAITING:
- waiting:
- erts_add_to_runq(receiver);
- break;
- default:
- break;
- }
-}
-
void
erts_queue_dist_message(Process *rcvr,
ErtsProcLocks *rcvr_locks,
@@ -339,7 +309,7 @@ erts_queue_dist_message(Process *rcvr,
Sint tok_serial = 0;
#endif
#ifdef ERTS_SMP
- ErtsProcLocks need_locks;
+ erts_aint_t state;
#endif
ERTS_SMP_LC_ASSERT(*rcvr_locks == erts_proc_lc_my_proc_locks(rcvr));
@@ -347,20 +317,21 @@ erts_queue_dist_message(Process *rcvr,
mp = message_alloc();
#ifdef ERTS_SMP
- need_locks = ~(*rcvr_locks) & (ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
- if (need_locks) {
- *rcvr_locks |= need_locks;
- if (erts_smp_proc_trylock(rcvr, need_locks) == EBUSY) {
- if (need_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) {
erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_STATUS);
- need_locks = (ERTS_PROC_LOCK_MSGQ
- | ERTS_PROC_LOCK_STATUS);
+ need_locks |= ERTS_PROC_LOCK_STATUS;
}
erts_smp_proc_lock(rcvr, need_locks);
}
}
- if (rcvr->is_exiting || ERTS_PROC_PENDING_EXIT(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))
+ 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;
@@ -376,6 +347,8 @@ erts_queue_dist_message(Process *rcvr,
/* Ahh... need to decode it in order to trace it... */
ErlHeapFragment *mbuf;
Eterm msg;
+ 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))
@@ -437,26 +410,33 @@ erts_queue_dist_message(Process *rcvr,
mp->data.dist_ext = dist_ext;
LINK_MESSAGE(rcvr, mp);
- notify_new_message(rcvr);
+ if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ))
+ erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
+
+ erts_proc_notify_new_message(rcvr);
}
}
/* Add a message last in message queue */
-void
-erts_queue_message(Process* receiver,
- ErtsProcLocks *receiver_locks,
- ErlHeapFragment* bp,
- Eterm message,
- Eterm seq_trace_token
+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
-)
+ )
{
+ Sint res;
ErlMessage* mp;
-#ifdef ERTS_SMP
- ErtsProcLocks need_locks;
-#else
+ int locked_msgq = 0;
+ erts_aint_t state;
+
+#ifndef ERTS_SMP
ASSERT(bp != NULL || receiver->mbuf == NULL);
#endif
@@ -464,31 +444,45 @@ erts_queue_message(Process* receiver,
mp = message_alloc();
+ if (receiver_state)
+ state = *receiver_state;
+ else
+ state = erts_smp_atomic32_read_acqb(&receiver->state);
+
#ifdef ERTS_SMP
- 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) {
+
+ if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT))
+ goto exiting;
+
+ 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_MSGQ
- | ERTS_PROC_LOCK_STATUS);
+ need_locks |= ERTS_PROC_LOCK_STATUS;
}
erts_smp_proc_lock(receiver, need_locks);
}
+ locked_msgq = 1;
+ state = erts_smp_atomic32_read_nob(&receiver->state);
+ if (receiver_state)
+ *receiver_state = state;
}
- if (receiver->is_exiting || ERTS_PROC_PENDING_EXIT(receiver)) {
- /* Drop message if receiver is exiting or has a pending
- * exit ...
- */
+#endif
+
+ if (state & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING)) {
+#ifdef ERTS_SMP
+ exiting:
+#endif
+ /* 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);
- return;
+ return 0;
}
-#endif
ERL_MESSAGE_TERM(mp) = message;
ERL_MESSAGE_TOKEN(mp) = seq_trace_token;
@@ -498,7 +492,10 @@ erts_queue_message(Process* receiver,
mp->next = NULL;
mp->data.heap_frag = bp;
-#ifdef ERTS_SMP
+#ifndef ERTS_SMP
+ res = receiver->msg.len;
+#else
+ res = receiver->u.alive.msg_inq.len;
if (*receiver_locks & ERTS_PROC_LOCK_MAIN) {
/*
* We move 'in queue' to 'private queue' and place
@@ -508,15 +505,15 @@ erts_queue_message(Process* receiver,
* we don't need to include the 'in queue' in
* the root set when garbage collecting.
*/
+ res += receiver->msg.len;
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(receiver);
LINK_MESSAGE_PRIVQ(receiver, mp);
}
- else {
+ else
+#endif
+ {
LINK_MESSAGE(receiver, mp);
}
-#else
- LINK_MESSAGE(receiver, mp);
-#endif
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(message_queued)) {
@@ -536,15 +533,43 @@ erts_queue_message(Process* receiver,
tok_label, tok_lastcnt, tok_serial);
}
#endif
- notify_new_message(receiver);
- if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) {
+ if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE))
trace_receive(receiver, message);
- }
+
+ if (locked_msgq)
+ erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_MSGQ);
+
+ erts_proc_notify_new_message(receiver);
#ifndef ERTS_SMP
ERTS_HOLE_CHECK(receiver);
#endif
+ return res;
+}
+
+void
+erts_queue_message(Process* receiver,
+ ErtsProcLocks *receiver_locks,
+ ErlHeapFragment* bp,
+ Eterm message,
+ Eterm seq_trace_token
+#ifdef USE_VM_PROBES
+ , Eterm dt_utag
+#endif
+ )
+{
+ queue_message(NULL,
+ receiver,
+ receiver_locks,
+ NULL,
+ bp,
+ message,
+ seq_trace_token
+#ifdef USE_VM_PROBES
+ , dt_utag
+#endif
+ );
}
void
@@ -576,9 +601,7 @@ erts_move_msg_mbuf_to_heap(Eterm** hpp, ErlOffHeap* off_heap, ErlMessage *msg)
#endif
#ifdef HARD_DEBUG
- ProcBin *dbg_mso_start = off_heap->mso;
- ErlFunThing *dbg_fun_start = off_heap->funs;
- ExternalThing *dbg_external_start = off_heap->externals;
+ 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;
@@ -752,48 +775,16 @@ copy_done:
int i, j;
ErlHeapFragment* frag;
{
- ProcBin *mso = off_heap->mso;
- i = j = 0;
- while (mso != dbg_mso_start) {
- mso = mso->next;
- i++;
- }
- for (frag=bp; frag; frag=frag->next) {
- mso = frag->off_heap.mso;
- while (mso) {
- mso = mso->next;
- j++;
- }
- }
- ASSERT(i == j);
- }
- {
- ErlFunThing *fun = off_heap->funs;
+ struct erl_off_heap_header* dbg_oh = off_heap->first;
i = j = 0;
- while (fun != dbg_fun_start) {
- fun = fun->next;
+ while (dbg_oh != dbg_oh_start) {
+ dbg_oh = dbg_oh->next;
i++;
}
for (frag=bp; frag; frag=frag->next) {
- fun = frag->off_heap.funs;
- while (fun) {
- fun = fun->next;
- j++;
- }
- }
- ASSERT(i == j);
- }
- {
- ExternalThing *external = off_heap->externals;
- i = j = 0;
- while (external != dbg_external_start) {
- external = external->next;
- i++;
- }
- for (frag=bp; frag; frag=frag->next) {
- external = frag->off_heap.externals;
- while (external) {
- external = external->next;
+ dbg_oh = frag->off_heap.first;
+ while (dbg_oh) {
+ dbg_oh = dbg_oh->next;
j++;
}
}
@@ -878,7 +869,7 @@ erts_move_msg_attached_data_to_heap(Eterm **hpp, ErlOffHeap *ohp, ErlMessage *ms
* Send a local message when sender & receiver processes are known.
*/
-void
+Sint
erts_send_message(Process* sender,
Process* receiver,
ErtsProcLocks *receiver_locks,
@@ -888,6 +879,7 @@ erts_send_message(Process* sender,
Uint msize;
ErlHeapFragment* bp = NULL;
Eterm token = NIL;
+ Sint res = 0;
#ifdef USE_VM_PROBES
DTRACE_CHARBUF(sender_name, 64);
DTRACE_CHARBUF(receiver_name, 64);
@@ -974,15 +966,17 @@ erts_send_message(Process* sender,
msize, tok_label, tok_lastcnt, tok_serial);
}
#endif
- erts_queue_message(receiver,
- receiver_locks,
- bp,
- message,
- token
+ res = queue_message(NULL,
+ receiver,
+ receiver_locks,
+ NULL,
+ bp,
+ message,
+ token
#ifdef USE_VM_PROBES
- , utag
+ , utag
#endif
- );
+ );
BM_SWAP_TIMER(send,system);
} else if (sender == receiver) {
/* Drop message if receiver has a pending exit ... */
@@ -1026,31 +1020,45 @@ erts_send_message(Process* sender,
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);
- return;
} else {
#ifdef ERTS_SMP
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(msize,&bp,&ohp,receiver,receiver_locks);
+ 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);
DTRACE6(message_send, sender_name, receiver_name,
msize, tok_label, tok_lastcnt, tok_serial);
- erts_queue_message(receiver, receiver_locks, bp, message, token
+ res = queue_message(sender,
+ receiver,
+ receiver_locks,
+ &state,
+ bp,
+ message,
+ token
#ifdef USE_VM_PROBES
- , NIL
+ , NIL
#endif
- );
+ );
BM_SWAP_TIMER(send,system);
#else
ErlMessage* mp = message_alloc();
@@ -1080,12 +1088,9 @@ erts_send_message(Process* sender,
mp->next = NULL;
mp->data.attached = NULL;
LINK_MESSAGE(receiver, mp);
+ res = receiver->msg.len;
+ erts_proc_notify_new_message(receiver);
- if (receiver->status == P_WAITING) {
- erts_add_to_runq(receiver);
- } else if (receiver->status == P_SUSPENDED) {
- receiver->rstatus = P_RUNABLE;
- }
if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) {
trace_receive(receiver, message);
}
@@ -1093,6 +1098,7 @@ erts_send_message(Process* sender,
#endif /* #ifndef ERTS_SMP */
return;
}
+ return res;
}
/*
diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h
index 3e9a24ee81..2ae94965b1 100644
--- a/erts/emulator/beam/erl_message.h
+++ b/erts/emulator/beam/erl_message.h
@@ -90,7 +90,7 @@ typedef struct {
ErlMessage* first;
ErlMessage** last; /* point to the last next pointer */
ErlMessage** save;
- int len; /* queue length */
+ Sint len; /* queue length */
/*
* The following two fields are used by the recv_mark/1 and
@@ -105,7 +105,7 @@ typedef struct {
typedef struct {
ErlMessage* first;
ErlMessage** last; /* point to the last next pointer */
- int len; /* queue length */
+ Sint len; /* queue length */
} ErlMessageInQueue;
#endif
@@ -125,23 +125,23 @@ typedef struct {
#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; \
- } \
+#define ERTS_SMP_MSGQ_MV_INQ2PRIVQ(P) \
+do { \
+ if ((P)->u.alive.msg_inq.first) { \
+ *(P)->msg.last = (P)->u.alive.msg_inq.first; \
+ (P)->msg.last = (P)->u.alive.msg_inq.last; \
+ (P)->msg.len += (P)->u.alive.msg_inq.len; \
+ (P)->u.alive.msg_inq.first = NULL; \
+ (P)->u.alive.msg_inq.last = &(P)->u.alive.msg_inq.first; \
+ (P)->u.alive.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++; \
+ *(p)->u.alive.msg_inq.last = (mp); \
+ (p)->u.alive.msg_inq.last = &(mp)->next; \
+ (p)->u.alive.msg_inq.len++; \
} while(0)
#else
@@ -234,7 +234,7 @@ void erts_queue_message(Process*, ErtsProcLocks*, ErlHeapFragment*, Eterm, Eterm
#endif
);
void erts_deliver_exit_message(Eterm, Process*, ErtsProcLocks *, Eterm, Eterm);
-void erts_send_message(Process*, Process*, ErtsProcLocks*, Eterm, unsigned);
+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 *);
diff --git a/erts/emulator/beam/erl_mtrace.c b/erts/emulator/beam/erl_mtrace.c
index 358c67bf20..5a6fb8589f 100644
--- a/erts/emulator/beam/erl_mtrace.c
+++ b/erts/emulator/beam/erl_mtrace.c
@@ -611,7 +611,7 @@ void erts_mtrace_init(char *receiver, char *nodename)
if (erts_sock_gethostname(hostname, MAXHOSTNAMELEN) != 0)
hostname[0] = '\0';
hostname[MAXHOSTNAMELEN-1] = '\0';
- sys_get_pid(pid);
+ sys_get_pid(pid, sizeof(pid));
write_trace_header(nodename ? nodename : "", pid, hostname);
erts_mtrace_update_heap_size();
}
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index b323bc7f69..0dca588847 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -33,6 +33,10 @@
#include "big.h"
#include "beam_bp.h"
#include "erl_thr_progress.h"
+#include "dtrace-wrapper.h"
+#if defined(USE_DYNAMIC_TRACE) && (defined(USE_DTRACE) || defined(USE_SYSTEMTAP))
+#define HAVE_USE_DTRACE 1
+#endif
#include <limits.h>
#include <stddef.h> /* offsetof */
@@ -311,6 +315,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
#endif
Eterm receiver = to_pid->pid;
int flush_me = 0;
+ int scheduler = erts_get_scheduler_id() != 0;
if (env != NULL) {
c_p = env->proc;
@@ -330,8 +335,11 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP)
rp_had_locks = rp_locks;
#endif
- rp = erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN,
- receiver, rp_locks, ERTS_P2P_FLG_SMP_INC_REFC);
+
+ rp = (scheduler
+ ? erts_proc_lookup(receiver)
+ : erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN,
+ receiver, rp_locks, ERTS_P2P_FLG_SMP_INC_REFC));
if (rp == NULL) {
ASSERT(env == NULL || receiver != c_p->id);
return 0;
@@ -358,12 +366,12 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
, NIL
#endif
);
- if (rp_locks) {
- ERTS_SMP_LC_ASSERT(rp_locks == (rp_had_locks | (ERTS_PROC_LOCK_MSGQ |
- ERTS_PROC_LOCK_STATUS)));
- erts_smp_proc_unlock(rp, (ERTS_PROC_LOCK_MSGQ | ERTS_PROC_LOCK_STATUS));
- }
- erts_smp_proc_dec_refc(rp);
+ if (c_p == rp)
+ rp_locks &= ~ERTS_PROC_LOCK_MAIN;
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+ if (!scheduler)
+ erts_smp_proc_dec_refc(rp);
if (flush_me) {
cache_env(env);
}
@@ -1520,6 +1528,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
if (len < 0) {
BIF_ERROR(BIF_P, BADARG);
}
+
lib_name = (char *) erts_alloc(ERTS_ALC_T_TMP, len + 1);
if (intlist_to_buf(BIF_ARG_1, lib_name, len) != len) {
@@ -1528,6 +1537,12 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
}
lib_name[len] = '\0';
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ erts_free(ERTS_ALC_T_TMP, lib_name);
+ ERTS_BIF_YIELD2(bif_export[BIF_load_nif_2],
+ BIF_P, BIF_ARG_1, BIF_ARG_2);
+ }
+
/* Block system (is this the right place to do it?) */
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
erts_smp_thr_progress_block();
@@ -1541,11 +1556,11 @@ 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);
+ mod=erts_get_module(mod_atom, erts_active_code_ix());
ASSERT(mod != NULL);
- if (!in_area(caller, mod->code, mod->code_length)) {
- ASSERT(in_area(caller, mod->old_code, mod->old_code_length));
+ if (!in_area(caller, mod->curr.code, mod->curr.code_length)) {
+ ASSERT(in_area(caller, mod->old.code, mod->old.code_length));
ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old "
"module '%T' not allowed", mod_atom);
@@ -1591,7 +1606,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
BeamInstr** code_pp;
ErlNifFunc* f = &entry->funcs[i];
if (!erts_atom_get(f->name, sys_strlen(f->name), &f_atom)
- || (code_pp = get_func_pp(mod->code, f_atom, f->arity))==NULL) {
+ || (code_pp = get_func_pp(mod->curr.code, 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);
}
@@ -1620,18 +1635,18 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
erts_refc_init(&lib->rt_dtor_cnt, 0);
lib->mod = mod;
env.mod_nif = lib;
- if (mod->nif != NULL) { /* Reload */
+ if (mod->curr.nif != NULL) { /* Reload */
int k;
- lib->priv_data = mod->nif->priv_data;
+ lib->priv_data = mod->curr.nif->priv_data;
- ASSERT(mod->nif->entry != NULL);
+ ASSERT(mod->curr.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 */
- for (k=0; k < mod->nif->entry->num_of_funcs; k++) {
- ErlNifFunc* old_func = &mod->nif->entry->funcs[k];
+ for (k=0; k < mod->curr.nif->entry->num_of_funcs; k++) {
+ ErlNifFunc* old_func = &mod->curr.nif->entry->funcs[k];
for (i=0; i < entry->num_of_funcs; i++) {
if (old_func->arity == entry->funcs[i].arity
&& sys_strcmp(old_func->name, entry->funcs[i].name) == 0) {
@@ -1652,24 +1667,24 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
ret = load_nif_error(BIF_P, reload, "Library reload-call unsuccessful.");
}
else {
- mod->nif->entry = NULL; /* to prevent 'unload' callback */
- erts_unload_nif(mod->nif);
+ mod->curr.nif->entry = NULL; /* to prevent 'unload' callback */
+ erts_unload_nif(mod->curr.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 (mod->old.nif != NULL) { /* Upgrade */
+ void* prev_old_data = mod->old.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);
+ veto = entry->upgrade(&env, &lib->priv_data, &mod->old.nif->priv_data, BIF_ARG_2);
erts_post_nif(&env);
if (veto) {
- mod->old_nif->priv_data = prev_old_data;
+ mod->old.nif->priv_data = prev_old_data;
ret = load_nif_error(BIF_P, upgrade, "Library upgrade-call unsuccessful.");
}
/*else if (mod->old_nif->priv_data != prev_old_data) {
@@ -1689,20 +1704,21 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
/*
** Everything ok, patch the beam code with op_call_nif
*/
- mod->nif = lib;
+ mod->curr.nif = lib;
for (i=0; i < entry->num_of_funcs; i++)
{
BeamInstr* code_ptr;
erts_atom_get(entry->funcs[i].name, sys_strlen(entry->funcs[i].name), &f_atom);
- code_ptr = *get_func_pp(mod->code, f_atom, entry->funcs[i].arity);
+ code_ptr = *get_func_pp(mod->curr.code, f_atom, entry->funcs[i].arity);
if (code_ptr[1] == 0) {
code_ptr[5+0] = (BeamInstr) BeamOp(op_call_nif);
}
else { /* Function traced, patch the original instruction word */
- BpData** bps = (BpData**) code_ptr[1];
- BpData* bp = (BpData*) bps[erts_bp_sched2ix()];
- bp->orig_instr = (BeamInstr) BeamOp(op_call_nif);
+ GenericBp* g = (GenericBp *) code_ptr[1];
+ ASSERT(code_ptr[5+0] ==
+ (BeamInstr) BeamOp(op_i_generic_breakpoint));
+ g->orig_instr = (BeamInstr) BeamOp(op_call_nif);
}
code_ptr[5+1] = (BeamInstr) entry->funcs[i].fptr;
code_ptr[5+2] = (BeamInstr) lib;
@@ -1722,6 +1738,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
erts_smp_thr_progress_unblock();
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_release_code_write_permission();
erts_free(ERTS_ALC_T_TMP, lib_name);
if (reload_warning) {
@@ -1841,3 +1858,1079 @@ static unsigned calc_checksum(unsigned char* ptr, unsigned size)
#endif /* READONLY_CHECK */
+#ifdef HAVE_USE_DTRACE
+
+#define MESSAGE_BUFSIZ 1024
+
+static void get_string_maybe(ErlNifEnv *env, const ERL_NIF_TERM term,
+ char **ptr, char *buf, int bufsiz)
+{
+ ErlNifBinary str_bin;
+
+ if (!enif_inspect_iolist_as_binary(env, term, &str_bin) ||
+ str_bin.size > bufsiz) {
+ *ptr = NULL;
+ } else {
+ memcpy(buf, (char *) str_bin.data, str_bin.size);
+ buf[str_bin.size] = '\0';
+ *ptr = buf;
+ }
+}
+
+ERL_NIF_TERM erl_nif_user_trace_s1(ErlNifEnv* env, int argc,
+ const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary message_bin;
+ DTRACE_CHARBUF(messagebuf, MESSAGE_BUFSIZ + 1);
+
+ if (DTRACE_ENABLED(user_trace_s1)) {
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &message_bin) ||
+ message_bin.size > MESSAGE_BUFSIZ) {
+ return am_badarg;
+ }
+ memcpy(messagebuf, (char *) message_bin.data, message_bin.size);
+ messagebuf[message_bin.size] = '\0';
+ DTRACE1(user_trace_s1, messagebuf);
+ return am_true;
+ } else {
+ return am_false;
+ }
+}
+
+ERL_NIF_TERM erl_nif_user_trace_i4s4(ErlNifEnv* env, int argc,
+ const ERL_NIF_TERM argv[])
+{
+ DTRACE_CHARBUF(procbuf, 32 + 1);
+ DTRACE_CHARBUF(user_tagbuf, MESSAGE_BUFSIZ + 1);
+ char *utbuf = NULL;
+ ErlNifSInt64 i1, i2, i3, i4;
+ DTRACE_CHARBUF(messagebuf1, MESSAGE_BUFSIZ + 1);
+ DTRACE_CHARBUF(messagebuf2, MESSAGE_BUFSIZ + 1);
+ DTRACE_CHARBUF(messagebuf3, MESSAGE_BUFSIZ + 1);
+ DTRACE_CHARBUF(messagebuf4, MESSAGE_BUFSIZ + 1);
+ char *mbuf1 = NULL, *mbuf2 = NULL, *mbuf3 = NULL, *mbuf4 = NULL;
+
+ if (DTRACE_ENABLED(user_trace_i4s4)) {
+ dtrace_nifenv_str(env, procbuf);
+ get_string_maybe(env, argv[0], &utbuf, user_tagbuf, MESSAGE_BUFSIZ);
+ if (! enif_get_int64(env, argv[1], &i1))
+ i1 = 0;
+ if (! enif_get_int64(env, argv[2], &i2))
+ i2 = 0;
+ if (! enif_get_int64(env, argv[3], &i3))
+ i3 = 0;
+ if (! enif_get_int64(env, argv[4], &i4))
+ i4 = 0;
+ get_string_maybe(env, argv[5], &mbuf1, messagebuf1, MESSAGE_BUFSIZ);
+ get_string_maybe(env, argv[6], &mbuf2, messagebuf2, MESSAGE_BUFSIZ);
+ get_string_maybe(env, argv[7], &mbuf3, messagebuf3, MESSAGE_BUFSIZ);
+ get_string_maybe(env, argv[8], &mbuf4, messagebuf4, MESSAGE_BUFSIZ);
+ DTRACE10(user_trace_i4s4, procbuf, utbuf,
+ i1, i2, i3, i4, mbuf1, mbuf2, mbuf3, mbuf4);
+ return am_true;
+ } else {
+ return am_false;
+ }
+}
+
+#define DTRACE10_LABEL(name, label, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) \
+ erlang_##name##label((a0), (a1), (a2), (a3), (a4), (a5), (a6), (a7), (a8), (a9))
+#define N_STATEMENT(the_label) \
+ case the_label: \
+ if (DTRACE_ENABLED(user_trace_n##the_label)) { \
+ dtrace_nifenv_str(env, procbuf); \
+ get_string_maybe(env, argv[1], &utbuf, user_tagbuf, MESSAGE_BUFSIZ); \
+ if (! enif_get_int64(env, argv[2], &i1)) \
+ i1 = 0; \
+ if (! enif_get_int64(env, argv[3], &i2)) \
+ i2 = 0; \
+ if (! enif_get_int64(env, argv[4], &i3)) \
+ i3 = 0; \
+ if (! enif_get_int64(env, argv[5], &i4)) \
+ i4 = 0; \
+ get_string_maybe(env, argv[6], &mbuf1, messagebuf1, MESSAGE_BUFSIZ); \
+ get_string_maybe(env, argv[7], &mbuf2, messagebuf2, MESSAGE_BUFSIZ); \
+ get_string_maybe(env, argv[8], &mbuf3, messagebuf3, MESSAGE_BUFSIZ); \
+ get_string_maybe(env, argv[9], &mbuf4, messagebuf4, MESSAGE_BUFSIZ); \
+ DTRACE10_LABEL(user_trace_n, the_label, procbuf, utbuf, \
+ i1, i2, i3, i4, mbuf1, mbuf2, mbuf3, mbuf4); \
+ return am_true; \
+ } else { \
+ return am_false; \
+ } \
+ break
+
+ERL_NIF_TERM erl_nif_user_trace_n(ErlNifEnv* env, int argc,
+ const ERL_NIF_TERM argv[])
+{
+ DTRACE_CHARBUF(procbuf, 32 + 1);
+ DTRACE_CHARBUF(user_tagbuf, MESSAGE_BUFSIZ + 1);
+ char *utbuf = NULL;
+ ErlNifSInt64 i1, i2, i3, i4;
+ DTRACE_CHARBUF(messagebuf1, MESSAGE_BUFSIZ + 1);
+ DTRACE_CHARBUF(messagebuf2, MESSAGE_BUFSIZ + 1);
+ DTRACE_CHARBUF(messagebuf3, MESSAGE_BUFSIZ + 1);
+ DTRACE_CHARBUF(messagebuf4, MESSAGE_BUFSIZ + 1);
+ char *mbuf1 = NULL, *mbuf2 = NULL, *mbuf3 = NULL, *mbuf4 = NULL;
+ ErlNifSInt64 label = 0;
+
+ if (! enif_get_int64(env, argv[0], &label) || label < 0 || label > 1023) {
+ return am_badarg;
+ }
+ switch (label) {
+ N_STATEMENT(0);
+ N_STATEMENT(1);
+ N_STATEMENT(2);
+ N_STATEMENT(3);
+ N_STATEMENT(4);
+ N_STATEMENT(5);
+ N_STATEMENT(6);
+ N_STATEMENT(7);
+ N_STATEMENT(8);
+ N_STATEMENT(9);
+ N_STATEMENT(10);
+ N_STATEMENT(11);
+ N_STATEMENT(12);
+ N_STATEMENT(13);
+ N_STATEMENT(14);
+ N_STATEMENT(15);
+ N_STATEMENT(16);
+ N_STATEMENT(17);
+ N_STATEMENT(18);
+ N_STATEMENT(19);
+ N_STATEMENT(20);
+ N_STATEMENT(21);
+ N_STATEMENT(22);
+ N_STATEMENT(23);
+ N_STATEMENT(24);
+ N_STATEMENT(25);
+ N_STATEMENT(26);
+ N_STATEMENT(27);
+ N_STATEMENT(28);
+ N_STATEMENT(29);
+ N_STATEMENT(30);
+ N_STATEMENT(31);
+ N_STATEMENT(32);
+ N_STATEMENT(33);
+ N_STATEMENT(34);
+ N_STATEMENT(35);
+ N_STATEMENT(36);
+ N_STATEMENT(37);
+ N_STATEMENT(38);
+ N_STATEMENT(39);
+ N_STATEMENT(40);
+ N_STATEMENT(41);
+ N_STATEMENT(42);
+ N_STATEMENT(43);
+ N_STATEMENT(44);
+ N_STATEMENT(45);
+ N_STATEMENT(46);
+ N_STATEMENT(47);
+ N_STATEMENT(48);
+ N_STATEMENT(49);
+ N_STATEMENT(50);
+ N_STATEMENT(51);
+ N_STATEMENT(52);
+ N_STATEMENT(53);
+ N_STATEMENT(54);
+ N_STATEMENT(55);
+ N_STATEMENT(56);
+ N_STATEMENT(57);
+ N_STATEMENT(58);
+ N_STATEMENT(59);
+ N_STATEMENT(60);
+ N_STATEMENT(61);
+ N_STATEMENT(62);
+ N_STATEMENT(63);
+ N_STATEMENT(64);
+ N_STATEMENT(65);
+ N_STATEMENT(66);
+ N_STATEMENT(67);
+ N_STATEMENT(68);
+ N_STATEMENT(69);
+ N_STATEMENT(70);
+ N_STATEMENT(71);
+ N_STATEMENT(72);
+ N_STATEMENT(73);
+ N_STATEMENT(74);
+ N_STATEMENT(75);
+ N_STATEMENT(76);
+ N_STATEMENT(77);
+ N_STATEMENT(78);
+ N_STATEMENT(79);
+ N_STATEMENT(80);
+ N_STATEMENT(81);
+ N_STATEMENT(82);
+ N_STATEMENT(83);
+ N_STATEMENT(84);
+ N_STATEMENT(85);
+ N_STATEMENT(86);
+ N_STATEMENT(87);
+ N_STATEMENT(88);
+ N_STATEMENT(89);
+ N_STATEMENT(90);
+ N_STATEMENT(91);
+ N_STATEMENT(92);
+ N_STATEMENT(93);
+ N_STATEMENT(94);
+ N_STATEMENT(95);
+ N_STATEMENT(96);
+ N_STATEMENT(97);
+ N_STATEMENT(98);
+ N_STATEMENT(99);
+ N_STATEMENT(100);
+ N_STATEMENT(101);
+ N_STATEMENT(102);
+ N_STATEMENT(103);
+ N_STATEMENT(104);
+ N_STATEMENT(105);
+ N_STATEMENT(106);
+ N_STATEMENT(107);
+ N_STATEMENT(108);
+ N_STATEMENT(109);
+ N_STATEMENT(110);
+ N_STATEMENT(111);
+ N_STATEMENT(112);
+ N_STATEMENT(113);
+ N_STATEMENT(114);
+ N_STATEMENT(115);
+ N_STATEMENT(116);
+ N_STATEMENT(117);
+ N_STATEMENT(118);
+ N_STATEMENT(119);
+ N_STATEMENT(120);
+ N_STATEMENT(121);
+ N_STATEMENT(122);
+ N_STATEMENT(123);
+ N_STATEMENT(124);
+ N_STATEMENT(125);
+ N_STATEMENT(126);
+ N_STATEMENT(127);
+ N_STATEMENT(128);
+ N_STATEMENT(129);
+ N_STATEMENT(130);
+ N_STATEMENT(131);
+ N_STATEMENT(132);
+ N_STATEMENT(133);
+ N_STATEMENT(134);
+ N_STATEMENT(135);
+ N_STATEMENT(136);
+ N_STATEMENT(137);
+ N_STATEMENT(138);
+ N_STATEMENT(139);
+ N_STATEMENT(140);
+ N_STATEMENT(141);
+ N_STATEMENT(142);
+ N_STATEMENT(143);
+ N_STATEMENT(144);
+ N_STATEMENT(145);
+ N_STATEMENT(146);
+ N_STATEMENT(147);
+ N_STATEMENT(148);
+ N_STATEMENT(149);
+ N_STATEMENT(150);
+ N_STATEMENT(151);
+ N_STATEMENT(152);
+ N_STATEMENT(153);
+ N_STATEMENT(154);
+ N_STATEMENT(155);
+ N_STATEMENT(156);
+ N_STATEMENT(157);
+ N_STATEMENT(158);
+ N_STATEMENT(159);
+ N_STATEMENT(160);
+ N_STATEMENT(161);
+ N_STATEMENT(162);
+ N_STATEMENT(163);
+ N_STATEMENT(164);
+ N_STATEMENT(165);
+ N_STATEMENT(166);
+ N_STATEMENT(167);
+ N_STATEMENT(168);
+ N_STATEMENT(169);
+ N_STATEMENT(170);
+ N_STATEMENT(171);
+ N_STATEMENT(172);
+ N_STATEMENT(173);
+ N_STATEMENT(174);
+ N_STATEMENT(175);
+ N_STATEMENT(176);
+ N_STATEMENT(177);
+ N_STATEMENT(178);
+ N_STATEMENT(179);
+ N_STATEMENT(180);
+ N_STATEMENT(181);
+ N_STATEMENT(182);
+ N_STATEMENT(183);
+ N_STATEMENT(184);
+ N_STATEMENT(185);
+ N_STATEMENT(186);
+ N_STATEMENT(187);
+ N_STATEMENT(188);
+ N_STATEMENT(189);
+ N_STATEMENT(190);
+ N_STATEMENT(191);
+ N_STATEMENT(192);
+ N_STATEMENT(193);
+ N_STATEMENT(194);
+ N_STATEMENT(195);
+ N_STATEMENT(196);
+ N_STATEMENT(197);
+ N_STATEMENT(198);
+ N_STATEMENT(199);
+ N_STATEMENT(200);
+ N_STATEMENT(201);
+ N_STATEMENT(202);
+ N_STATEMENT(203);
+ N_STATEMENT(204);
+ N_STATEMENT(205);
+ N_STATEMENT(206);
+ N_STATEMENT(207);
+ N_STATEMENT(208);
+ N_STATEMENT(209);
+ N_STATEMENT(210);
+ N_STATEMENT(211);
+ N_STATEMENT(212);
+ N_STATEMENT(213);
+ N_STATEMENT(214);
+ N_STATEMENT(215);
+ N_STATEMENT(216);
+ N_STATEMENT(217);
+ N_STATEMENT(218);
+ N_STATEMENT(219);
+ N_STATEMENT(220);
+ N_STATEMENT(221);
+ N_STATEMENT(222);
+ N_STATEMENT(223);
+ N_STATEMENT(224);
+ N_STATEMENT(225);
+ N_STATEMENT(226);
+ N_STATEMENT(227);
+ N_STATEMENT(228);
+ N_STATEMENT(229);
+ N_STATEMENT(230);
+ N_STATEMENT(231);
+ N_STATEMENT(232);
+ N_STATEMENT(233);
+ N_STATEMENT(234);
+ N_STATEMENT(235);
+ N_STATEMENT(236);
+ N_STATEMENT(237);
+ N_STATEMENT(238);
+ N_STATEMENT(239);
+ N_STATEMENT(240);
+ N_STATEMENT(241);
+ N_STATEMENT(242);
+ N_STATEMENT(243);
+ N_STATEMENT(244);
+ N_STATEMENT(245);
+ N_STATEMENT(246);
+ N_STATEMENT(247);
+ N_STATEMENT(248);
+ N_STATEMENT(249);
+ N_STATEMENT(250);
+ N_STATEMENT(251);
+ N_STATEMENT(252);
+ N_STATEMENT(253);
+ N_STATEMENT(254);
+ N_STATEMENT(255);
+ N_STATEMENT(256);
+ N_STATEMENT(257);
+ N_STATEMENT(258);
+ N_STATEMENT(259);
+ N_STATEMENT(260);
+ N_STATEMENT(261);
+ N_STATEMENT(262);
+ N_STATEMENT(263);
+ N_STATEMENT(264);
+ N_STATEMENT(265);
+ N_STATEMENT(266);
+ N_STATEMENT(267);
+ N_STATEMENT(268);
+ N_STATEMENT(269);
+ N_STATEMENT(270);
+ N_STATEMENT(271);
+ N_STATEMENT(272);
+ N_STATEMENT(273);
+ N_STATEMENT(274);
+ N_STATEMENT(275);
+ N_STATEMENT(276);
+ N_STATEMENT(277);
+ N_STATEMENT(278);
+ N_STATEMENT(279);
+ N_STATEMENT(280);
+ N_STATEMENT(281);
+ N_STATEMENT(282);
+ N_STATEMENT(283);
+ N_STATEMENT(284);
+ N_STATEMENT(285);
+ N_STATEMENT(286);
+ N_STATEMENT(287);
+ N_STATEMENT(288);
+ N_STATEMENT(289);
+ N_STATEMENT(290);
+ N_STATEMENT(291);
+ N_STATEMENT(292);
+ N_STATEMENT(293);
+ N_STATEMENT(294);
+ N_STATEMENT(295);
+ N_STATEMENT(296);
+ N_STATEMENT(297);
+ N_STATEMENT(298);
+ N_STATEMENT(299);
+ N_STATEMENT(300);
+ N_STATEMENT(301);
+ N_STATEMENT(302);
+ N_STATEMENT(303);
+ N_STATEMENT(304);
+ N_STATEMENT(305);
+ N_STATEMENT(306);
+ N_STATEMENT(307);
+ N_STATEMENT(308);
+ N_STATEMENT(309);
+ N_STATEMENT(310);
+ N_STATEMENT(311);
+ N_STATEMENT(312);
+ N_STATEMENT(313);
+ N_STATEMENT(314);
+ N_STATEMENT(315);
+ N_STATEMENT(316);
+ N_STATEMENT(317);
+ N_STATEMENT(318);
+ N_STATEMENT(319);
+ N_STATEMENT(320);
+ N_STATEMENT(321);
+ N_STATEMENT(322);
+ N_STATEMENT(323);
+ N_STATEMENT(324);
+ N_STATEMENT(325);
+ N_STATEMENT(326);
+ N_STATEMENT(327);
+ N_STATEMENT(328);
+ N_STATEMENT(329);
+ N_STATEMENT(330);
+ N_STATEMENT(331);
+ N_STATEMENT(332);
+ N_STATEMENT(333);
+ N_STATEMENT(334);
+ N_STATEMENT(335);
+ N_STATEMENT(336);
+ N_STATEMENT(337);
+ N_STATEMENT(338);
+ N_STATEMENT(339);
+ N_STATEMENT(340);
+ N_STATEMENT(341);
+ N_STATEMENT(342);
+ N_STATEMENT(343);
+ N_STATEMENT(344);
+ N_STATEMENT(345);
+ N_STATEMENT(346);
+ N_STATEMENT(347);
+ N_STATEMENT(348);
+ N_STATEMENT(349);
+ N_STATEMENT(350);
+ N_STATEMENT(351);
+ N_STATEMENT(352);
+ N_STATEMENT(353);
+ N_STATEMENT(354);
+ N_STATEMENT(355);
+ N_STATEMENT(356);
+ N_STATEMENT(357);
+ N_STATEMENT(358);
+ N_STATEMENT(359);
+ N_STATEMENT(360);
+ N_STATEMENT(361);
+ N_STATEMENT(362);
+ N_STATEMENT(363);
+ N_STATEMENT(364);
+ N_STATEMENT(365);
+ N_STATEMENT(366);
+ N_STATEMENT(367);
+ N_STATEMENT(368);
+ N_STATEMENT(369);
+ N_STATEMENT(370);
+ N_STATEMENT(371);
+ N_STATEMENT(372);
+ N_STATEMENT(373);
+ N_STATEMENT(374);
+ N_STATEMENT(375);
+ N_STATEMENT(376);
+ N_STATEMENT(377);
+ N_STATEMENT(378);
+ N_STATEMENT(379);
+ N_STATEMENT(380);
+ N_STATEMENT(381);
+ N_STATEMENT(382);
+ N_STATEMENT(383);
+ N_STATEMENT(384);
+ N_STATEMENT(385);
+ N_STATEMENT(386);
+ N_STATEMENT(387);
+ N_STATEMENT(388);
+ N_STATEMENT(389);
+ N_STATEMENT(390);
+ N_STATEMENT(391);
+ N_STATEMENT(392);
+ N_STATEMENT(393);
+ N_STATEMENT(394);
+ N_STATEMENT(395);
+ N_STATEMENT(396);
+ N_STATEMENT(397);
+ N_STATEMENT(398);
+ N_STATEMENT(399);
+ N_STATEMENT(400);
+ N_STATEMENT(401);
+ N_STATEMENT(402);
+ N_STATEMENT(403);
+ N_STATEMENT(404);
+ N_STATEMENT(405);
+ N_STATEMENT(406);
+ N_STATEMENT(407);
+ N_STATEMENT(408);
+ N_STATEMENT(409);
+ N_STATEMENT(410);
+ N_STATEMENT(411);
+ N_STATEMENT(412);
+ N_STATEMENT(413);
+ N_STATEMENT(414);
+ N_STATEMENT(415);
+ N_STATEMENT(416);
+ N_STATEMENT(417);
+ N_STATEMENT(418);
+ N_STATEMENT(419);
+ N_STATEMENT(420);
+ N_STATEMENT(421);
+ N_STATEMENT(422);
+ N_STATEMENT(423);
+ N_STATEMENT(424);
+ N_STATEMENT(425);
+ N_STATEMENT(426);
+ N_STATEMENT(427);
+ N_STATEMENT(428);
+ N_STATEMENT(429);
+ N_STATEMENT(430);
+ N_STATEMENT(431);
+ N_STATEMENT(432);
+ N_STATEMENT(433);
+ N_STATEMENT(434);
+ N_STATEMENT(435);
+ N_STATEMENT(436);
+ N_STATEMENT(437);
+ N_STATEMENT(438);
+ N_STATEMENT(439);
+ N_STATEMENT(440);
+ N_STATEMENT(441);
+ N_STATEMENT(442);
+ N_STATEMENT(443);
+ N_STATEMENT(444);
+ N_STATEMENT(445);
+ N_STATEMENT(446);
+ N_STATEMENT(447);
+ N_STATEMENT(448);
+ N_STATEMENT(449);
+ N_STATEMENT(450);
+ N_STATEMENT(451);
+ N_STATEMENT(452);
+ N_STATEMENT(453);
+ N_STATEMENT(454);
+ N_STATEMENT(455);
+ N_STATEMENT(456);
+ N_STATEMENT(457);
+ N_STATEMENT(458);
+ N_STATEMENT(459);
+ N_STATEMENT(460);
+ N_STATEMENT(461);
+ N_STATEMENT(462);
+ N_STATEMENT(463);
+ N_STATEMENT(464);
+ N_STATEMENT(465);
+ N_STATEMENT(466);
+ N_STATEMENT(467);
+ N_STATEMENT(468);
+ N_STATEMENT(469);
+ N_STATEMENT(470);
+ N_STATEMENT(471);
+ N_STATEMENT(472);
+ N_STATEMENT(473);
+ N_STATEMENT(474);
+ N_STATEMENT(475);
+ N_STATEMENT(476);
+ N_STATEMENT(477);
+ N_STATEMENT(478);
+ N_STATEMENT(479);
+ N_STATEMENT(480);
+ N_STATEMENT(481);
+ N_STATEMENT(482);
+ N_STATEMENT(483);
+ N_STATEMENT(484);
+ N_STATEMENT(485);
+ N_STATEMENT(486);
+ N_STATEMENT(487);
+ N_STATEMENT(488);
+ N_STATEMENT(489);
+ N_STATEMENT(490);
+ N_STATEMENT(491);
+ N_STATEMENT(492);
+ N_STATEMENT(493);
+ N_STATEMENT(494);
+ N_STATEMENT(495);
+ N_STATEMENT(496);
+ N_STATEMENT(497);
+ N_STATEMENT(498);
+ N_STATEMENT(499);
+ N_STATEMENT(500);
+ N_STATEMENT(501);
+ N_STATEMENT(502);
+ N_STATEMENT(503);
+ N_STATEMENT(504);
+ N_STATEMENT(505);
+ N_STATEMENT(506);
+ N_STATEMENT(507);
+ N_STATEMENT(508);
+ N_STATEMENT(509);
+ N_STATEMENT(510);
+ N_STATEMENT(511);
+ N_STATEMENT(512);
+ N_STATEMENT(513);
+ N_STATEMENT(514);
+ N_STATEMENT(515);
+ N_STATEMENT(516);
+ N_STATEMENT(517);
+ N_STATEMENT(518);
+ N_STATEMENT(519);
+ N_STATEMENT(520);
+ N_STATEMENT(521);
+ N_STATEMENT(522);
+ N_STATEMENT(523);
+ N_STATEMENT(524);
+ N_STATEMENT(525);
+ N_STATEMENT(526);
+ N_STATEMENT(527);
+ N_STATEMENT(528);
+ N_STATEMENT(529);
+ N_STATEMENT(530);
+ N_STATEMENT(531);
+ N_STATEMENT(532);
+ N_STATEMENT(533);
+ N_STATEMENT(534);
+ N_STATEMENT(535);
+ N_STATEMENT(536);
+ N_STATEMENT(537);
+ N_STATEMENT(538);
+ N_STATEMENT(539);
+ N_STATEMENT(540);
+ N_STATEMENT(541);
+ N_STATEMENT(542);
+ N_STATEMENT(543);
+ N_STATEMENT(544);
+ N_STATEMENT(545);
+ N_STATEMENT(546);
+ N_STATEMENT(547);
+ N_STATEMENT(548);
+ N_STATEMENT(549);
+ N_STATEMENT(550);
+ N_STATEMENT(551);
+ N_STATEMENT(552);
+ N_STATEMENT(553);
+ N_STATEMENT(554);
+ N_STATEMENT(555);
+ N_STATEMENT(556);
+ N_STATEMENT(557);
+ N_STATEMENT(558);
+ N_STATEMENT(559);
+ N_STATEMENT(560);
+ N_STATEMENT(561);
+ N_STATEMENT(562);
+ N_STATEMENT(563);
+ N_STATEMENT(564);
+ N_STATEMENT(565);
+ N_STATEMENT(566);
+ N_STATEMENT(567);
+ N_STATEMENT(568);
+ N_STATEMENT(569);
+ N_STATEMENT(570);
+ N_STATEMENT(571);
+ N_STATEMENT(572);
+ N_STATEMENT(573);
+ N_STATEMENT(574);
+ N_STATEMENT(575);
+ N_STATEMENT(576);
+ N_STATEMENT(577);
+ N_STATEMENT(578);
+ N_STATEMENT(579);
+ N_STATEMENT(580);
+ N_STATEMENT(581);
+ N_STATEMENT(582);
+ N_STATEMENT(583);
+ N_STATEMENT(584);
+ N_STATEMENT(585);
+ N_STATEMENT(586);
+ N_STATEMENT(587);
+ N_STATEMENT(588);
+ N_STATEMENT(589);
+ N_STATEMENT(590);
+ N_STATEMENT(591);
+ N_STATEMENT(592);
+ N_STATEMENT(593);
+ N_STATEMENT(594);
+ N_STATEMENT(595);
+ N_STATEMENT(596);
+ N_STATEMENT(597);
+ N_STATEMENT(598);
+ N_STATEMENT(599);
+ N_STATEMENT(600);
+ N_STATEMENT(601);
+ N_STATEMENT(602);
+ N_STATEMENT(603);
+ N_STATEMENT(604);
+ N_STATEMENT(605);
+ N_STATEMENT(606);
+ N_STATEMENT(607);
+ N_STATEMENT(608);
+ N_STATEMENT(609);
+ N_STATEMENT(610);
+ N_STATEMENT(611);
+ N_STATEMENT(612);
+ N_STATEMENT(613);
+ N_STATEMENT(614);
+ N_STATEMENT(615);
+ N_STATEMENT(616);
+ N_STATEMENT(617);
+ N_STATEMENT(618);
+ N_STATEMENT(619);
+ N_STATEMENT(620);
+ N_STATEMENT(621);
+ N_STATEMENT(622);
+ N_STATEMENT(623);
+ N_STATEMENT(624);
+ N_STATEMENT(625);
+ N_STATEMENT(626);
+ N_STATEMENT(627);
+ N_STATEMENT(628);
+ N_STATEMENT(629);
+ N_STATEMENT(630);
+ N_STATEMENT(631);
+ N_STATEMENT(632);
+ N_STATEMENT(633);
+ N_STATEMENT(634);
+ N_STATEMENT(635);
+ N_STATEMENT(636);
+ N_STATEMENT(637);
+ N_STATEMENT(638);
+ N_STATEMENT(639);
+ N_STATEMENT(640);
+ N_STATEMENT(641);
+ N_STATEMENT(642);
+ N_STATEMENT(643);
+ N_STATEMENT(644);
+ N_STATEMENT(645);
+ N_STATEMENT(646);
+ N_STATEMENT(647);
+ N_STATEMENT(648);
+ N_STATEMENT(649);
+ N_STATEMENT(650);
+ N_STATEMENT(651);
+ N_STATEMENT(652);
+ N_STATEMENT(653);
+ N_STATEMENT(654);
+ N_STATEMENT(655);
+ N_STATEMENT(656);
+ N_STATEMENT(657);
+ N_STATEMENT(658);
+ N_STATEMENT(659);
+ N_STATEMENT(660);
+ N_STATEMENT(661);
+ N_STATEMENT(662);
+ N_STATEMENT(663);
+ N_STATEMENT(664);
+ N_STATEMENT(665);
+ N_STATEMENT(666);
+ N_STATEMENT(667);
+ N_STATEMENT(668);
+ N_STATEMENT(669);
+ N_STATEMENT(670);
+ N_STATEMENT(671);
+ N_STATEMENT(672);
+ N_STATEMENT(673);
+ N_STATEMENT(674);
+ N_STATEMENT(675);
+ N_STATEMENT(676);
+ N_STATEMENT(677);
+ N_STATEMENT(678);
+ N_STATEMENT(679);
+ N_STATEMENT(680);
+ N_STATEMENT(681);
+ N_STATEMENT(682);
+ N_STATEMENT(683);
+ N_STATEMENT(684);
+ N_STATEMENT(685);
+ N_STATEMENT(686);
+ N_STATEMENT(687);
+ N_STATEMENT(688);
+ N_STATEMENT(689);
+ N_STATEMENT(690);
+ N_STATEMENT(691);
+ N_STATEMENT(692);
+ N_STATEMENT(693);
+ N_STATEMENT(694);
+ N_STATEMENT(695);
+ N_STATEMENT(696);
+ N_STATEMENT(697);
+ N_STATEMENT(698);
+ N_STATEMENT(699);
+ N_STATEMENT(700);
+ N_STATEMENT(701);
+ N_STATEMENT(702);
+ N_STATEMENT(703);
+ N_STATEMENT(704);
+ N_STATEMENT(705);
+ N_STATEMENT(706);
+ N_STATEMENT(707);
+ N_STATEMENT(708);
+ N_STATEMENT(709);
+ N_STATEMENT(710);
+ N_STATEMENT(711);
+ N_STATEMENT(712);
+ N_STATEMENT(713);
+ N_STATEMENT(714);
+ N_STATEMENT(715);
+ N_STATEMENT(716);
+ N_STATEMENT(717);
+ N_STATEMENT(718);
+ N_STATEMENT(719);
+ N_STATEMENT(720);
+ N_STATEMENT(721);
+ N_STATEMENT(722);
+ N_STATEMENT(723);
+ N_STATEMENT(724);
+ N_STATEMENT(725);
+ N_STATEMENT(726);
+ N_STATEMENT(727);
+ N_STATEMENT(728);
+ N_STATEMENT(729);
+ N_STATEMENT(730);
+ N_STATEMENT(731);
+ N_STATEMENT(732);
+ N_STATEMENT(733);
+ N_STATEMENT(734);
+ N_STATEMENT(735);
+ N_STATEMENT(736);
+ N_STATEMENT(737);
+ N_STATEMENT(738);
+ N_STATEMENT(739);
+ N_STATEMENT(740);
+ N_STATEMENT(741);
+ N_STATEMENT(742);
+ N_STATEMENT(743);
+ N_STATEMENT(744);
+ N_STATEMENT(745);
+ N_STATEMENT(746);
+ N_STATEMENT(747);
+ N_STATEMENT(748);
+ N_STATEMENT(749);
+ N_STATEMENT(750);
+ N_STATEMENT(751);
+ N_STATEMENT(752);
+ N_STATEMENT(753);
+ N_STATEMENT(754);
+ N_STATEMENT(755);
+ N_STATEMENT(756);
+ N_STATEMENT(757);
+ N_STATEMENT(758);
+ N_STATEMENT(759);
+ N_STATEMENT(760);
+ N_STATEMENT(761);
+ N_STATEMENT(762);
+ N_STATEMENT(763);
+ N_STATEMENT(764);
+ N_STATEMENT(765);
+ N_STATEMENT(766);
+ N_STATEMENT(767);
+ N_STATEMENT(768);
+ N_STATEMENT(769);
+ N_STATEMENT(770);
+ N_STATEMENT(771);
+ N_STATEMENT(772);
+ N_STATEMENT(773);
+ N_STATEMENT(774);
+ N_STATEMENT(775);
+ N_STATEMENT(776);
+ N_STATEMENT(777);
+ N_STATEMENT(778);
+ N_STATEMENT(779);
+ N_STATEMENT(780);
+ N_STATEMENT(781);
+ N_STATEMENT(782);
+ N_STATEMENT(783);
+ N_STATEMENT(784);
+ N_STATEMENT(785);
+ N_STATEMENT(786);
+ N_STATEMENT(787);
+ N_STATEMENT(788);
+ N_STATEMENT(789);
+ N_STATEMENT(790);
+ N_STATEMENT(791);
+ N_STATEMENT(792);
+ N_STATEMENT(793);
+ N_STATEMENT(794);
+ N_STATEMENT(795);
+ N_STATEMENT(796);
+ N_STATEMENT(797);
+ N_STATEMENT(798);
+ N_STATEMENT(799);
+ N_STATEMENT(800);
+ N_STATEMENT(801);
+ N_STATEMENT(802);
+ N_STATEMENT(803);
+ N_STATEMENT(804);
+ N_STATEMENT(805);
+ N_STATEMENT(806);
+ N_STATEMENT(807);
+ N_STATEMENT(808);
+ N_STATEMENT(809);
+ N_STATEMENT(810);
+ N_STATEMENT(811);
+ N_STATEMENT(812);
+ N_STATEMENT(813);
+ N_STATEMENT(814);
+ N_STATEMENT(815);
+ N_STATEMENT(816);
+ N_STATEMENT(817);
+ N_STATEMENT(818);
+ N_STATEMENT(819);
+ N_STATEMENT(820);
+ N_STATEMENT(821);
+ N_STATEMENT(822);
+ N_STATEMENT(823);
+ N_STATEMENT(824);
+ N_STATEMENT(825);
+ N_STATEMENT(826);
+ N_STATEMENT(827);
+ N_STATEMENT(828);
+ N_STATEMENT(829);
+ N_STATEMENT(830);
+ N_STATEMENT(831);
+ N_STATEMENT(832);
+ N_STATEMENT(833);
+ N_STATEMENT(834);
+ N_STATEMENT(835);
+ N_STATEMENT(836);
+ N_STATEMENT(837);
+ N_STATEMENT(838);
+ N_STATEMENT(839);
+ N_STATEMENT(840);
+ N_STATEMENT(841);
+ N_STATEMENT(842);
+ N_STATEMENT(843);
+ N_STATEMENT(844);
+ N_STATEMENT(845);
+ N_STATEMENT(846);
+ N_STATEMENT(847);
+ N_STATEMENT(848);
+ N_STATEMENT(849);
+ N_STATEMENT(850);
+ N_STATEMENT(851);
+ N_STATEMENT(852);
+ N_STATEMENT(853);
+ N_STATEMENT(854);
+ N_STATEMENT(855);
+ N_STATEMENT(856);
+ N_STATEMENT(857);
+ N_STATEMENT(858);
+ N_STATEMENT(859);
+ N_STATEMENT(860);
+ N_STATEMENT(861);
+ N_STATEMENT(862);
+ N_STATEMENT(863);
+ N_STATEMENT(864);
+ N_STATEMENT(865);
+ N_STATEMENT(866);
+ N_STATEMENT(867);
+ N_STATEMENT(868);
+ N_STATEMENT(869);
+ N_STATEMENT(870);
+ N_STATEMENT(871);
+ N_STATEMENT(872);
+ N_STATEMENT(873);
+ N_STATEMENT(874);
+ N_STATEMENT(875);
+ N_STATEMENT(876);
+ N_STATEMENT(877);
+ N_STATEMENT(878);
+ N_STATEMENT(879);
+ N_STATEMENT(880);
+ N_STATEMENT(881);
+ N_STATEMENT(882);
+ N_STATEMENT(883);
+ N_STATEMENT(884);
+ N_STATEMENT(885);
+ N_STATEMENT(886);
+ N_STATEMENT(887);
+ N_STATEMENT(888);
+ N_STATEMENT(889);
+ N_STATEMENT(890);
+ N_STATEMENT(891);
+ N_STATEMENT(892);
+ N_STATEMENT(893);
+ N_STATEMENT(894);
+ N_STATEMENT(895);
+ N_STATEMENT(896);
+ N_STATEMENT(897);
+ N_STATEMENT(898);
+ N_STATEMENT(899);
+ N_STATEMENT(900);
+ N_STATEMENT(901);
+ N_STATEMENT(902);
+ N_STATEMENT(903);
+ N_STATEMENT(904);
+ N_STATEMENT(905);
+ N_STATEMENT(906);
+ N_STATEMENT(907);
+ N_STATEMENT(908);
+ N_STATEMENT(909);
+ N_STATEMENT(910);
+ N_STATEMENT(911);
+ N_STATEMENT(912);
+ N_STATEMENT(913);
+ N_STATEMENT(914);
+ N_STATEMENT(915);
+ N_STATEMENT(916);
+ N_STATEMENT(917);
+ N_STATEMENT(918);
+ N_STATEMENT(919);
+ N_STATEMENT(920);
+ N_STATEMENT(921);
+ N_STATEMENT(922);
+ N_STATEMENT(923);
+ N_STATEMENT(924);
+ N_STATEMENT(925);
+ N_STATEMENT(926);
+ N_STATEMENT(927);
+ N_STATEMENT(928);
+ N_STATEMENT(929);
+ N_STATEMENT(930);
+ N_STATEMENT(931);
+ N_STATEMENT(932);
+ N_STATEMENT(933);
+ N_STATEMENT(934);
+ N_STATEMENT(935);
+ N_STATEMENT(936);
+ N_STATEMENT(937);
+ N_STATEMENT(938);
+ N_STATEMENT(939);
+ N_STATEMENT(940);
+ N_STATEMENT(941);
+ N_STATEMENT(942);
+ N_STATEMENT(943);
+ N_STATEMENT(944);
+ N_STATEMENT(945);
+ N_STATEMENT(946);
+ N_STATEMENT(947);
+ N_STATEMENT(948);
+ N_STATEMENT(949);
+ N_STATEMENT(950);
+ }
+ return am_error; /* NOTREACHED, shut up the compiler */
+}
+
+#endif /* HAVE_USE_DTRACE */
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index e5d99dc4f1..93e56332e1 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2012. 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
@@ -187,11 +187,7 @@ extern TWinDynNifCallbacks WinDynNifCallbacks;
#else
# define ERL_NIF_INIT_GLOB
# define ERL_NIF_INIT_BODY
-# if defined(VXWORKS)
-# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* MODNAME ## _init(void)
-# else
-# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* nif_init(void)
-# endif
+# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* nif_init(void)
#endif
@@ -226,6 +222,15 @@ ERL_NIF_INIT_DECL(NAME) \
} \
ERL_NIF_INIT_EPILOGUE
+#if defined(USE_DYNAMIC_TRACE) && (defined(USE_DTRACE) || defined(USE_SYSTEMTAP))
+#define HAVE_USE_DTRACE 1
+#endif
+
+#ifdef HAVE_USE_DTRACE
+ERL_NIF_TERM erl_nif_user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM erl_nif_user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM erl_nif_user_trace_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+#endif
#endif /* __ERL_NIF_H__ */
diff --git a/erts/emulator/beam/erl_node_container_utils.h b/erts/emulator/beam/erl_node_container_utils.h
index 329a2204cc..7b4cb7b042 100644
--- a/erts/emulator/beam/erl_node_container_utils.h
+++ b/erts/emulator/beam/erl_node_container_utils.h
@@ -128,8 +128,47 @@ extern int erts_use_r9_pids_ports;
* Pids *
\* */
-#define internal_pid_index(x) (internal_pid_data((x)) \
- & erts_process_tab_index_mask)
+#define erts_max_processes erts_proc.max
+
+typedef struct {
+ erts_smp_atomic_t *tab;
+ int max;
+ int tab_cache_lines;
+ int pix_per_cache_line;
+ int pix_cl_mask;
+ int pix_cl_shift;
+ int pix_cli_mask;
+ int pix_cli_shift;
+} ErtsProcTab;
+
+extern ErtsProcTab erts_proc;
+
+ERTS_GLB_INLINE int erts_pid_data2ix(Eterm pid_data);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE int erts_pid_data2ix(Eterm pid_data)
+{
+ int n, pix;
+
+ n = (int) pid_data;
+ if (erts_proc.pix_cl_mask) {
+ pix = ((n & erts_proc.pix_cl_mask) << erts_proc.pix_cl_shift);
+ pix += ((n >> erts_proc.pix_cli_shift) & erts_proc.pix_cli_mask);
+ }
+ else {
+ n %= erts_proc.max;
+ pix = n % erts_proc.tab_cache_lines;
+ pix *= erts_proc.pix_per_cache_line;
+ pix += n / erts_proc.tab_cache_lines;
+ }
+ ASSERT(0 <= pix && pix < erts_proc.max);
+ return pix;
+}
+
+#endif
+
+#define internal_pid_index(x) erts_pid_data2ix(internal_pid_data((x)))
#define internal_pid_node_name(x) (internal_pid_node((x))->sysname)
#define external_pid_node_name(x) (external_pid_node((x))->sysname)
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index c7fd379367..40837d3817 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-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2012. 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
@@ -1298,21 +1298,22 @@ setup_reference_table(void)
UnUseTmpHeapNoproc(3);
/* Insert all processes */
- for (i = 0; i < erts_max_processes; i++)
- if (process_tab[i]) {
+ for (i = 0; i < erts_max_processes; i++) {
+ Process *proc = erts_pix2proc(i);
+ if (proc) {
ErlMessage *msg;
/* Insert Heap */
- insert_offheap(&(process_tab[i]->off_heap),
+ insert_offheap(&(proc->off_heap),
HEAP_REF,
- process_tab[i]->id);
+ proc->id);
/* Insert message buffers */
- for(hfp = process_tab[i]->mbuf; hfp; hfp = hfp->next)
+ for(hfp = proc->mbuf; hfp; hfp = hfp->next)
insert_offheap(&(hfp->off_heap),
HEAP_REF,
- process_tab[i]->id);
+ proc->id);
/* Insert msg msg buffers */
- for (msg = process_tab[i]->msg.first; msg; msg = msg->next) {
+ for (msg = proc->msg.first; msg; msg = msg->next) {
ErlHeapFragment *heap_frag = NULL;
if (msg->data.attached) {
if (is_value(ERL_MESSAGE_TERM(msg)))
@@ -1320,7 +1321,7 @@ setup_reference_table(void)
else {
if (msg->data.dist_ext->dep)
insert_dist_entry(msg->data.dist_ext->dep,
- HEAP_REF, process_tab[i]->id, 0);
+ HEAP_REF, proc->id, 0);
if (is_not_nil(ERL_MESSAGE_TOKEN(msg)))
heap_frag = erts_dist_ext_trailer(msg->data.dist_ext);
}
@@ -1328,10 +1329,10 @@ setup_reference_table(void)
if (heap_frag)
insert_offheap(&(heap_frag->off_heap),
HEAP_REF,
- process_tab[i]->id);
+ proc->id);
}
#ifdef ERTS_SMP
- for (msg = process_tab[i]->msg_inq.first; msg; msg = msg->next) {
+ for (msg = proc->u.alive.msg_inq.first; msg; msg = msg->next) {
ErlHeapFragment *heap_frag = NULL;
if (msg->data.attached) {
if (is_value(ERL_MESSAGE_TERM(msg)))
@@ -1339,7 +1340,7 @@ setup_reference_table(void)
else {
if (msg->data.dist_ext->dep)
insert_dist_entry(msg->data.dist_ext->dep,
- HEAP_REF, process_tab[i]->id, 0);
+ HEAP_REF, proc->id, 0);
if (is_not_nil(ERL_MESSAGE_TOKEN(msg)))
heap_frag = erts_dist_ext_trailer(msg->data.dist_ext);
}
@@ -1347,21 +1348,22 @@ setup_reference_table(void)
if (heap_frag)
insert_offheap(&(heap_frag->off_heap),
HEAP_REF,
- process_tab[i]->id);
+ proc->id);
}
#endif
/* Insert links */
- if(process_tab[i]->nlinks)
- insert_links(process_tab[i]->nlinks, process_tab[i]->id);
- if(process_tab[i]->monitors)
- insert_monitors(process_tab[i]->monitors, process_tab[i]->id);
+ if(proc->nlinks)
+ insert_links(proc->nlinks, proc->id);
+ if(proc->monitors)
+ insert_monitors(proc->monitors, proc->id);
/* Insert controller */
{
- DistEntry *dep = ERTS_PROC_GET_DIST_ENTRY(process_tab[i]);
+ DistEntry *dep = ERTS_PROC_GET_DIST_ENTRY(proc);
if (dep)
- insert_dist_entry(dep, CTRL_REF, process_tab[i]->id, 0);
+ insert_dist_entry(dep, CTRL_REF, proc->id, 0);
}
}
+ }
#ifdef ERTS_SMP
erts_foreach_sys_msg_in_q(insert_sys_msg);
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index 0f1a0d441a..86454fe1fa 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -56,12 +56,6 @@
#define ERTS_PORT_IS_IN_RUNQ(RQ, P) \
((P)->sched.next || (P)->sched.prev || (RQ)->ports.start == (P))
-#define ERTS_PORT_NOT_IN_RUNQ(P) \
-do { \
- (P)->sched.prev = NULL; \
- (P)->sched.next = NULL; \
-} while (0)
-
#ifdef USE_VM_PROBES
#define DTRACE_DRIVER(PROBE_NAME, PP) \
if (DTRACE_ENABLED(driver_ready_input)) { \
@@ -167,7 +161,7 @@ enqueue_port(ErtsRunQueue *runq, Port *pp)
{
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
pp->sched.next = NULL;
- pp->sched.prev = runq->ports.end;
+ pp->sched.in_runq = 1;
if (runq->ports.end) {
ASSERT(runq->ports.start);
runq->ports.end->sched.next = pp;
@@ -177,39 +171,10 @@ enqueue_port(ErtsRunQueue *runq, Port *pp)
runq->ports.start = pp;
}
- runq->ports.info.len++;
- if (runq->ports.info.max_len < runq->ports.info.len)
- runq->ports.info.max_len = runq->ports.info.len;
- runq->len++;
- if (runq->max_len < runq->len)
- runq->max_len = runq->len;
runq->ports.end = pp;
ASSERT(runq->ports.start && runq->ports.end);
-}
-static ERTS_INLINE void
-dequeue_port(ErtsRunQueue *runq, Port *pp)
-{
- ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
- if (pp->sched.next)
- pp->sched.next->sched.prev = pp->sched.prev;
- else {
- ASSERT(runq->ports.end == pp);
- runq->ports.end = pp->sched.prev;
- }
- if (pp->sched.prev)
- pp->sched.prev->sched.next = pp->sched.next;
- else {
- ASSERT(runq->ports.start == pp);
- runq->ports.start = pp->sched.next;
- }
-
- ASSERT(runq->ports.info.len > 0);
- runq->ports.info.len--;
- ASSERT(runq->len > 0);
- runq->len--;
- ASSERT(runq->ports.start || !runq->ports.end);
- ASSERT(runq->ports.end || !runq->ports.start);
+ erts_smp_inc_runq_len(runq, &runq->ports.info, ERTS_PORT_PRIO_LEVEL);
}
static ERTS_INLINE Port *
@@ -222,16 +187,11 @@ pop_port(ErtsRunQueue *runq)
}
else {
runq->ports.start = runq->ports.start->sched.next;
- if (runq->ports.start)
- runq->ports.start->sched.prev = NULL;
- else {
+ if (!runq->ports.start) {
ASSERT(runq->ports.end == pp);
runq->ports.end = NULL;
}
- ASSERT(runq->ports.info.len > 0);
- runq->ports.info.len--;
- ASSERT(runq->len > 0);
- runq->len--;
+ erts_smp_dec_runq_len(runq, &runq->ports.info, ERTS_PORT_PRIO_LEVEL);
}
ASSERT(runq->ports.start || !runq->ports.end);
@@ -285,7 +245,7 @@ check_port_queue(ErtsRunQueue *runq, Port *chk_pp, int inq)
}
ASSERT(no_forward == no_backward);
}
- ASSERT(no_forward == runq->ports.info.len);
+ ASSERT(no_forward == RUNQ_READ_LEN(&runq->ports.info.len));
if (chk_pp) {
if (chk_pp->sched.taskq || chk_pp->sched.exe_taskq) {
ASSERT(chk_pp->sched.taskq != chk_pp->sched.exe_taskq);
@@ -467,10 +427,11 @@ erts_port_task_abort(Eterm id, ErtsPortTaskHandle *pthp)
ErtsPortTaskQueue *ptqp;
ErtsPortTask *ptp;
Port *pp;
- int port_is_dequeued = 0;
pp = &erts_port[internal_port_index(id)];
runq = erts_port_runq(pp);
+ if (!runq)
+ return 1;
ptp = handle2task(pthp);
@@ -505,22 +466,12 @@ erts_port_task_abort(Eterm id, ErtsPortTaskHandle *pthp)
if (ptqp->first || pp->sched.taskq != ptqp)
ptqp = NULL;
- else {
+ else
pp->sched.taskq = NULL;
- if (!pp->sched.exe_taskq) {
- dequeue_port(runq, pp);
- ERTS_PORT_NOT_IN_RUNQ(pp);
- port_is_dequeued = 1;
- }
- }
ERTS_PT_CHK_PRES_PORTQ(runq, pp);
erts_smp_runq_unlock(runq);
-
- if (erts_system_profile_flags.runnable_ports && port_is_dequeued) {
- profile_runnable_port(pp, am_inactive);
- }
port_task_free(ptp);
if (ptqp)
@@ -575,7 +526,7 @@ erts_port_task_schedule(Eterm id,
if (!pp->sched.taskq) {
pp->sched.taskq = port_taskq_init(port_taskq_alloc(), pp);
- enq_port = !pp->sched.exe_taskq;
+ enq_port = !pp->sched.in_runq && !pp->sched.exe_taskq;
}
#ifdef ERTS_SMP
@@ -585,13 +536,13 @@ erts_port_task_schedule(Eterm id,
/* Port emigrated ... */
erts_smp_atomic_set_nob(&pp->run_queue, (erts_aint_t) xrunq);
erts_smp_runq_unlock(runq);
- runq = xrunq;
+ runq = erts_port_runq(pp);
+ if (!runq)
+ return -1;
}
}
#endif
- ASSERT(!enq_port || !(runq->flags & ERTS_RUNQ_FLG_SUSPENDED));
-
ASSERT(pp->sched.taskq);
ASSERT(ptp);
@@ -633,7 +584,7 @@ erts_port_task_schedule(Eterm id,
#elif defined(DEBUG)
if (!enq_port && !pp->sched.exe_taskq) {
/* We should be in port run q */
- ASSERT(pp->sched.prev || runq->ports.start == pp);
+ ASSERT(pp->sched.in_runq);
}
#endif
#endif
@@ -661,63 +612,54 @@ void
erts_port_task_free_port(Port *pp)
{
ErtsRunQueue *runq;
- int port_is_dequeued = 0;
+ ErtsPortTaskQueue *ptqp;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp));
ASSERT(!(pp->status & ERTS_PORT_SFLGS_DEAD));
runq = erts_port_runq(pp);
ASSERT(runq);
ERTS_PT_CHK_PRES_PORTQ(runq, pp);
- if (pp->sched.exe_taskq) {
+ ptqp = pp->sched.exe_taskq;
+ if (ptqp) {
/* I (this thread) am currently executing this port, free it
when scheduled out... */
- ErtsPortTask *ptp = port_task_alloc();
+ ErtsPortTask *ptp;
+ enqueue_free:
+ ptp = port_task_alloc();
erts_smp_port_state_lock(pp);
pp->status &= ~ERTS_PORT_SFLG_CLOSING;
pp->status |= ERTS_PORT_SFLG_FREE_SCHEDULED;
erts_may_save_closed_port(pp);
erts_smp_port_state_unlock(pp);
- ERTS_SMP_LC_ASSERT(erts_smp_atomic_read_nob(&pp->refc) > 1);
+ ERTS_LC_ASSERT(erts_smp_atomic_read_nob(&pp->refc) > 1);
ptp->type = ERTS_PORT_TASK_FREE;
ptp->event = (ErlDrvEvent) -1;
ptp->event_data = NULL;
set_handle(ptp, NULL);
- push_task(pp->sched.exe_taskq, ptp);
+ push_task(ptqp, ptp);
ERTS_PT_CHK_PRES_PORTQ(runq, pp);
erts_smp_runq_unlock(runq);
}
else {
- ErtsPortTaskQueue *ptqp = pp->sched.taskq;
- if (ptqp) {
- dequeue_port(runq, pp);
- ERTS_PORT_NOT_IN_RUNQ(pp);
- port_is_dequeued = 1;
+ if (pp->sched.in_runq) {
+ ptqp = pp->sched.taskq;
+ if (!ptqp)
+ pp->sched.taskq = ptqp = port_taskq_init(port_taskq_alloc(), pp);
+ goto enqueue_free;
}
+ ASSERT(!pp->sched.taskq);
erts_smp_port_state_lock(pp);
pp->status &= ~ERTS_PORT_SFLG_CLOSING;
pp->status |= ERTS_PORT_SFLG_FREE_SCHEDULED;
erts_may_save_closed_port(pp);
erts_smp_port_state_unlock(pp);
-#ifdef ERTS_SMP
erts_smp_atomic_dec_nob(&pp->refc); /* Not alive */
-#endif
- ERTS_SMP_LC_ASSERT(erts_smp_atomic_read_nob(&pp->refc) > 0); /* Lock */
+ ERTS_LC_ASSERT(erts_smp_atomic_read_nob(&pp->refc) > 0); /* Lock */
handle_remaining_tasks(runq, pp); /* May release runq lock */
ASSERT(!pp->sched.exe_taskq && (!ptqp || !ptqp->first));
pp->sched.taskq = NULL;
ERTS_PT_CHK_PRES_PORTQ(runq, pp);
-#ifndef ERTS_SMP
- ASSERT(pp->status & ERTS_PORT_SFLG_PORT_DEBUG);
- erts_port_status_set(pp, ERTS_PORT_SFLG_FREE);
-#endif
erts_smp_runq_unlock(runq);
-
- if (erts_system_profile_flags.runnable_ports && port_is_dequeued) {
- profile_runnable_port(pp, am_inactive);
- }
-
- if (ptqp)
- port_taskq_free(ptqp);
}
}
@@ -738,7 +680,6 @@ typedef struct {
int
erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
{
- int port_was_enqueued = 0;
Port *pp;
ErtsPortTaskQueue *ptqp;
ErtsPortTask *ptp;
@@ -757,11 +698,18 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
goto done;
}
- ERTS_PORT_NOT_IN_RUNQ(pp);
+ ASSERT(pp->sched.in_runq);
+ pp->sched.in_runq = 0;
+ if (!pp->sched.taskq) {
+ if (erts_system_profile_flags.runnable_ports)
+ profile_runnable_port(pp, am_inactive);
+ res = (erts_smp_atomic_read_nob(&erts_port_task_outstanding_io_tasks)
+ != (erts_aint_t) 0);
+ goto done;
+ }
*curr_port_pp = pp;
- ASSERT(pp->sched.taskq);
ASSERT(pp->sched.taskq->first);
ptqp = pp->sched.taskq;
pp->sched.taskq = NULL;
@@ -823,12 +771,8 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
handle_remaining_tasks(runq, pp);
ASSERT(!ptqp->first
&& (!pp->sched.taskq || !pp->sched.taskq->first));
-#ifdef ERTS_SMP
erts_smp_atomic_dec_nob(&pp->refc); /* Not alive */
- ERTS_SMP_LC_ASSERT(erts_smp_atomic_read_nob(&pp->refc) > 0); /* Lock */
-#else
- erts_port_status_bor_set(pp, ERTS_PORT_SFLG_FREE);
-#endif
+ ERTS_LC_ASSERT(erts_smp_atomic_read_nob(&pp->refc) > 0); /* Lock */
port_task_free(ptp);
if (pp->sched.taskq)
@@ -918,6 +862,8 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
if (!pp->sched.taskq) {
ASSERT(pp->sched.exe_taskq);
pp->sched.exe_taskq = NULL;
+ if (erts_system_profile_flags.runnable_ports)
+ profile_runnable_port(pp, am_inactive);
}
else {
#ifdef ERTS_SMP
@@ -940,14 +886,20 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
else {
/* Port emigrated ... */
erts_smp_atomic_set_nob(&pp->run_queue, (erts_aint_t) xrunq);
- enqueue_port(xrunq, pp);
- ASSERT(pp->sched.exe_taskq);
- pp->sched.exe_taskq = NULL;
- erts_smp_runq_unlock(xrunq);
- erts_smp_notify_inc_runq(xrunq);
+ erts_smp_runq_unlock(runq);
+
+ xrunq = erts_port_runq(pp);
+ if (xrunq) {
+ enqueue_port(xrunq, pp);
+ ASSERT(pp->sched.exe_taskq);
+ pp->sched.exe_taskq = NULL;
+ erts_smp_runq_unlock(xrunq);
+ erts_smp_notify_inc_runq(xrunq);
+ }
+
+ erts_smp_runq_lock(runq);
}
#endif
- port_was_enqueued = 1;
}
res = (erts_smp_atomic_read_nob(&erts_port_task_outstanding_io_tasks)
@@ -957,10 +909,6 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
port_taskq_free(ptqp);
- if (erts_system_profile_flags.runnable_ports && (port_was_enqueued != 1)) {
- profile_runnable_port(pp, am_inactive);
- }
-
/* trace port scheduling, out */
if (IS_TRACED_FL(pp, F_TRACE_SCHED_PORTS)) {
trace_sched_ports(pp, am_out);
@@ -1049,63 +997,33 @@ erts_port_is_scheduled(Port *pp)
{
int res;
ErtsRunQueue *runq = erts_port_runq(pp);
+ if (!runq)
+ return 0;
res = pp->sched.taskq || pp->sched.exe_taskq;
erts_smp_runq_unlock(runq);
return res;
}
#ifdef ERTS_SMP
+void
+erts_enqueue_port(ErtsRunQueue *rq, Port *pp)
+{
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
+ ASSERT(rq == (ErtsRunQueue *) erts_smp_atomic_read_nob(&pp->run_queue));
+ ASSERT(pp->sched.in_runq);
+ enqueue_port(rq, pp);
+}
-ErtsMigrateResult
-erts_port_migrate(Port *prt, int *prt_locked,
- ErtsRunQueue *from_rq, int *from_locked,
- ErtsRunQueue *to_rq, int *to_locked)
+Port *
+erts_dequeue_port(ErtsRunQueue *rq)
{
- ERTS_SMP_LC_ASSERT(*from_locked);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(from_rq, *from_locked);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(to_rq, *to_locked);
-
- if (!*from_locked || !*to_locked) {
- if (from_rq < to_rq) {
- if (!*to_locked) {
- if (!*from_locked)
- erts_smp_runq_lock(from_rq);
- erts_smp_runq_lock(to_rq);
- }
- else if (erts_smp_runq_trylock(from_rq) == EBUSY) {
- erts_smp_runq_unlock(to_rq);
- erts_smp_runq_lock(from_rq);
- erts_smp_runq_lock(to_rq);
- }
- }
- else {
- if (!*from_locked) {
- if (!*to_locked)
- erts_smp_runq_lock(to_rq);
- erts_smp_runq_lock(from_rq);
- }
- else if (erts_smp_runq_trylock(to_rq) == EBUSY) {
- erts_smp_runq_unlock(from_rq);
- erts_smp_runq_lock(to_rq);
- erts_smp_runq_lock(from_rq);
- }
- }
- *to_locked = *from_locked = 1;
- }
- ERTS_SMP_LC_CHK_RUNQ_LOCK(from_rq, *from_locked);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(to_rq, *to_locked);
-
- /* Refuse to migrate to a suspended run queue */
- if (to_rq->flags & ERTS_RUNQ_FLG_SUSPENDED)
- return ERTS_MIGRATE_FAILED_RUNQ_SUSPENDED;
- if (from_rq != (ErtsRunQueue *) erts_smp_atomic_read_nob(&prt->run_queue))
- return ERTS_MIGRATE_FAILED_RUNQ_CHANGED;
- if (!ERTS_PORT_IS_IN_RUNQ(from_rq, prt))
- return ERTS_MIGRATE_FAILED_NOT_IN_RUNQ;
- dequeue_port(from_rq, prt);
- erts_smp_atomic_set_nob(&prt->run_queue, (erts_aint_t) to_rq);
- enqueue_port(to_rq, prt);
- return ERTS_MIGRATE_SUCCESS;
+ Port *pp;
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
+ pp = pop_port(rq);
+ ASSERT(!pp
+ || rq == (ErtsRunQueue *) erts_smp_atomic_read_nob(&pp->run_queue));
+ ASSERT(!pp || pp->sched.in_runq);
+ return pp;
}
#endif
diff --git a/erts/emulator/beam/erl_port_task.h b/erts/emulator/beam/erl_port_task.h
index d7104e1143..fd88b1c1ff 100644
--- a/erts/emulator/beam/erl_port_task.h
+++ b/erts/emulator/beam/erl_port_task.h
@@ -62,7 +62,7 @@ typedef struct ErtsPortTaskQueue_ ErtsPortTaskQueue;
typedef struct {
Port *next;
- Port *prev;
+ int in_runq;
ErtsPortTaskQueue *taskq;
ErtsPortTaskQueue *exe_taskq;
} ErtsPortTaskSched;
@@ -92,7 +92,7 @@ ERTS_GLB_INLINE void
erts_port_task_init_sched(ErtsPortTaskSched *ptsp)
{
ptsp->next = NULL;
- ptsp->prev = NULL;
+ ptsp->in_runq = 0;
ptsp->taskq = NULL;
ptsp->exe_taskq = NULL;
}
@@ -123,13 +123,10 @@ int erts_port_task_schedule(Eterm,
ErlDrvEventData);
void erts_port_task_free_port(Port *);
int erts_port_is_scheduled(Port *);
+
#ifdef ERTS_SMP
-ErtsMigrateResult erts_port_migrate(Port *,
- int *,
- ErtsRunQueue *,
- int *,
- ErtsRunQueue *,
- int *);
+void erts_enqueue_port(ErtsRunQueue *rq, Port *pp);
+Port *erts_dequeue_port(ErtsRunQueue *rq);
#endif
#undef ERTS_INCLUDE_SCHEDULER_INTERNALS
#endif /* ERL_PORT_TASK_H__ */
diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c
index 34da9cab84..2320b64295 100644
--- a/erts/emulator/beam/erl_printf_term.c
+++ b/erts/emulator/beam/erl_printf_term.c
@@ -437,7 +437,10 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
}
break;
case BINARY_DEF:
- {
+ if (header_is_bin_matchstate(*boxed_val(wobj))) {
+ PRINT_STRING(res, fn, arg, "#MatchState");
+ }
+ else {
ProcBin* pb = (ProcBin *) binary_val(wobj);
if (pb->size == 1)
PRINT_STRING(res, fn, arg, "<<1 byte>>");
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index d2fa111b80..ddc43e621d 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -94,34 +94,194 @@
#define HIGH_BIT (1 << PRIORITY_HIGH)
#define NORMAL_BIT (1 << PRIORITY_NORMAL)
#define LOW_BIT (1 << PRIORITY_LOW)
+#define PORT_BIT (1 << ERTS_PORT_PRIO_LEVEL)
-#define ERTS_MAYBE_SAVE_TERMINATING_PROCESS(P) \
-do { \
- ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&proc_tab_mtx)); \
- if (saved_term_procs.end) \
- save_terminating_process((P)); \
-} while (0)
+#define ERTS_EMPTY_RUNQ(RQ) \
+ ((ERTS_RUNQ_FLGS_GET_NOB((RQ)) & ERTS_RUNQ_FLGS_QMASK) == 0 \
+ && (RQ)->misc.start == NULL)
-#define ERTS_EMPTY_RUNQ(RQ) \
- ((RQ)->len == 0 && (RQ)->misc.start == NULL)
+#undef RUNQ_READ_RQ
+#undef RUNQ_SET_RQ
+#define RUNQ_READ_RQ(X) ((ErtsRunQueue *) erts_smp_atomic_read_nob((X)))
+#define RUNQ_SET_RQ(X, RQ) erts_smp_atomic_set_nob((X), (erts_aint_t) (RQ))
+
+#ifdef DEBUG
+# if defined(ARCH_64) && !HALFWORD_HEAP
+# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N) \
+ (RUNQ_SET_RQ((RQP), (0xdeadbeefdead0003LL | ((N) << 4)))
+# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP) \
+do { \
+ ASSERT((RQP) != NULL); \
+ ASSERT(((((Uint) (RQP)) & ((Uint) 0x3))) == ((Uint) 0)); \
+ ASSERT((((Uint) (RQP)) & ~((Uint) 0xffff)) != ((Uint) 0xdeadbeefdead0000LL));\
+} while (0)
+# else
+# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N) \
+ (RUNQ_SET_RQ((RQP), (0xdead0003 | ((N) << 4))))
+# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP) \
+do { \
+ ASSERT((RQP) != NULL); \
+ ASSERT(((((UWord) (RQP)) & ((UWord) 1))) == ((UWord) 0)); \
+ ASSERT((((UWord) (RQP)) & ~((UWord) 0xffff)) != ((UWord) 0xdead0000)); \
+} while (0)
+# endif
+#else
+# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N)
+# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP)
+#endif
#define ERTS_EMPTY_RUNQ_PORTS(RQ) \
- ((RQ)->ports.info.len == 0 && (RQ)->misc.start == NULL)
+ (RUNQ_READ_LEN(&(RQ)->ports.info.len) == 0 && (RQ)->misc.start == NULL)
extern BeamInstr beam_apply[];
extern BeamInstr beam_exit[];
extern BeamInstr beam_continue_exit[];
-static Sint p_last;
-static Sint p_next;
-static Sint p_serial;
-static Uint p_serial_mask;
-static Uint p_serial_shift;
+#ifdef ARCH_32
+
+union {
+ erts_smp_dw_atomic_t pid_data;
+ char align[ERTS_CACHE_LINE_SIZE];
+} last erts_align_attribute(ERTS_CACHE_LINE_SIZE);
+
+
+static ERTS_INLINE Uint64
+dw_aint_to_uint64(erts_dw_aint_t *dw)
+{
+#ifdef ETHR_SU_DW_NAINT_T__
+ return (Uint64) dw->dw_sint;
+#else
+ Uint64 res;
+ res = (Uint64) ((Uint32) dw->sint[ERTS_DW_AINT_HIGH_WORD]);
+ res <<= 32;
+ res |= (Uint64) ((Uint32) dw->sint[ERTS_DW_AINT_LOW_WORD]);
+ return res;
+#endif
+}
+
+static void
+unint64_to_dw_aint(erts_dw_aint_t *dw, Uint64 val)
+{
+#ifdef ETHR_SU_DW_NAINT_T__
+ dw->dw_sint = (ETHR_SU_DW_NAINT_T__) val;
+#else
+ dw->sint[ERTS_DW_AINT_LOW_WORD] = (erts_aint_t) (val & 0xffffffff);
+ dw->sint[ERTS_DW_AINT_HIGH_WORD] = (erts_aint_t) ((val >> 32) & 0xffffffff);
+#endif
+}
+
+static ERTS_INLINE void
+last_pid_data_init_nob(Uint64 val)
+{
+ erts_dw_aint_t dw;
+ unint64_to_dw_aint(&dw, val);
+ erts_smp_dw_atomic_init_nob(&last.pid_data, &dw);
+}
+
+static ERTS_INLINE void
+last_pid_data_set_relb(Uint64 val)
+{
+ erts_dw_aint_t dw;
+ unint64_to_dw_aint(&dw, val);
+ erts_smp_dw_atomic_set_relb(&last.pid_data, &dw);
+}
+
+static ERTS_INLINE Uint64
+last_pid_data_read_nob(void)
+{
+ erts_dw_aint_t dw;
+ erts_smp_dw_atomic_read_nob(&last.pid_data, &dw);
+ return dw_aint_to_uint64(&dw);
+}
+
+static ERTS_INLINE Uint64
+last_pid_data_read_acqb(void)
+{
+ erts_dw_aint_t dw;
+ erts_smp_dw_atomic_read_acqb(&last.pid_data, &dw);
+ return dw_aint_to_uint64(&dw);
+}
+
+static ERTS_INLINE Uint64
+last_pid_data_cmpxchg_relb(Uint64 new, Uint64 exp)
+{
+ erts_dw_aint_t dw_new, dw_xchg;
+
+ unint64_to_dw_aint(&dw_new, new);
+ unint64_to_dw_aint(&dw_xchg, exp);
+
+ if (erts_smp_dw_atomic_cmpxchg_relb(&last.pid_data, &dw_new, &dw_xchg))
+ return exp;
+ else
+ return dw_aint_to_uint64(&dw_xchg);
+}
+
+#elif defined(ARCH_64)
+
+union {
+ erts_smp_atomic_t pid_data;
+ char align[ERTS_CACHE_LINE_SIZE];
+} last erts_align_attribute(ERTS_CACHE_LINE_SIZE);
+
+static ERTS_INLINE void
+last_pid_data_init_nob(Uint64 val)
+{
+ erts_smp_atomic_init_nob(&last.pid_data, (erts_aint_t) val);
+}
+
+static ERTS_INLINE void
+last_pid_data_set_relb(Uint64 val)
+{
+ erts_smp_atomic_set_relb(&last.pid_data, (erts_aint_t) val);
+}
+
+static ERTS_INLINE Uint64
+last_pid_data_read_nob(void)
+{
+ return (Uint64) erts_smp_atomic_read_nob(&last.pid_data);
+}
+
+static ERTS_INLINE Uint64
+last_pid_data_read_acqb(void)
+{
+ return (Uint64) erts_smp_atomic_read_acqb(&last.pid_data);
+}
+
+static ERTS_INLINE Uint64
+last_pid_data_cmpxchg_relb(Uint64 new, Uint64 exp)
+{
+ return (Uint64) erts_smp_atomic_cmpxchg_relb(&last.pid_data,
+ (erts_aint_t) new,
+ (erts_aint_t) exp);
+}
+
+#else
+# error "Not 64-bit, nor 32-bit architecture..."
+#endif
+
+static ERTS_INLINE int
+last_pid_data_cmp(Uint64 lpd1, Uint64 lpd2)
+{
+ Uint64 lpd1_wrap;
+
+ if (lpd1 == lpd2)
+ return 0;
+
+ lpd1_wrap = lpd1 + (((Uint64) 1) << 63);
+
+ if (lpd1 < lpd1_wrap)
+ return (lpd1 < lpd2 && lpd2 < lpd1_wrap) ? -1 : 1;
+ else
+ return (lpd1_wrap <= lpd2 && lpd2 < lpd1) ? 1 : -1;
+}
+
+
+#define ERTS_PID_DATA_MASK__ ((1 << _PID_DATA_SIZE) - 1)
int erts_sched_compact_load;
Uint erts_no_schedulers;
-Uint erts_max_processes = ERTS_DEFAULT_MAX_PROCESSES;
-Uint erts_process_tab_index_mask;
+
+ErtsProcTab erts_proc erts_align_attribute(ERTS_CACHE_LINE_SIZE);
int erts_sched_thread_suggested_stack_size = -1;
@@ -188,7 +348,7 @@ static struct {
struct {
int active_runqs;
int reds;
- int max_len;
+ erts_aint32_t max_len;
} prev_rise;
Uint n;
} balance_info;
@@ -208,7 +368,7 @@ erts_sched_stat_t erts_sched_stat;
static erts_tsd_key_t sched_data_key;
#endif
-static erts_smp_mtx_t proc_tab_mtx;
+erts_smp_rwmtx_t erts_proc_tab_rwmtx;
static erts_smp_atomic32_t function_calls;
@@ -231,7 +391,6 @@ typedef union {
static ErtsAlignedSchedulerSleepInfo *aligned_sched_sleep_info;
-Process** process_tab;
static Uint last_reductions;
static Uint last_exact_reductions;
Uint erts_default_process_flags;
@@ -257,11 +416,11 @@ struct ErtsTermProcElement_ {
union {
struct {
Eterm pid;
- SysTimeval spawned;
- SysTimeval exited;
+ Uint64 spawned;
+ Uint64 exited;
} process;
struct {
- SysTimeval time;
+ Uint64 interval;
} bif_invocation;
} u;
};
@@ -359,9 +518,11 @@ dbg_chk_aux_work_val(erts_aint32_t value)
valid |= ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN;
#endif
#ifdef ERTS_SMP
+ valid |= ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP;
valid |= ERTS_SSI_AUX_WORK_MISC_THR_PRGR;
valid |= ERTS_SSI_AUX_WORK_DD;
valid |= ERTS_SSI_AUX_WORK_DD_THR_PRGR;
+ valid |= ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP;
#endif
#if HAVE_ERTS_MSEG
valid |= ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK;
@@ -369,6 +530,10 @@ dbg_chk_aux_work_val(erts_aint32_t value)
#ifdef ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN
valid |= ERTS_SSI_AUX_WORK_CHECK_CHILDREN;
#endif
+#ifdef ERTS_SMP
+ valid |= ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION;
+ valid |= ERTS_SSI_AUX_WORK_FINISH_BP;
+#endif
#ifdef ERTS_SSI_AUX_WORK_REAP_PORTS
valid |= ERTS_SSI_AUX_WORK_REAP_PORTS;
#endif
@@ -398,6 +563,53 @@ erts_smp_lc_runq_is_locked(ErtsRunQueue *runq)
}
#endif
+static erts_interval_t *proc_interval;
+
+static void
+proc_interval_init(void)
+{
+ proc_interval = erts_alloc_permanent_cache_aligned(
+ ERTS_ALC_T_PROC_INTERVAL,
+ sizeof(erts_interval_t));
+ erts_smp_interval_init(proc_interval);
+}
+
+static ERTS_INLINE Uint64
+get_proc_interval(void)
+{
+ return erts_smp_current_interval_nob(proc_interval);
+}
+
+static ERTS_INLINE Uint64
+ensure_later_proc_interval(Uint64 interval)
+{
+ return erts_smp_ensure_later_interval_nob(proc_interval, interval);
+}
+
+static ERTS_INLINE Uint64
+step_proc_interval(void)
+{
+ return erts_smp_step_interval_nob(proc_interval);
+}
+
+Uint64
+erts_get_proc_interval(void)
+{
+ return get_proc_interval();
+}
+
+Uint64
+erts_ensure_later_proc_interval(Uint64 interval)
+{
+ return ensure_later_proc_interval(interval);
+}
+
+Uint64
+erts_step_proc_interval(void)
+{
+ return step_proc_interval();
+}
+
void
erts_pre_init_process(void)
{
@@ -447,7 +659,16 @@ erts_pre_init_process(void)
void
erts_init_process(int ncpu)
{
- Uint proc_bits = ERTS_PROC_BITS;
+ int proc_tab_sz;
+ int max_proc_bits;
+ int proc_bits = ERTS_PROC_BITS;
+ erts_smp_atomic_t *proc_entry;
+ char *proc_tab_end;
+ erts_smp_rwmtx_opt_t proc_tab_rwmtx_opts = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER;
+ proc_tab_rwmtx_opts.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ;
+ proc_tab_rwmtx_opts.lived = ERTS_SMP_RWMTX_LONG_LIVED;
+
+ proc_interval_init();
#ifdef ERTS_SMP
erts_disable_proc_not_running_opt = 0;
@@ -458,23 +679,51 @@ erts_init_process(int ncpu)
erts_smp_atomic32_init_nob(&process_count, 0);
- if (erts_use_r9_pids_ports) {
+ if (erts_use_r9_pids_ports)
proc_bits = ERTS_R9_PROC_BITS;
- ASSERT(erts_max_processes <= (1 << ERTS_R9_PROC_BITS));
+
+ if (erts_proc.max > (1 << proc_bits))
+ erts_proc.max = 1 << proc_bits;
+
+ proc_tab_sz = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(erts_proc.max
+ * sizeof(erts_smp_atomic_t));
+ erts_proc.tab = erts_alloc(ERTS_ALC_T_PROC_TABLE, proc_tab_sz);
+ proc_tab_end = ((char *) erts_proc.tab) + proc_tab_sz;
+ proc_entry = erts_proc.tab;
+ while (proc_tab_end > ((char *) proc_entry)) {
+ erts_smp_atomic_init_nob(proc_entry, ERTS_AINT_NULL);
+ proc_entry++;
}
- process_tab = (Process**) erts_alloc(ERTS_ALC_T_PROC_TABLE,
- erts_max_processes*sizeof(Process*));
- sys_memzero(process_tab, erts_max_processes * sizeof(Process*));
+ erts_smp_rwmtx_init_opt(&erts_proc_tab_rwmtx,
+ &proc_tab_rwmtx_opts,
+ "proc_tab");
+ last_pid_data_init_nob(~((Uint64) 0));
- erts_smp_mtx_init(&proc_tab_mtx, "proc_tab");
- p_last = -1;
- p_next = 0;
- p_serial = 0;
+ max_proc_bits = erts_fit_in_bits_int32((Sint32) erts_proc.max - 1);
+
+ erts_proc.tab_cache_lines = proc_tab_sz/ERTS_CACHE_LINE_SIZE;
+ erts_proc.pix_per_cache_line = ERTS_CACHE_LINE_SIZE/sizeof(erts_smp_atomic_t);
+ if ((erts_proc.max & (erts_proc.max - 1))
+ | (erts_proc.pix_per_cache_line & (erts_proc.pix_per_cache_line - 1))) {
+ /*
+ * erts_proc.max or erts_proc.pix_per_cache_line
+ * not a power of 2 :(
+ */
+ erts_proc.pix_cl_mask = 0;
+ erts_proc.pix_cl_shift = 0;
+ erts_proc.pix_cli_mask = 0;
+ erts_proc.pix_cli_shift = 0;
+ }
+ else {
+ ASSERT((erts_proc.tab_cache_lines
+ & (erts_proc.tab_cache_lines - 1)) == 0);
+ erts_proc.pix_cl_mask = erts_proc.tab_cache_lines-1;
+ erts_proc.pix_cl_shift = erts_fit_in_bits_int32(erts_proc.pix_per_cache_line-1);
+ erts_proc.pix_cli_shift = erts_fit_in_bits_int32(erts_proc.pix_cl_mask);
+ erts_proc.pix_cli_mask = (1 << (max_proc_bits - erts_proc.pix_cli_shift)) - 1;
+ }
- p_serial_shift = erts_fit_in_bits(erts_max_processes - 1);
- p_serial_mask = ((~(~((Uint) 0) << proc_bits)) >> p_serial_shift);
- erts_process_tab_index_mask = ~(~((Uint) 0) << p_serial_shift);
last_reductions = 0;
last_exact_reductions = 0;
erts_default_process_flags = 0;
@@ -732,8 +981,9 @@ static ERTS_INLINE ErtsProcList *
proclist_create(Process *p)
{
ErtsProcList *plp = proclist_alloc();
+ ensure_later_proc_interval(p->started_interval);
plp->pid = p->id;
- plp->started = p->started;
+ plp->started_interval = p->started_interval;
return plp;
}
@@ -746,8 +996,7 @@ proclist_destroy(ErtsProcList *plp)
static ERTS_INLINE int
proclist_same(ErtsProcList *plp, Process *p)
{
- return (plp->pid == p->id
- && erts_cmp_timeval(&plp->started, &p->started) == 0);
+ return plp->pid == p->id && plp->started_interval == p->started_interval;
}
ErtsProcList *
@@ -930,6 +1179,45 @@ haw_thr_prgr_current_check_progress(ErtsAuxWorkData *awdp)
}
}
+static ERTS_INLINE erts_aint32_t
+handle_delayed_aux_work_wakeup(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
+{
+ int jix, max_jix;
+ unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP);
+
+ ERTS_THR_MEMORY_BARRIER;
+
+ max_jix = awdp->delayed_wakeup.jix;
+ awdp->delayed_wakeup.jix = -1;
+ for (jix = 0; jix <= max_jix; jix++) {
+ int sched = awdp->delayed_wakeup.job[jix].sched;
+ erts_aint32_t aux_work = awdp->delayed_wakeup.job[jix].aux_work;
+
+ ASSERT(awdp->delayed_wakeup.sched2jix[sched] == jix);
+ awdp->delayed_wakeup.sched2jix[sched] = -1;
+ set_aux_work_flags_wakeup_nob(ERTS_SCHED_SLEEP_INFO_IX(sched-1),
+ aux_work);
+ }
+ return aux_work & ~ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP;
+}
+
+static ERTS_INLINE void
+schedule_aux_work_wakeup(ErtsAuxWorkData *awdp, int sched, erts_aint32_t aux_work)
+{
+ int jix = awdp->delayed_wakeup.sched2jix[sched];
+ if (jix >= 0) {
+ ASSERT(awdp->delayed_wakeup.job[jix].sched == sched);
+ awdp->delayed_wakeup.job[jix].aux_work |= aux_work;
+ }
+ else {
+ jix = ++awdp->delayed_wakeup.jix;
+ awdp->delayed_wakeup.sched2jix[sched] = jix;
+ awdp->delayed_wakeup.job[jix].sched = sched;
+ awdp->delayed_wakeup.job[jix].aux_work = aux_work;
+ }
+ set_aux_work_flags_wakeup_nob(awdp->ssi, ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP);
+}
+
#endif
typedef struct erts_misc_aux_work_t_ erts_misc_aux_work_t;
@@ -1160,7 +1448,94 @@ handle_async_ready_clean(ErtsAuxWorkData *awdp,
}
}
+#endif /* ERTS_USE_ASYNC_READY_Q */
+
+#ifdef ERTS_SMP
+void
+erts_notify_code_ix_activation(Process* p, ErtsThrPrgrVal later)
+{
+ ErtsAuxWorkData* awdp = &p->scheduler_data->aux_work_data;
+ ASSERT(awdp->code_ix_activation.code_stager == NULL);
+ awdp->code_ix_activation.code_stager = p;
+ awdp->code_ix_activation.thr_prgr = later;
+ erts_smp_proc_inc_refc(p);
+ set_aux_work_flags_wakeup_relb(p->scheduler_data->ssi,
+ ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION);
+}
+
+static erts_aint32_t
+handle_code_ix_activation(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
+{
+ Process* p;
+ if (!erts_thr_progress_has_reached(awdp->code_ix_activation.thr_prgr)) {
+ return aux_work & ~ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION;
+ }
+ unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION);
+ p = awdp->code_ix_activation.code_stager;
+ ASSERT(p);
+#ifdef DEBUG
+ awdp->code_ix_activation.code_stager = NULL;
+#endif
+ erts_commit_staging_code_ix();
+ erts_release_code_write_permission();
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ if (!ERTS_PROC_IS_EXITING(p)) {
+ erts_resume(p, ERTS_PROC_LOCK_STATUS);
+ }
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_dec_refc(p);
+ return aux_work & ~ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION;
+}
+#endif /* ERTS_SMP */
+
+#ifdef ERTS_SMP
+void
+erts_notify_finish_breakpointing(Process* p)
+{
+ ErtsAuxWorkData* awdp = &p->scheduler_data->aux_work_data;
+
+ ASSERT(awdp->bp_ix_activation.stager == NULL);
+ awdp->bp_ix_activation.stager = p;
+ awdp->bp_ix_activation.thr_prgr = erts_thr_progress_later(awdp->esdp);
+ erts_thr_progress_wakeup(awdp->esdp, awdp->bp_ix_activation.thr_prgr);
+ erts_smp_proc_inc_refc(p);
+ set_aux_work_flags_wakeup_relb(p->scheduler_data->ssi,
+ ERTS_SSI_AUX_WORK_FINISH_BP);
+}
+
+static erts_aint32_t
+handle_finish_bp(ErtsAuxWorkData* awdp, erts_aint32_t aux_work)
+{
+ ErtsThrPrgrVal current = haw_thr_prgr_current(awdp);
+
+ if (!erts_thr_progress_has_reached_this(current,
+ awdp->bp_ix_activation.thr_prgr)) {
+ return aux_work & ~ERTS_SSI_AUX_WORK_FINISH_BP;
+ }
+ if (erts_finish_breakpointing()) { /* Not done */
+ /* Arrange for being called again */
+ awdp->bp_ix_activation.thr_prgr =
+ erts_thr_progress_later(awdp->esdp);
+ erts_thr_progress_wakeup(awdp->esdp, awdp->bp_ix_activation.thr_prgr);
+ } else { /* Done */
+ Process* p;
+
+ unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_FINISH_BP);
+ p = awdp->bp_ix_activation.stager;
+#ifdef DEBUG
+ awdp->bp_ix_activation.stager = NULL;
#endif
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ if (!ERTS_PROC_IS_EXITING(p)) {
+ erts_resume(p, ERTS_PROC_LOCK_STATUS);
+ }
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_dec_refc(p);
+ erts_release_code_write_permission();
+ }
+ return aux_work & ~ERTS_SSI_AUX_WORK_FINISH_BP;
+}
+#endif /* ERTS_SMP */
static ERTS_INLINE erts_aint32_t
handle_fix_alloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
@@ -1186,8 +1561,14 @@ handle_fix_alloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
void
erts_alloc_notify_delayed_dealloc(int ix)
{
- set_aux_work_flags_wakeup_nob(ERTS_SCHED_SLEEP_INFO_IX(ix-1),
- ERTS_SSI_AUX_WORK_DD);
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ if (esdp)
+ schedule_aux_work_wakeup(&esdp->aux_work_data,
+ ix,
+ ERTS_SSI_AUX_WORK_DD);
+ else
+ set_aux_work_flags_wakeup_relb(ERTS_SCHED_SLEEP_INFO_IX(ix-1),
+ ERTS_SSI_AUX_WORK_DD);
}
static ERTS_INLINE erts_aint32_t
@@ -1273,6 +1654,74 @@ handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
return aux_work & ~ERTS_SSI_AUX_WORK_DD_THR_PRGR;
}
+/*
+ * Handle scheduled thread progress later operations.
+ */
+#define ERTS_MAX_THR_PRGR_LATER_OPS 50
+
+static ERTS_INLINE erts_aint32_t
+handle_thr_prgr_later_op(ErtsAuxWorkData *awdp, erts_aint32_t aux_work)
+{
+ int lops;
+ ErtsThrPrgrVal current = haw_thr_prgr_current(awdp);
+
+ 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;
+ lop->func(lop->data);
+ if (!awdp->later_op.first) {
+ awdp->later_op.last = NULL;
+ unset_aux_work_flags(awdp->ssi,
+ ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP);
+ return aux_work & ~ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP;
+ }
+ }
+
+ return aux_work;
+}
+
+#endif /* ERTS_SMP */
+
+void
+erts_schedule_thr_prgr_later_op(void (*later_func)(void *),
+ void *later_data,
+ ErtsThrPrgrLaterOp *lop)
+{
+#ifndef ERTS_SMP
+ later_func(later_data);
+#else
+ ErtsSchedulerData *esdp;
+ ErtsAuxWorkData *awdp;
+ int request_wakeup = 1;
+
+ esdp = erts_get_scheduler_data();
+ ASSERT(esdp);
+ awdp = &esdp->aux_work_data;
+
+ lop->func = later_func;
+ lop->data = later_data;
+ lop->later = erts_thr_progress_later(esdp);
+ lop->next = NULL;
+ if (!awdp->later_op.last)
+ awdp->later_op.first = lop;
+ else {
+ ErtsThrPrgrLaterOp *last = awdp->later_op.last;
+ last->next = lop;
+ if (erts_thr_progress_equal(last->later, lop->later))
+ request_wakeup = 0;
+ }
+ awdp->later_op.last = lop;
+ set_aux_work_flags_wakeup_nob(awdp->ssi,
+ ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP);
+ if (request_wakeup)
+ erts_thr_progress_wakeup(esdp, lop->later);
+#endif
+}
+
+#ifdef ERTS_SMP
+
static erts_atomic32_t completed_dealloc_count;
static void
@@ -1485,6 +1934,8 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting)
* eachother. Most frequent first.
*/
#ifdef ERTS_SMP
+ HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP,
+ handle_delayed_aux_work_wakeup);
HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_DD,
handle_delayed_dealloc);
/* DD must be before DD_THR_PRGR */
@@ -1496,6 +1947,11 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting)
| ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC),
handle_fix_alloc);
+#ifdef ERTS_SMP
+ HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP,
+ handle_thr_prgr_later_op);
+#endif
+
#if ERTS_USE_ASYNC_READY_Q
HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_ASYNC_READY,
handle_async_ready);
@@ -1525,9 +1981,19 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting)
handle_mseg_cache_check);
#endif
+#ifdef ERTS_SMP
+ HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION,
+ handle_code_ix_activation);
+#endif
+
HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_REAP_PORTS,
handle_reap_ports);
+#ifdef ERTS_SMP
+ HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_FINISH_BP,
+ handle_finish_bp);
+#endif
+
ERTS_DBG_CHK_AUX_WORK_VAL(aux_work);
#ifdef ERTS_SMP
@@ -1686,8 +2152,8 @@ sched_waiting_sys(Uint no, ErtsRunQueue *rq)
{
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
ASSERT(rq->waiting >= 0);
- rq->flags |= (ERTS_RUNQ_FLG_OUT_OF_WORK
- | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK);
+ (void) ERTS_RUNQ_FLGS_SET(rq, (ERTS_RUNQ_FLG_OUT_OF_WORK
+ | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK));
rq->waiting++;
rq->waiting *= -1;
rq->woken = 0;
@@ -1763,8 +2229,8 @@ static ERTS_INLINE void
sched_waiting(Uint no, ErtsRunQueue *rq)
{
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
- rq->flags |= (ERTS_RUNQ_FLG_OUT_OF_WORK
- | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK);
+ (void) ERTS_RUNQ_FLGS_SET(rq, (ERTS_RUNQ_FLG_OUT_OF_WORK
+ | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK));
if (rq->waiting < 0)
rq->waiting--;
else
@@ -1796,9 +2262,8 @@ ongoing_multi_scheduling_block(void)
static ERTS_INLINE void
empty_runq(ErtsRunQueue *rq)
{
- erts_aint32_t oifls = erts_smp_atomic32_read_band_nob(&rq->info_flags,
- ~ERTS_RUNQ_IFLG_NONEMPTY);
- if (oifls & ERTS_RUNQ_IFLG_NONEMPTY) {
+ Uint32 old_flags = ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_NONEMPTY|ERTS_RUNQ_FLG_PROTECTED);
+ if (old_flags & ERTS_RUNQ_FLG_NONEMPTY) {
#ifdef DEBUG
erts_aint32_t empty = erts_smp_atomic32_read_nob(&no_empty_run_queues);
/*
@@ -1814,9 +2279,8 @@ empty_runq(ErtsRunQueue *rq)
static ERTS_INLINE void
non_empty_runq(ErtsRunQueue *rq)
{
- erts_aint32_t oifls = erts_smp_atomic32_read_bor_nob(&rq->info_flags,
- ERTS_RUNQ_IFLG_NONEMPTY);
- if (!(oifls & ERTS_RUNQ_IFLG_NONEMPTY)) {
+ Uint32 old_flags = ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_NONEMPTY);
+ if (!(old_flags & ERTS_RUNQ_FLG_NONEMPTY)) {
#ifdef DEBUG
erts_aint32_t empty = erts_smp_atomic32_read_nob(&no_empty_run_queues);
/*
@@ -1963,7 +2427,7 @@ thr_prgr_fin_wait(void *vssi)
| ERTS_SSI_FLG_TSE_SLEEPING));
}
-static void init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp);
+static void init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp);
static void *
aux_thread(void *unused)
@@ -1983,7 +2447,7 @@ aux_thread(void *unused)
callbacks.finalize_wait = thr_prgr_fin_wait;
erts_thr_progress_register_managed_thread(NULL, &callbacks, 1);
- init_aux_work_data(awdp, NULL);
+ init_aux_work_data(awdp, NULL, NULL);
awdp->ssi = ssi;
sched_prep_spin_wait(ssi);
@@ -2431,19 +2895,16 @@ try_inc_no_active_runqs(int active)
static ERTS_INLINE int
chk_wake_sched(ErtsRunQueue *crq, int ix, int activate)
{
- erts_aint32_t iflgs;
+ Uint32 flags;
ErtsRunQueue *wrq;
if (crq->ix == ix)
return 0;
wrq = ERTS_RUNQ_IX(ix);
- iflgs = erts_smp_atomic32_read_nob(&wrq->info_flags);
- if (!(iflgs & (ERTS_RUNQ_IFLG_SUSPENDED|ERTS_RUNQ_IFLG_NONEMPTY))) {
+ flags = ERTS_RUNQ_FLGS_GET(wrq);
+ if (!(flags & (ERTS_RUNQ_FLG_SUSPENDED|ERTS_RUNQ_FLG_NONEMPTY))) {
if (activate) {
- if (try_inc_no_active_runqs(ix+1)) {
- erts_smp_xrunq_lock(crq, wrq);
- wrq->flags &= ~ERTS_RUNQ_FLG_INACTIVE;
- erts_smp_xrunq_unlock(crq, wrq);
- }
+ if (try_inc_no_active_runqs(ix+1))
+ (void) ERTS_RUNQ_FLGS_UNSET(wrq, ERTS_RUNQ_FLG_INACTIVE);
}
wake_scheduler(wrq, 0);
return 1;
@@ -2510,9 +2971,7 @@ erts_sched_notify_check_cpu_bind(void)
int ix;
for (ix = 0; ix < erts_no_run_queues; ix++) {
ErtsRunQueue *rq = ERTS_RUNQ_IX(ix);
- erts_smp_runq_lock(rq);
- rq->flags |= ERTS_RUNQ_FLG_CHK_CPU_BIND;
- erts_smp_runq_unlock(rq);
+ (void) ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_CHK_CPU_BIND);
wake_scheduler(rq, 0);
}
#else
@@ -2521,409 +2980,547 @@ erts_sched_notify_check_cpu_bind(void)
}
-#ifdef ERTS_SMP
+static ERTS_INLINE void
+enqueue_process(ErtsRunQueue *runq, int prio, Process *p)
+{
+ ErtsRunPrioQueue *rpq;
-ErtsRunQueue *
-erts_prepare_emigrate(ErtsRunQueue *c_rq, ErtsRunQueueInfo *c_rqi, int prio)
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
+
+ erts_smp_inc_runq_len(runq, &runq->procs.prio_info[prio], prio);
+
+ if (prio == PRIORITY_LOW) {
+ p->schedule_count = RESCHEDULE_LOW;
+ rpq = &runq->procs.prio[PRIORITY_NORMAL];
+ }
+ else {
+ p->schedule_count = 1;
+ rpq = &runq->procs.prio[prio];
+ }
+
+ p->next = NULL;
+ if (rpq->last)
+ rpq->last->next = p;
+ else
+ rpq->first = p;
+ rpq->last = p;
+}
+
+
+static ERTS_INLINE void
+unqueue_process(ErtsRunQueue *runq,
+ ErtsRunPrioQueue *rpq,
+ ErtsRunQueueInfo *rqi,
+ int prio,
+ Process *prev_proc,
+ Process *proc)
{
- ASSERT(ERTS_CHK_RUNQ_FLG_EMIGRATE(c_rq->flags, prio));
- ASSERT(ERTS_CHK_RUNQ_FLG_EVACUATE(c_rq->flags, prio)
- || c_rqi->len >= c_rqi->migrate.limit.this);
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
- while (1) {
- ErtsRunQueue *n_rq = c_rqi->migrate.runq;
- ERTS_DBG_VERIFY_VALID_RUNQP(n_rq);
- erts_smp_xrunq_lock(c_rq, n_rq);
-
- /*
- * erts_smp_xrunq_lock() may release lock on c_rq! We have
- * to check that we still want to emigrate and emigrate
- * to the same run queue as before.
- */
+ if (prev_proc)
+ prev_proc->next = proc->next;
+ else
+ rpq->first = proc->next;
+ if (!proc->next)
+ rpq->last = prev_proc;
- if (ERTS_CHK_RUNQ_FLG_EMIGRATE(c_rq->flags, prio)) {
- Uint32 force = (ERTS_CHK_RUNQ_FLG_EVACUATE(c_rq->flags, prio)
- | (c_rq->flags & ERTS_RUNQ_FLG_INACTIVE));
- if (force || c_rqi->len > c_rqi->migrate.limit.this) {
- ErtsRunQueueInfo *n_rqi;
- /* We still want to emigrate */
-
- if (n_rq != c_rqi->migrate.runq) {
- /* Ahh... run queue changed; need to do it all over again... */
- erts_smp_runq_unlock(n_rq);
- continue;
- }
- else {
+ if (!rpq->first)
+ rpq->last = NULL;
- if (prio == ERTS_PORT_PRIO_LEVEL)
- n_rqi = &n_rq->ports.info;
- else
- n_rqi = &n_rq->procs.prio_info[prio];
+ erts_smp_dec_runq_len(runq, rqi, prio);
+}
- if (force || (n_rqi->len < c_rqi->migrate.limit.other)) {
- /* emigrate ... */
- return n_rq;
- }
- }
- }
- }
- ASSERT(n_rq != c_rq);
- erts_smp_runq_unlock(n_rq);
- if (!(c_rq->flags & ERTS_RUNQ_FLG_INACTIVE)) {
- /* No more emigrations to this runq */
- ERTS_UNSET_RUNQ_FLG_EMIGRATE(c_rq->flags, prio);
- ERTS_DBG_SET_INVALID_RUNQP(c_rqi->migrate.runq, 0x3);
- }
+static ERTS_INLINE Process *
+dequeue_process(ErtsRunQueue *runq, int prio_q, erts_aint32_t *statep)
+{
+ erts_aint32_t state;
+ int prio;
+ ErtsRunPrioQueue *rpq;
+ ErtsRunQueueInfo *rqi;
+ Process *p;
+
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
+
+ ASSERT(PRIORITY_NORMAL == prio_q
+ || PRIORITY_HIGH == prio_q
+ || PRIORITY_MAX == prio_q);
+
+ rpq = &runq->procs.prio[prio_q];
+ p = rpq->first;
+ if (!p)
+ return NULL;
+
+ ERTS_SMP_DATA_DEPENDENCY_READ_MEMORY_BARRIER;
+
+ state = erts_smp_atomic32_read_nob(&p->state);
+ if (statep)
+ *statep = state;
+
+ prio = (int) (ERTS_PSFLG_PRIO_MASK & state);
+
+ rqi = &runq->procs.prio_info[prio];
+
+ if (p)
+ unqueue_process(runq, rpq, rqi, prio, NULL, p);
+
+ return p;
+}
+
+static ERTS_INLINE int
+check_requeue_process(ErtsRunQueue *rq, int prio_q)
+{
+ ErtsRunPrioQueue *rpq = &rq->procs.prio[prio_q];
+ Process *p = rpq->first;
+ if (--p->schedule_count > 0 && p != rpq->last) {
+ /* reschedule */
+ rpq->first = p->next;
+ rpq->last->next = p;
+ rpq->last = p;
+ p->next = NULL;
+ return 1;
+ }
+ return 0;
+}
+#ifdef ERTS_SMP
+
+static ErtsRunQueue *
+check_immigration_need(ErtsRunQueue *c_rq, ErtsMigrationPath *mp, int prio)
+{
+ int len;
+ Uint32 f_flags, f_rq_flags;
+ ErtsRunQueue *f_rq;
+
+ f_flags = mp->prio[prio].flags;
+
+ ASSERT(ERTS_CHK_RUNQ_FLG_IMMIGRATE(mp->flags, prio));
+
+ f_rq = mp->prio[prio].runq;
+ if (!f_rq)
return NULL;
+
+ f_rq_flags = ERTS_RUNQ_FLGS_GET(f_rq);
+ if (f_rq_flags & ERTS_RUNQ_FLG_PROTECTED)
+ return NULL;
+
+ if (ERTS_CHK_RUNQ_FLG_EVACUATE(f_flags, prio))
+ return f_rq;
+
+ if (f_rq_flags & ERTS_RUNQ_FLG_INACTIVE)
+ return f_rq;
+
+ if (prio == ERTS_PORT_PRIO_LEVEL)
+ len = RUNQ_READ_LEN(&c_rq->ports.info.len);
+ else
+ len = RUNQ_READ_LEN(&c_rq->procs.prio_info[prio].len);
+
+ if (len < mp->prio[prio].limit.this) {
+ if (prio == ERTS_PORT_PRIO_LEVEL)
+ len = RUNQ_READ_LEN(&f_rq->ports.info.len);
+ else
+ len = RUNQ_READ_LEN(&f_rq->procs.prio_info[prio].len);
+
+ if (len > mp->prio[prio].limit.other)
+ return f_rq;
}
+ return NULL;
}
static void
-immigrate(ErtsRunQueue *rq)
+immigrate(ErtsRunQueue *c_rq, ErtsMigrationPath *mp)
{
- int prio;
+ Uint32 iflags, iflag;
+ erts_smp_runq_unlock(c_rq);
- ASSERT(rq->flags & ERTS_RUNQ_FLGS_IMMIGRATE_QMASK);
+ ASSERT(erts_thr_progress_is_managed_thread());
- for (prio = 0; prio < ERTS_NO_PRIO_LEVELS; prio++) {
- if (ERTS_CHK_RUNQ_FLG_IMMIGRATE(rq->flags, prio)) {
- ErtsRunQueueInfo *rqi = (prio == ERTS_PORT_PRIO_LEVEL
- ? &rq->ports.info
- : &rq->procs.prio_info[prio]);
- ErtsRunQueue *from_rq = rqi->migrate.runq;
- int rq_locked, from_rq_locked;
+ iflags = mp->flags & ERTS_RUNQ_FLGS_IMMIGRATE_QMASK;
- ERTS_DBG_VERIFY_VALID_RUNQP(from_rq);
+ iflag = iflags & -iflags;
- rq_locked = 1;
- from_rq_locked = 1;
- erts_smp_xrunq_lock(rq, from_rq);
- /*
- * erts_smp_xrunq_lock() may release lock on rq! We have
- * to check that we still want to immigrate from the same
- * run queue as before.
- */
- if (ERTS_CHK_RUNQ_FLG_IMMIGRATE(rq->flags, prio)
- && from_rq == rqi->migrate.runq) {
- ErtsRunQueueInfo *from_rqi = (prio == ERTS_PORT_PRIO_LEVEL
- ? &from_rq->ports.info
- : &from_rq->procs.prio_info[prio]);
- if ((ERTS_CHK_RUNQ_FLG_EVACUATE(rq->flags, prio)
- && ERTS_CHK_RUNQ_FLG_EVACUATE(from_rq->flags, prio)
- && from_rqi->len)
- || (from_rqi->len > rqi->migrate.limit.other
- && rqi->len < rqi->migrate.limit.this)) {
- if (prio == ERTS_PORT_PRIO_LEVEL) {
- Port *prt = from_rq->ports.start;
- if (prt) {
- int prt_locked = 0;
- (void) erts_port_migrate(prt, &prt_locked,
- from_rq, &from_rq_locked,
- rq, &rq_locked);
- if (prt_locked)
- erts_smp_port_unlock(prt);
- }
- }
- else {
- Process *proc;
- ErtsRunPrioQueue *from_rpq;
- from_rpq = (prio == PRIORITY_LOW
- ? &from_rq->procs.prio[PRIORITY_NORMAL]
- : &from_rq->procs.prio[prio]);
- for (proc = from_rpq->first; proc; proc = proc->next)
- if (proc->prio == prio && !proc->bound_runq)
- break;
- if (proc) {
- ErtsProcLocks proc_locks = 0;
- (void) erts_proc_migrate(proc, &proc_locks,
- from_rq, &from_rq_locked,
- rq, &rq_locked);
- if (proc_locks)
- erts_smp_proc_unlock(proc, proc_locks);
- }
+ while (iflag) {
+ ErtsRunQueue *rq;
+ int prio;
+
+ switch (iflag) {
+ case (MAX_BIT << ERTS_RUNQ_FLGS_IMMIGRATE_SHFT):
+ prio = PRIORITY_MAX;
+ break;
+ case (HIGH_BIT << ERTS_RUNQ_FLGS_IMMIGRATE_SHFT):
+ prio = PRIORITY_HIGH;
+ break;
+ case (NORMAL_BIT << ERTS_RUNQ_FLGS_IMMIGRATE_SHFT):
+ prio = PRIORITY_NORMAL;
+ break;
+ case (LOW_BIT << ERTS_RUNQ_FLGS_IMMIGRATE_SHFT):
+ prio = PRIORITY_LOW;
+ break;
+ case (PORT_BIT << ERTS_RUNQ_FLGS_IMMIGRATE_SHFT):
+ prio = ERTS_PORT_PRIO_LEVEL;
+ break;
+ default:
+ erl_exit(ERTS_ABORT_EXIT,
+ "%s:%d:%s(): Invalid immigrate queue mask",
+ __FILE__, __LINE__, __func__);
+ prio = 0;
+ break;
+ }
+
+ iflags &= ~iflag;
+ iflag = iflags & -iflags;
+
+ rq = check_immigration_need(c_rq, mp, prio);
+ if (rq) {
+ erts_smp_runq_lock(rq);
+ if (prio == ERTS_PORT_PRIO_LEVEL) {
+ Port *prt;
+ prt = erts_dequeue_port(rq);
+ if (prt)
+ RUNQ_SET_RQ(&prt->run_queue, c_rq);
+ erts_smp_runq_unlock(rq);
+ if (prt) {
+ /* port might terminate while we have no lock... */
+ rq = erts_port_runq(prt);
+ if (rq) {
+ if (rq != c_rq)
+ erl_exit(ERTS_ABORT_EXIT,
+ "%s:%d:%s(): Internal error",
+ __FILE__, __LINE__, __func__);
+ erts_enqueue_port(c_rq, prt);
+ if (!iflag)
+ return; /* done */
+ erts_smp_runq_unlock(c_rq);
}
}
- else {
- ERTS_UNSET_RUNQ_FLG_IMMIGRATE(rq->flags, prio);
- ERTS_DBG_SET_INVALID_RUNQP(rqi->migrate.runq, 0x1);
+ }
+ else {
+ ErtsRunPrioQueue *rpq = &rq->procs.prio[prio == PRIORITY_LOW
+ ? PRIORITY_NORMAL
+ : prio];
+ Process *prev_proc = NULL;
+ Process *proc = rpq->first;
+ int rq_locked = 1;
+
+ while (proc) {
+ erts_aint32_t state;
+ state = erts_smp_atomic32_read_acqb(&proc->state);
+ if (!(ERTS_PSFLG_BOUND & state)
+ && (prio == (int) (ERTS_PSFLG_PRIO_MASK & state))) {
+ ErtsRunQueueInfo *rqi = &rq->procs.prio_info[prio];
+ unqueue_process(rq, rpq, rqi, prio, prev_proc, proc);
+ erts_smp_runq_unlock(rq);
+ RUNQ_SET_RQ(&proc->run_queue, c_rq);
+ rq_locked = 0;
+
+ erts_smp_runq_lock(c_rq);
+ enqueue_process(c_rq, prio, proc);
+ if (!iflag)
+ return; /* done */
+ erts_smp_runq_unlock(c_rq);
+ break;
+ }
+ prev_proc = proc;
+ proc = proc->next;
}
+ if (rq_locked)
+ erts_smp_runq_unlock(rq);
}
- if (from_rq_locked)
- erts_smp_runq_unlock(from_rq);
- if (!rq_locked)
- erts_smp_runq_lock(rq);
}
}
+
+ erts_smp_runq_lock(c_rq);
}
+static ERTS_INLINE void
+suspend_run_queue(ErtsRunQueue *rq)
+{
+ erts_smp_atomic32_read_bor_nob(&rq->scheduler->ssi->flags,
+ ERTS_SSI_FLG_SUSPENDED);
+ (void) ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_SUSPENDED);
+
+ wake_scheduler(rq, 0);
+}
+
+static void scheduler_ix_resume_wake(Uint ix);
+
+static ERTS_INLINE void
+resume_run_queue(ErtsRunQueue *rq)
+{
+ int pix;
+
+ erts_smp_runq_lock(rq);
+
+ (void) ERTS_RUNQ_FLGS_MASK_SET(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));
+
+ 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);
+
+ scheduler_ix_resume_wake(rq->ix);
+}
+
+typedef struct {
+ Process *first;
+ Process *last;
+} ErtsStuckBoundProcesses;
+
static void
-evacuate_run_queue(ErtsRunQueue *evac_rq, ErtsRunQueue *rq)
+schedule_bound_processes(ErtsRunQueue *rq,
+ ErtsStuckBoundProcesses *sbpp)
{
- Port *prt;
- int notify_to_rq = 0;
- int prio;
- int prt_locked = 0;
- int rq_locked = 0;
- int evac_rq_locked = 1;
- ErtsMigrateResult mres;
+ Process *proc, *next;
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
- erts_smp_runq_lock(evac_rq);
+ proc = sbpp->first;
+ while (proc) {
+ erts_aint32_t state = erts_smp_atomic32_read_acqb(&proc->state);
+ next = proc->next;
+ enqueue_process(rq, (int) (ERTS_PSFLG_PRIO_MASK & state), proc);
+ proc = next;
+ }
+}
- erts_smp_atomic32_read_bor_nob(&evac_rq->scheduler->ssi->flags,
- ERTS_SSI_FLG_SUSPENDED);
+static void
+evacuate_run_queue(ErtsRunQueue *rq,
+ ErtsStuckBoundProcesses *sbpp)
+{
+ int prio_q;
+ ErtsRunQueue *to_rq;
+ ErtsMigrationPaths *mps;
+ ErtsMigrationPath *mp;
- evac_rq->flags &= ~ERTS_RUNQ_FLGS_IMMIGRATE_QMASK;
- evac_rq->flags |= (ERTS_RUNQ_FLGS_EMIGRATE_QMASK
- | ERTS_RUNQ_FLGS_EVACUATE_QMASK
- | ERTS_RUNQ_FLG_SUSPENDED);
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
- erts_smp_atomic32_read_bor_nob(&evac_rq->info_flags, ERTS_RUNQ_IFLG_SUSPENDED);
- /*
- * Need to set up evacuation paths first since we
- * may release the run queue lock on evac_rq
- * when evacuating.
- */
- evac_rq->misc.evac_runq = rq;
- evac_rq->ports.info.migrate.runq = rq;
- for (prio = 0; prio < ERTS_NO_PROC_PRIO_LEVELS; prio++)
- evac_rq->procs.prio_info[prio].migrate.runq = rq;
+ (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
+
+ mps = erts_get_migration_paths_managed();
+ mp = &mps->mpath[rq->ix];
/* Evacuate scheduled misc ops */
- if (evac_rq->misc.start) {
- rq_locked = 1;
- erts_smp_xrunq_lock(evac_rq, rq);
- if (rq->misc.end)
- rq->misc.end->next = evac_rq->misc.start;
+ if (rq->misc.start) {
+ ErtsMiscOpList *start, *end;
+
+ to_rq = mp->misc_evac_runq;
+ if (!to_rq)
+ return;
+
+ start = rq->misc.start;
+ end = rq->misc.end;
+ rq->misc.start = NULL;
+ rq->misc.end = NULL;
+ erts_smp_runq_unlock(rq);
+
+ erts_smp_runq_lock(to_rq);
+ if (to_rq->misc.end)
+ to_rq->misc.end->next = start;
else
- rq->misc.start = evac_rq->misc.start;
- rq->misc.end = evac_rq->misc.end;
- evac_rq->misc.start = NULL;
- evac_rq->misc.end = NULL;
+ to_rq->misc.start = start;
+
+ to_rq->misc.end = end;
+ erts_smp_runq_unlock(to_rq);
+ smp_notify_inc_runq(to_rq);
+ erts_smp_runq_lock(to_rq);
}
- /* Evacuate scheduled ports */
- prt = evac_rq->ports.start;
- while (prt) {
- mres = erts_port_migrate(prt, &prt_locked,
- evac_rq, &evac_rq_locked,
- rq, &rq_locked);
- if (mres == ERTS_MIGRATE_SUCCESS)
- notify_to_rq = 1;
- if (prt_locked)
- erts_smp_port_unlock(prt);
- if (!evac_rq_locked) {
- evac_rq_locked = 1;
- erts_smp_runq_lock(evac_rq);
+ if (rq->ports.start) {
+ Port *prt;
+
+ to_rq = mp->prio[ERTS_PORT_PRIO_LEVEL].runq;
+ if (!to_rq)
+ return;
+
+ /* Evacuate scheduled ports */
+ prt = rq->ports.start;
+ while (prt) {
+ ErtsRunQueue *prt_rq;
+ prt = erts_dequeue_port(rq);
+ RUNQ_SET_RQ(&prt->run_queue, to_rq);
+ erts_smp_runq_unlock(rq);
+ /*
+ * The port might terminate while
+ * we have no lock on it...
+ */
+ prt_rq = erts_port_runq(prt);
+ if (prt_rq) {
+ if (prt_rq != to_rq)
+ erl_exit(ERTS_ABORT_EXIT,
+ "%s:%d:%s() internal error\n",
+ __FILE__, __LINE__, __func__);
+ erts_enqueue_port(to_rq, prt);
+ erts_smp_runq_unlock(to_rq);
+ }
+ erts_smp_runq_lock(rq);
+ prt = rq->ports.start;
}
- prt = evac_rq->ports.start;
+ smp_notify_inc_runq(to_rq);
}
/* Evacuate scheduled processes */
- for (prio = 0; prio < ERTS_NO_PROC_PRIO_LEVELS; prio++) {
+ for (prio_q = 0; prio_q < ERTS_NO_PROC_PRIO_QUEUES; prio_q++) {
+ erts_aint32_t state;
Process *proc;
+ int notify = 0;
+ to_rq = NULL;
- switch (prio) {
- case PRIORITY_MAX:
- case PRIORITY_HIGH:
- case PRIORITY_NORMAL:
- proc = evac_rq->procs.prio[prio].first;
- while (proc) {
- ErtsProcLocks proc_locks = 0;
-
- /* Bound processes are stuck... */
- while (proc->bound_runq) {
- proc = proc->next;
- if (!proc)
- goto end_of_proc;
- }
-
- mres = erts_proc_migrate(proc, &proc_locks,
- evac_rq, &evac_rq_locked,
- rq, &rq_locked);
- if (mres == ERTS_MIGRATE_SUCCESS)
- notify_to_rq = 1;
- if (proc_locks)
- erts_smp_proc_unlock(proc, proc_locks);
- if (!evac_rq_locked) {
- erts_smp_runq_lock(evac_rq);
- evac_rq_locked = 1;
- }
+ if (!mp->prio[prio_q].runq)
+ return;
+ if (prio_q == PRIORITY_NORMAL && !mp->prio[PRIORITY_LOW].runq)
+ return;
- proc = evac_rq->procs.prio[prio].first;
+ proc = dequeue_process(rq, prio_q, &state);
+ while (proc) {
+ if (ERTS_PSFLG_BOUND & state) {
+ /* Bound processes get stuck here... */
+ proc->next = NULL;
+ if (sbpp->last)
+ sbpp->last->next = proc;
+ else
+ sbpp->first = proc;
+ sbpp->last = proc;
}
+ else {
+ int prio = (int) (ERTS_PSFLG_PRIO_MASK & state);
+ erts_smp_runq_unlock(rq);
- end_of_proc:
+ to_rq = mp->prio[prio].runq;
+ RUNQ_SET_RQ(&proc->run_queue, to_rq);
-#ifdef DEBUG
- for (proc = evac_rq->procs.prio[prio].first;
- proc;
- proc = proc->next) {
- ASSERT(proc->bound_runq);
+ erts_smp_runq_lock(to_rq);
+ enqueue_process(to_rq, prio, proc);
+ erts_smp_runq_unlock(to_rq);
+ notify = 1;
+
+ erts_smp_runq_lock(rq);
}
-#endif
- break;
- case PRIORITY_LOW:
- break;
- default:
- ASSERT(!"Invalid process priority");
- break;
+ proc = dequeue_process(rq, prio_q, &state);
}
+ if (notify)
+ smp_notify_inc_runq(to_rq);
}
- if (rq_locked)
- erts_smp_runq_unlock(rq);
-
- if (evac_rq_locked)
- erts_smp_runq_unlock(evac_rq);
-
- if (notify_to_rq)
- smp_notify_inc_runq(rq);
-
- wake_scheduler(evac_rq, 0);
+ if (ERTS_EMPTY_RUNQ(rq))
+ empty_runq(rq);
}
static int
-try_steal_task_from_victim(ErtsRunQueue *rq, int *rq_lockedp, ErtsRunQueue *vrq)
+try_steal_task_from_victim(ErtsRunQueue *rq, int *rq_lockedp, ErtsRunQueue *vrq, Uint32 flags)
{
- Process *proc;
- int vrq_locked;
+ Uint32 procs_qmask = flags & ERTS_RUNQ_FLGS_PROCS_QMASK;
+ int max_prio_bit;
+ ErtsRunPrioQueue *rpq;
- if (*rq_lockedp)
- erts_smp_xrunq_lock(rq, vrq);
- else
- erts_smp_runq_lock(vrq);
- vrq_locked = 1;
+ if (*rq_lockedp) {
+ erts_smp_runq_unlock(rq);
+ *rq_lockedp = 0;
+ }
- ERTS_SMP_LC_CHK_RUNQ_LOCK(rq, *rq_lockedp);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(vrq, vrq_locked);
+ ERTS_SMP_LC_ASSERT(!erts_smp_lc_runq_is_locked(rq));
+
+ erts_smp_runq_lock(vrq);
if (rq->halt_in_progress)
- goto try_steal_port;
+ goto no_procs;
/*
* Check for a runnable process to steal...
*/
- switch (vrq->flags & ERTS_RUNQ_FLGS_PROCS_QMASK) {
- case MAX_BIT:
- case MAX_BIT|HIGH_BIT:
- case MAX_BIT|NORMAL_BIT:
- case MAX_BIT|LOW_BIT:
- case MAX_BIT|HIGH_BIT|NORMAL_BIT:
- case MAX_BIT|HIGH_BIT|LOW_BIT:
- case MAX_BIT|NORMAL_BIT|LOW_BIT:
- case MAX_BIT|HIGH_BIT|NORMAL_BIT|LOW_BIT:
- for (proc = vrq->procs.prio[PRIORITY_MAX].last;
- proc;
- proc = proc->prev) {
- if (!proc->bound_runq)
- break;
- }
- if (proc)
+ while (procs_qmask) {
+ Process *prev_proc;
+ Process *proc;
+
+ max_prio_bit = procs_qmask & -procs_qmask;
+ switch (max_prio_bit) {
+ case MAX_BIT:
+ rpq = &vrq->procs.prio[PRIORITY_MAX];
break;
- case HIGH_BIT:
- case HIGH_BIT|NORMAL_BIT:
- case HIGH_BIT|LOW_BIT:
- case HIGH_BIT|NORMAL_BIT|LOW_BIT:
- for (proc = vrq->procs.prio[PRIORITY_HIGH].last;
- proc;
- proc = proc->prev) {
- if (!proc->bound_runq)
- break;
- }
- if (proc)
+ case HIGH_BIT:
+ rpq = &vrq->procs.prio[PRIORITY_HIGH];
break;
- case NORMAL_BIT:
- case LOW_BIT:
- case NORMAL_BIT|LOW_BIT:
- for (proc = vrq->procs.prio[PRIORITY_NORMAL].last;
- proc;
- proc = proc->prev) {
- if (!proc->bound_runq)
- break;
- }
- if (proc)
+ case NORMAL_BIT:
+ case LOW_BIT:
+ rpq = &vrq->procs.prio[PRIORITY_NORMAL];
break;
- case 0:
- proc = NULL;
- break;
- default:
- ASSERT(!"Invalid queue mask");
- proc = NULL;
- break;
- }
+ case 0:
+ goto no_procs;
+ default:
+ ASSERT(!"Invalid queue mask");
+ goto no_procs;
+ }
- if (proc) {
- ErtsProcLocks proc_locks = 0;
- int res;
- ErtsMigrateResult mres;
- mres = erts_proc_migrate(proc, &proc_locks,
- vrq, &vrq_locked,
- rq, rq_lockedp);
- if (proc_locks)
- erts_smp_proc_unlock(proc, proc_locks);
- res = !0;
- switch (mres) {
- case ERTS_MIGRATE_FAILED_RUNQ_SUSPENDED:
- res = 0;
- case ERTS_MIGRATE_SUCCESS:
- if (vrq_locked)
+ prev_proc = NULL;
+ proc = rpq->first;
+
+ while (proc) {
+ erts_aint32_t state = erts_smp_atomic32_read_acqb(&proc->state);
+ if (!(ERTS_PSFLG_BOUND & state)) {
+ /* Steal process */
+ int prio = (int) (ERTS_PSFLG_PRIO_MASK & state);
+ ErtsRunQueueInfo *rqi = &vrq->procs.prio_info[prio];
+ unqueue_process(vrq, rpq, rqi, prio, prev_proc, proc);
erts_smp_runq_unlock(vrq);
- return res;
- default: /* Other failures */
- break;
- }
- }
+ RUNQ_SET_RQ(&proc->run_queue, rq);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(rq, *rq_lockedp);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(vrq, vrq_locked);
+ erts_smp_runq_lock(rq);
+ *rq_lockedp = 1;
+ enqueue_process(rq, prio, proc);
+ return !0;
+ }
+ prev_proc = proc;
+ proc = proc->next;
+ }
- if (!vrq_locked) {
- if (*rq_lockedp)
- erts_smp_xrunq_lock(rq, vrq);
- else
- erts_smp_runq_lock(vrq);
- vrq_locked = 1;
+ procs_qmask &= ~max_prio_bit;
}
- try_steal_port:
+no_procs:
- ERTS_SMP_LC_CHK_RUNQ_LOCK(rq, *rq_lockedp);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(vrq, vrq_locked);
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(vrq));
/*
* Check for a runnable port to steal...
*/
- if (vrq->ports.info.len) {
- Port *prt = vrq->ports.end;
- int prt_locked = 0;
- int res;
- ErtsMigrateResult mres;
-
- mres = erts_port_migrate(prt, &prt_locked,
- vrq, &vrq_locked,
- rq, rq_lockedp);
- if (prt_locked)
- erts_smp_port_unlock(prt);
- res = !0;
- switch (mres) {
- case ERTS_MIGRATE_FAILED_RUNQ_SUSPENDED:
- res = 0;
- case ERTS_MIGRATE_SUCCESS:
- if (vrq_locked)
- erts_smp_runq_unlock(vrq);
- return res;
- default: /* Other failures */
- break;
+ if (vrq->ports.start) {
+ ErtsRunQueue *prt_rq;
+ Port *prt = erts_dequeue_port(vrq);
+ RUNQ_SET_RQ(&prt->run_queue, rq);
+ erts_smp_runq_unlock(vrq);
+
+ /*
+ * The port might terminate while
+ * we have no lock on it...
+ */
+
+ prt_rq = erts_port_runq(prt);
+ if (!prt_rq)
+ return 0;
+ else {
+ if (prt_rq != rq)
+ erl_exit(ERTS_ABORT_EXIT,
+ "%s:%d:%s() internal error\n",
+ __FILE__, __LINE__, __func__);
+ *rq_lockedp = 1;
+ erts_enqueue_port(rq, prt);
+ return !0;
}
}
- if (vrq_locked)
- erts_smp_runq_unlock(vrq);
+ erts_smp_runq_unlock(vrq);
return 0;
}
@@ -2933,9 +3530,10 @@ static ERTS_INLINE int
check_possible_steal_victim(ErtsRunQueue *rq, int *rq_lockedp, int vix)
{
ErtsRunQueue *vrq = ERTS_RUNQ_IX(vix);
- erts_aint32_t iflgs = erts_smp_atomic32_read_nob(&vrq->info_flags);
- if (iflgs & ERTS_RUNQ_IFLG_NONEMPTY)
- return try_steal_task_from_victim(rq, rq_lockedp, vrq);
+ Uint32 flags = ERTS_RUNQ_FLGS_GET(vrq);
+ if ((flags & (ERTS_RUNQ_FLG_NONEMPTY
+ | ERTS_RUNQ_FLG_PROTECTED)) == ERTS_RUNQ_FLG_NONEMPTY)
+ return try_steal_task_from_victim(rq, rq_lockedp, vrq, flags);
else
return 0;
}
@@ -2945,15 +3543,12 @@ static int
try_steal_task(ErtsRunQueue *rq)
{
int res, rq_locked, vix, active_rqs, blnc_rqs;
+ Uint32 flags;
- /*
- * We are not allowed to steal jobs to this run queue
- * if it is suspended. Note that it might get suspended
- * at any time when we don't have the lock on the run
- * queue.
- */
- if (rq->flags & ERTS_RUNQ_FLG_SUSPENDED)
- return 0;
+ /* Protect jobs we steal from getting stolen from us... */
+ flags = ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_PROTECTED);
+ if (flags & ERTS_RUNQ_FLG_SUSPENDED)
+ return 0; /* go suspend instead... */
res = 0;
rq_locked = 1;
@@ -3072,12 +3667,123 @@ do { \
ASSERT(sum__ == (RQ)->full_reds_history_sum); \
} while (0);
+#define ERTS_PRE_ALLOCED_MPATHS 8
+
+erts_atomic_t erts_migration_paths;
+
+static struct {
+ size_t size;
+ ErtsMigrationPaths *freelist;
+ struct {
+ ErtsMigrationPaths *first;
+ ErtsMigrationPaths *last;
+ } retired;
+} mpaths;
+
+static void
+init_migration_paths(void)
+{
+ int qix, i;
+ char *p;
+ ErtsMigrationPaths *mps;
+
+ mpaths.size = sizeof(ErtsMigrationPaths);
+ mpaths.size += sizeof(ErtsMigrationPath)*(erts_no_schedulers-1);
+ mpaths.size = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(mpaths.size);
+
+ p = erts_alloc_permanent_cache_aligned(ERTS_ALC_T_LL_MPATHS,
+ (mpaths.size
+ * ERTS_PRE_ALLOCED_MPATHS));
+ mpaths.freelist = NULL;
+ for (i = 0; i < ERTS_PRE_ALLOCED_MPATHS-1; i++) {
+ mps = (ErtsMigrationPaths *) p;
+ mps->next = mpaths.freelist;
+ mpaths.freelist = mps;
+ p += mpaths.size;
+ }
+
+ mps = (ErtsMigrationPaths *) p;
+ mps->block = NULL;
+ for (qix = 0; qix < erts_no_run_queues; qix++) {
+ int pix;
+ mps->mpath[qix].flags = 0;
+ mps->mpath[qix].misc_evac_runq = NULL;
+ for (pix = 0; pix < ERTS_NO_PRIO_LEVELS; pix++) {
+ mps->mpath[qix].prio[pix].limit.this = -1;
+ mps->mpath[qix].prio[pix].limit.other = -1;
+ mps->mpath[qix].prio[pix].runq = NULL;
+ mps->mpath[qix].prio[pix].flags = 0;
+ }
+ }
+ erts_atomic_init_wb(&erts_migration_paths, (erts_aint_t) mps);
+}
+
+static ERTS_INLINE ErtsMigrationPaths *
+alloc_mpaths(void)
+{
+ void *block;
+ ErtsMigrationPaths *res;
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(&balance_info.update_mtx));
+
+ res = mpaths.freelist;
+ if (res) {
+ mpaths.freelist = res->next;
+ res->block = NULL;
+ return res;
+ }
+ res = erts_alloc(ERTS_ALC_T_SL_MPATHS,
+ mpaths.size+ERTS_CACHE_LINE_SIZE);
+ block = (void *) res;
+ if (((UWord) res) & ERTS_CACHE_LINE_MASK)
+ res = (ErtsMigrationPaths *) ((((UWord) res) & ~ERTS_CACHE_LINE_MASK)
+ + ERTS_CACHE_LINE_SIZE);
+ res->block = block;
+ return res;
+}
+
+static ERTS_INLINE void
+retire_mpaths(ErtsMigrationPaths *mps)
+{
+ ErtsThrPrgrVal current;
+
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(&balance_info.update_mtx));
+
+ current = erts_thr_progress_current();
+
+ while (mpaths.retired.first) {
+ ErtsMigrationPaths *tmp = mpaths.retired.first;
+ if (!erts_thr_progress_has_reached_this(current, tmp->thr_prgr))
+ break;
+ mpaths.retired.first = tmp->next;
+ if (tmp->block) {
+ erts_free(ERTS_ALC_T_SL_MPATHS, tmp->block);
+ }
+ else {
+ tmp->next = mpaths.freelist;
+ mpaths.freelist = tmp;
+ }
+ }
+
+ if (!mpaths.retired.first)
+ mpaths.retired.last = NULL;
+
+ mps->thr_prgr = erts_thr_progress_later(NULL);
+ mps->next = NULL;
+
+ if (mpaths.retired.last)
+ mpaths.retired.last->next = mps;
+ else
+ mpaths.retired.first = mps;
+ mpaths.retired.last = mps;
+}
+
static void
check_balance(ErtsRunQueue *c_rq)
{
#if ERTS_MAX_PROCESSES >= (1 << 27)
# error check_balance() assumes ERTS_MAX_PROCESS < (1 << 27)
#endif
+ ErtsMigrationPaths *new_mpaths, *old_mpaths;
ErtsRunQueueBalance avg = {0};
Sint64 scheds_reds, full_scheds_reds;
int forced, active, current_active, oowc, half_full_scheds, full_scheds,
@@ -3103,9 +3809,9 @@ check_balance(ErtsRunQueue *c_rq)
ERTS_FOREACH_RUNQ(rq,
{
if (rq->waiting)
- rq->flags |= ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK;
+ (void) ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK);
else
- rq->flags &= ~ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK;
+ (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK);
rq->check_balance_reds = ERTS_RUNQ_CALL_CHECK_BALANCE_REDS;
});
@@ -3147,7 +3853,7 @@ check_balance(ErtsRunQueue *c_rq)
ErtsRunQueue *rq = ERTS_RUNQ_IX(qix);
erts_smp_runq_lock(rq);
- run_queue_info[qix].flags = rq->flags;
+ run_queue_info[qix].flags = ERTS_RUNQ_FLGS_GET_NOB(rq);
for (pix = 0; pix < ERTS_NO_PROC_PRIO_LEVELS; pix++) {
run_queue_info[qix].prio[pix].max_len
= rq->procs.prio_info[pix].max_len;
@@ -3481,47 +4187,27 @@ erts_fprintf(stderr, "--------------------------------\n");
set_no_active_runqs(active);
balance_info.halftime = 1;
- erts_smp_atomic32_set_nob(&balance_info.checking_balance, 0);
+ new_mpaths = alloc_mpaths();
+
+ /* Write migration paths */
- /* Write migration paths and reset balance statistics in all queues */
for (qix = 0; qix < blnc_no_rqs; qix++) {
int mqix;
- Uint32 flags;
- ErtsRunQueue *rq = ERTS_RUNQ_IX(qix);
- ErtsRunQueueInfo *rqi;
- flags = run_queue_info[qix].flags;
- erts_smp_runq_lock(rq);
- flags |= (rq->flags & ~ERTS_RUNQ_FLGS_MIGRATION_INFO);
- ASSERT(!(flags & ERTS_RUNQ_FLG_OUT_OF_WORK));
- if (rq->waiting)
- flags |= ERTS_RUNQ_FLG_OUT_OF_WORK;
-
- rq->full_reds_history_sum
- = run_queue_info[qix].full_reds_history_sum;
- rq->full_reds_history[freds_hist_ix]
- = run_queue_info[qix].full_reds_history_change;
+ Uint32 flags = run_queue_info[qix].flags;
+ ErtsMigrationPath *mp = &new_mpaths->mpath[qix];
- ERTS_DBG_CHK_FULL_REDS_HISTORY(rq);
+ mp->flags = flags;
+ mp->misc_evac_runq = NULL;
- rq->out_of_work_count = 0;
- rq->flags = flags;
- rq->max_len = rq->len;
for (pix = 0; pix < ERTS_NO_PRIO_LEVELS; pix++) {
- rqi = (pix == ERTS_PORT_PRIO_LEVEL
- ? &rq->ports.info
- : &rq->procs.prio_info[pix]);
- rqi->max_len = rqi->len;
- rqi->reds = 0;
if (!(ERTS_CHK_RUNQ_FLG_EMIGRATE(flags, pix)
| ERTS_CHK_RUNQ_FLG_IMMIGRATE(flags, pix))) {
ASSERT(run_queue_info[qix].prio[pix].immigrate_from < 0);
ASSERT(run_queue_info[qix].prio[pix].emigrate_to < 0);
-#ifdef DEBUG
- rqi->migrate.limit.this = -1;
- rqi->migrate.limit.other = -1;
- ERTS_DBG_SET_INVALID_RUNQP(rqi->migrate.runq, 0x2);
-#endif
-
+ mp->prio[pix].limit.this = -1;
+ mp->prio[pix].limit.other = -1;
+ mp->prio[pix].runq = NULL;
+ mp->prio[pix].flags = 0;
}
else if (ERTS_CHK_RUNQ_FLG_EMIGRATE(flags, pix)) {
ASSERT(!ERTS_CHK_RUNQ_FLG_IMMIGRATE(flags, pix));
@@ -3529,11 +4215,12 @@ erts_fprintf(stderr, "--------------------------------\n");
ASSERT(run_queue_info[qix].prio[pix].emigrate_to >= 0);
mqix = run_queue_info[qix].prio[pix].emigrate_to;
- rqi->migrate.limit.this
+ mp->prio[pix].limit.this
= run_queue_info[qix].prio[pix].migration_limit;
- rqi->migrate.limit.other
+ mp->prio[pix].limit.other
= run_queue_info[mqix].prio[pix].migration_limit;
- rqi->migrate.runq = ERTS_RUNQ_IX(mqix);
+ mp->prio[pix].runq = ERTS_RUNQ_IX(mqix);
+ mp->prio[pix].flags = run_queue_info[mqix].flags;
}
else {
ASSERT(ERTS_CHK_RUNQ_FLG_IMMIGRATE(flags, pix));
@@ -3541,24 +4228,142 @@ erts_fprintf(stderr, "--------------------------------\n");
ASSERT(run_queue_info[qix].prio[pix].immigrate_from >= 0);
mqix = run_queue_info[qix].prio[pix].immigrate_from;
- rqi->migrate.limit.this
+ mp->prio[pix].limit.this
= run_queue_info[qix].prio[pix].migration_limit;
- rqi->migrate.limit.other
+ mp->prio[pix].limit.other
= run_queue_info[mqix].prio[pix].migration_limit;
- rqi->migrate.runq = ERTS_RUNQ_IX(mqix);
+ mp->prio[pix].runq = ERTS_RUNQ_IX(mqix);
+ mp->prio[pix].flags = run_queue_info[mqix].flags;
}
}
+ }
+
+ old_mpaths = erts_get_migration_paths_managed();
+
+ /* Keep offline run-queues as is */
+ for (qix = blnc_no_rqs; qix < erts_no_schedulers; qix++) {
+ ErtsMigrationPath *nmp = &new_mpaths->mpath[qix];
+ ErtsMigrationPath *omp = &old_mpaths->mpath[qix];
+
+ nmp->flags = omp->flags;
+ nmp->misc_evac_runq = omp->misc_evac_runq;
+
+ for (pix = 0; pix < ERTS_NO_PRIO_LEVELS; pix++) {
+ nmp->prio[pix].limit.this = omp->prio[pix].limit.this;
+ nmp->prio[pix].limit.other = omp->prio[pix].limit.other;
+ nmp->prio[pix].runq = omp->prio[pix].runq;
+ nmp->prio[pix].flags = omp->prio[pix].flags;
+ }
+ }
+
+
+ /* Publish new migration paths... */
+ erts_atomic_set_wb(&erts_migration_paths, (erts_aint_t) new_mpaths);
+
+ /* Reset balance statistics in all online queues */
+ for (qix = 0; qix < blnc_no_rqs; qix++) {
+ Uint32 flags = run_queue_info[qix].flags;
+ ErtsRunQueue *rq = ERTS_RUNQ_IX(qix);
+
+ erts_smp_runq_lock(rq);
+ ASSERT(!(flags & ERTS_RUNQ_FLG_OUT_OF_WORK));
+ if (rq->waiting)
+ flags |= ERTS_RUNQ_FLG_OUT_OF_WORK;
+
+ rq->full_reds_history_sum
+ = run_queue_info[qix].full_reds_history_sum;
+ rq->full_reds_history[freds_hist_ix]
+ = run_queue_info[qix].full_reds_history_change;
+
+ ERTS_DBG_CHK_FULL_REDS_HISTORY(rq);
+
+ rq->out_of_work_count = 0;
+ (void) ERTS_RUNQ_FLGS_MASK_SET(rq, ERTS_RUNQ_FLGS_MIGRATION_INFO, flags);
+
+ rq->max_len = rq->len;
+ for (pix = 0; pix < ERTS_NO_PRIO_LEVELS; pix++) {
+ ErtsRunQueueInfo *rqi;
+ rqi = (pix == ERTS_PORT_PRIO_LEVEL
+ ? &rq->ports.info
+ : &rq->procs.prio_info[pix]);
+ erts_smp_reset_max_len(rq, rqi);
+ rqi->reds = 0;
+ }
rq->check_balance_reds = ERTS_RUNQ_CALL_CHECK_BALANCE_REDS;
erts_smp_runq_unlock(rq);
}
+ erts_smp_atomic32_set_nob(&balance_info.checking_balance, 0);
+
balance_info.n++;
+ retire_mpaths(old_mpaths);
erts_smp_mtx_unlock(&balance_info.update_mtx);
erts_smp_runq_lock(c_rq);
}
+static void
+change_no_used_runqs(int used)
+{
+ ErtsMigrationPaths *new_mpaths, *old_mpaths;
+ int active, qix;
+ erts_smp_mtx_lock(&balance_info.update_mtx);
+ get_no_runqs(&active, NULL);
+ set_no_used_runqs(used);
+
+ old_mpaths = erts_get_migration_paths_managed();
+ new_mpaths = alloc_mpaths();
+
+ /* Write migration paths... */
+
+ for (qix = 0; qix < used; qix++) {
+ int pix;
+ ErtsMigrationPath *omp = &old_mpaths->mpath[qix];
+ ErtsMigrationPath *nmp = &new_mpaths->mpath[qix];
+
+ nmp->flags = omp->flags & ~ERTS_RUNQ_FLGS_MIGRATION_QMASKS;
+ nmp->misc_evac_runq = NULL;
+
+ for (pix = 0; pix < ERTS_NO_PRIO_LEVELS; pix++) {
+ nmp->prio[pix].limit.this = -1;
+ nmp->prio[pix].limit.other = -1;
+ nmp->prio[pix].runq = NULL;
+ nmp->prio[pix].flags = 0;
+ }
+ }
+ for (qix = used; qix < erts_no_run_queues; qix++) {
+ int pix;
+ ErtsRunQueue *to_rq = ERTS_RUNQ_IX(qix % used);
+ ErtsMigrationPath *nmp = &new_mpaths->mpath[qix];
+
+ nmp->flags = (ERTS_RUNQ_FLGS_EMIGRATE_QMASK
+ | ERTS_RUNQ_FLGS_EVACUATE_QMASK);
+ nmp->misc_evac_runq = to_rq;
+ for (pix = 0; pix < ERTS_NO_PRIO_LEVELS; pix++) {
+ nmp->prio[pix].limit.this = -1;
+ nmp->prio[pix].limit.other = -1;
+ nmp->prio[pix].runq = to_rq;
+ nmp->prio[pix].flags = 0;
+ }
+ }
+
+ /* ... and publish them. */
+ erts_atomic_set_wb(&erts_migration_paths, (erts_aint_t) new_mpaths);
+
+ retire_mpaths(old_mpaths);
+
+ /* Make sure that we balance soon... */
+ balance_info.forced_check_balance = 1;
+
+ erts_smp_mtx_unlock(&balance_info.update_mtx);
+
+ erts_smp_runq_lock(ERTS_RUNQ_IX(0));
+ ERTS_RUNQ_IX(0)->check_balance_reds = 0;
+ erts_smp_runq_unlock(ERTS_RUNQ_IX(0));
+}
+
+
#endif /* #ifdef ERTS_SMP */
Uint
@@ -3626,11 +4431,11 @@ static struct {
int limit;
int dec_shift;
int dec_mask;
- void (*check)(ErtsRunQueue *rq);
+ void (*check)(ErtsRunQueue *rq, Uint32 flags);
} wakeup_other;
static void
-wakeup_other_check(ErtsRunQueue *rq)
+wakeup_other_check(ErtsRunQueue *rq, Uint32 flags)
{
int wo_reds = rq->wakeup_other_reds;
if (wo_reds) {
@@ -3648,6 +4453,8 @@ wakeup_other_check(ErtsRunQueue *rq)
if (rq->wakeup_other > wakeup_other.limit) {
int empty_rqs =
erts_smp_atomic32_read_acqb(&no_empty_run_queues);
+ if (flags & ERTS_RUNQ_FLG_PROTECTED)
+ (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
if (empty_rqs != 0)
wake_scheduler_on_empty_runq(rq);
rq->wakeup_other = 0;
@@ -3692,18 +4499,21 @@ wakeup_other_set_limit(void)
}
static void
-wakeup_other_check_legacy(ErtsRunQueue *rq)
+wakeup_other_check_legacy(ErtsRunQueue *rq, Uint32 flags)
{
int wo_reds = rq->wakeup_other_reds;
if (wo_reds) {
- if (rq->len < 2) {
+ erts_aint32_t len = rq->len;
+ if (len < 2) {
rq->wakeup_other -= ERTS_WAKEUP_OTHER_DEC_LEGACY*wo_reds;
if (rq->wakeup_other < 0)
rq->wakeup_other = 0;
}
else if (rq->wakeup_other < wakeup_other.limit)
- rq->wakeup_other += rq->len*wo_reds + ERTS_WAKEUP_OTHER_FIXED_INC_LEGACY;
+ rq->wakeup_other += len*wo_reds + ERTS_WAKEUP_OTHER_FIXED_INC_LEGACY;
else {
+ if (flags & ERTS_RUNQ_FLG_PROTECTED)
+ (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
if (erts_smp_atomic32_read_acqb(&no_empty_run_queues) != 0) {
wake_scheduler_on_empty_runq(rq);
rq->wakeup_other = 0;
@@ -3850,7 +4660,7 @@ erts_sched_set_busy_wait_threshold(char *str)
return 0;
}
static void
-init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp)
+init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp)
{
awdp->sched_id = esdp ? (int) esdp->no : 0;
awdp->esdp = esdp;
@@ -3860,6 +4670,8 @@ init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp)
awdp->dd.thr_prgr = ERTS_THR_PRGR_VAL_WAITING;
awdp->dd.completed_callback = NULL;
awdp->dd.completed_arg = NULL;
+ awdp->later_op.first = NULL;
+ awdp->later_op.last = NULL;
#endif
#ifdef ERTS_USE_ASYNC_READY_Q
#ifdef ERTS_SMP
@@ -3868,12 +4680,32 @@ init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp)
#endif
awdp->async_ready.queue = NULL;
#endif
+#ifdef ERTS_SMP
+ if (!dawwp) {
+ awdp->delayed_wakeup.job = NULL;
+ awdp->delayed_wakeup.sched2jix = NULL;
+ awdp->delayed_wakeup.jix = -1;
+ }
+ else {
+ int i;
+ awdp->delayed_wakeup.job = (ErtsDelayedAuxWorkWakeupJob *) dawwp;
+ dawwp += sizeof(ErtsDelayedAuxWorkWakeupJob)*(erts_no_schedulers+1);
+ awdp->delayed_wakeup.sched2jix = (int *) dawwp;
+ awdp->delayed_wakeup.jix = -1;
+ for (i = 0; i <= erts_no_schedulers; i++)
+ awdp->delayed_wakeup.sched2jix[i] = -1;
+ }
+#endif
}
void
erts_init_scheduling(int no_schedulers, int no_schedulers_online)
{
int ix, n, no_ssi;
+ char *daww_ptr;
+#ifdef ERTS_SMP
+ size_t daww_sz;
+#endif
init_misc_op_list_alloc();
@@ -3903,7 +4735,6 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online)
ErtsRunQueue *rq = ERTS_RUNQ_IX(ix);
rq->ix = ix;
- erts_smp_atomic32_init_nob(&rq->info_flags, ERTS_RUNQ_IFLG_NONEMPTY);
/* make sure that the "extra" id correponds to the schedulers
* id if the esdp->no <-> ix+1 mapping change.
@@ -3914,7 +4745,7 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online)
rq->waiting = 0;
rq->woken = 0;
- rq->flags = 0;
+ ERTS_RUNQ_FLGS_INIT(rq, ERTS_RUNQ_FLG_NONEMPTY);
rq->check_balance_reds = ERTS_RUNQ_CALL_CHECK_BALANCE_REDS;
rq->full_reds_history_sum = 0;
for (rix = 0; rix < ERTS_FULL_REDS_HISTORY_SIZE; rix++) {
@@ -3928,19 +4759,14 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online)
rq->wakeup_other_reds = 0;
rq->halt_in_progress = 0;
- rq->procs.len = 0;
rq->procs.pending_exiters = NULL;
rq->procs.context_switches = 0;
rq->procs.reductions = 0;
for (pix = 0; pix < ERTS_NO_PROC_PRIO_LEVELS; pix++) {
- rq->procs.prio_info[pix].len = 0;
+ erts_smp_atomic32_init_nob(&rq->procs.prio_info[pix].len, 0);
rq->procs.prio_info[pix].max_len = 0;
rq->procs.prio_info[pix].reds = 0;
- rq->procs.prio_info[pix].migrate.limit.this = 0;
- rq->procs.prio_info[pix].migrate.limit.other = 0;
- ERTS_DBG_SET_INVALID_RUNQP(rq->procs.prio_info[pix].migrate.runq,
- 0x0);
if (pix < ERTS_NO_PROC_PRIO_LEVELS - 1) {
rq->procs.prio[pix].first = NULL;
rq->procs.prio[pix].last = NULL;
@@ -3949,14 +4775,10 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online)
rq->misc.start = NULL;
rq->misc.end = NULL;
- rq->misc.evac_runq = NULL;
- rq->ports.info.len = 0;
+ erts_smp_atomic32_init_nob(&rq->ports.info.len, 0);
rq->ports.info.max_len = 0;
rq->ports.info.reds = 0;
- rq->ports.info.migrate.limit.this = 0;
- rq->ports.info.migrate.limit.other = 0;
- rq->ports.info.migrate.runq = NULL;
rq->ports.start = NULL;
rq->ports.end = NULL;
}
@@ -4006,6 +4828,15 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online)
/* Create and initialize scheduler specific data */
+#ifdef ERTS_SMP
+ daww_sz = ERTS_ALC_CACHE_LINE_ALIGN_SIZE((sizeof(ErtsDelayedAuxWorkWakeupJob)
+ + sizeof(int))*(n+1));
+ daww_ptr = erts_alloc_permanent_cache_aligned(ERTS_ALC_T_SCHDLR_DATA,
+ daww_sz*n);
+#else
+ daww_ptr = NULL;
+#endif
+
erts_aligned_scheduler_data =
erts_alloc_permanent_cache_aligned(ERTS_ALC_T_SCHDLR_DATA,
n*sizeof(ErtsAlignedSchedulerData));
@@ -4040,7 +4871,10 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online)
esdp->run_queue = ERTS_RUNQ_IX(ix);
esdp->run_queue->scheduler = esdp;
- init_aux_work_data(&esdp->aux_work_data, esdp);
+ init_aux_work_data(&esdp->aux_work_data, esdp, daww_ptr);
+#ifdef ERTS_SMP
+ daww_ptr += daww_sz;
+#endif
init_sched_wall_time(&esdp->sched_wall_time);
}
@@ -4079,10 +4913,12 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online)
balance_info.prev_rise.reds = 0;
balance_info.n = 0;
+ init_migration_paths();
+
if (no_schedulers_online < no_schedulers) {
+ change_no_used_runqs(no_schedulers_online);
for (ix = no_schedulers_online; ix < erts_no_run_queues; ix++)
- evacuate_run_queue(ERTS_RUNQ_IX(ix),
- ERTS_RUNQ_IX(ix % no_schedulers_online));
+ suspend_run_queue(ERTS_RUNQ_IX(ix));
}
schdlr_sspnd.wait_curr_online = no_schedulers_online;
@@ -4145,91 +4981,197 @@ erts_get_scheduler_data(void)
#endif
-static int remove_proc_from_runq(ErtsRunQueue *rq, Process *p, int to_inactive);
+/*
+ * scheduler_out_process() return with c_rq locked.
+ */
+static ERTS_INLINE int
+schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p)
+{
+ erts_aint32_t a, e, n;
+ int res = 0;
+
+ a = state;
+
+ while (1) {
+ n = e = a;
+
+ ASSERT(a & ERTS_PSFLG_RUNNING);
+ ASSERT(!(a & ERTS_PSFLG_IN_RUNQ));
+
+ n &= ~ERTS_PSFLG_RUNNING;
+ if ((a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE)
+ n |= ERTS_PSFLG_IN_RUNQ;
+ a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e);
+ if (a == e)
+ break;
+ }
+
+ if (!(n & ERTS_PSFLG_IN_RUNQ)) {
+ if (erts_system_profile_flags.runnable_procs)
+ profile_runnable_proc(p, am_inactive);
+ }
+ else {
+ int prio = (int) (ERTS_PSFLG_PRIO_MASK & n);
+ ErtsRunQueue *runq = erts_get_runq_proc(p);
+
+ ASSERT(!(n & ERTS_PSFLG_SUSPENDED));
+
+#ifdef ERTS_SMP
+ if (!(ERTS_PSFLG_BOUND & n)) {
+ ErtsRunQueue *new_runq = erts_check_emigration_need(runq, prio);
+ if (new_runq) {
+ RUNQ_SET_RQ(&p->run_queue, new_runq);
+ runq = new_runq;
+ }
+ }
+#endif
+ ASSERT(runq);
+ res = 1;
+
+ erts_smp_runq_lock(runq);
+
+ /* Enqueue the process */
+ enqueue_process(runq, prio, p);
+
+ if (runq == c_rq)
+ return res;
+ erts_smp_runq_unlock(runq);
+ smp_notify_inc_runq(runq);
+ }
+ erts_smp_runq_lock(c_rq);
+ return res;
+}
static ERTS_INLINE void
-suspend_process(ErtsRunQueue *rq, Process *p)
+add2runq(Process *p, erts_aint32_t state)
{
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
- ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
- p->rcount++; /* count number of suspend */
-#ifdef ERTS_SMP
- ASSERT(!(p->runq_flags & ERTS_PROC_RUNQ_FLG_RUNNING)
- || p == erts_get_current_process());
- ASSERT(p->status != P_RUNNING
- || p->runq_flags & ERTS_PROC_RUNQ_FLG_RUNNING);
- if (p->status_flags & ERTS_PROC_SFLG_PENDADD2SCHEDQ)
- goto runable;
-#endif
- switch(p->status) {
- case P_SUSPENDED:
- break;
- case P_RUNABLE:
+ int prio = (int) (ERTS_PSFLG_PRIO_MASK & state);
+ ErtsRunQueue *runq = erts_get_runq_proc(p);
+
#ifdef ERTS_SMP
- runable:
- if (!ERTS_PROC_PENDING_EXIT(p))
+ if (!(ERTS_PSFLG_BOUND & state)) {
+ ErtsRunQueue *new_runq = erts_check_emigration_need(runq, prio);
+ if (new_runq) {
+ RUNQ_SET_RQ(&p->run_queue, new_runq);
+ runq = new_runq;
+ }
+ }
#endif
- remove_proc_from_runq(rq, p, 1);
- /* else:
- * leave process in schedq so it will discover the pending exit
- */
- p->rstatus = P_RUNABLE; /* wakeup as runnable */
- break;
- case P_RUNNING:
- p->rstatus = P_RUNABLE; /* wakeup as runnable */
- break;
- case P_WAITING:
- p->rstatus = P_WAITING; /* wakeup as waiting */
- break;
- case P_EXITING:
- return; /* ignore this */
- case P_GARBING:
- case P_FREE:
- erl_exit(1, "bad state in suspend_process()\n");
+ ASSERT(runq);
+
+ erts_smp_runq_lock(runq);
+
+ /* Enqueue the process */
+ enqueue_process(runq, prio, p);
+
+ erts_smp_runq_unlock(runq);
+ smp_notify_inc_runq(runq);
+
+}
+
+static ERTS_INLINE void
+schedule_process(Process *p, erts_aint32_t state, int active_enq)
+{
+ erts_aint32_t a = state, n;
+
+ while (1) {
+ erts_aint32_t e;
+ n = e = a;
+
+ if (a & ERTS_PSFLG_FREE)
+ return; /* We don't want to schedule free processes... */
+ n |= ERTS_PSFLG_ACTIVE;
+ if (!(a & (ERTS_PSFLG_SUSPENDED|ERTS_PSFLG_RUNNING)))
+ n |= ERTS_PSFLG_IN_RUNQ;
+ a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e);
+ if (a == e)
+ break;
+ if (!active_enq && (a & ERTS_PSFLG_ACTIVE))
+ return; /* Someone else activated process ... */
}
- if ((erts_system_profile_flags.runnable_procs) && (p->rcount == 1) && (p->status != P_WAITING)) {
- profile_runnable_proc(p, am_inactive);
+ if (erts_system_profile_flags.runnable_procs
+ && !(a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED))) {
+ profile_runnable_proc(p, am_active);
}
- p->status = P_SUSPENDED;
-
+ if ((n & ERTS_PSFLG_IN_RUNQ) && !(a & ERTS_PSFLG_IN_RUNQ))
+ add2runq(p, n);
}
-static ERTS_INLINE void
-resume_process(Process *p)
+void
+erts_schedule_process(Process *p, erts_aint32_t state)
{
- Uint32 *statusp;
+ schedule_process(p, state, 0);
+}
+
+static ERTS_INLINE int
+suspend_process(Process *c_p, Process *p)
+{
+ erts_aint32_t state = erts_smp_atomic32_read_acqb(&p->state);
+ int suspended = 0;
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
- switch (p->status) {
- case P_SUSPENDED:
- statusp = &p->status;
- break;
- case P_GARBING:
- if (p->gcstatus == P_SUSPENDED) {
- statusp = &p->gcstatus;
- break;
+
+ if ((state & ERTS_PSFLG_SUSPENDED))
+ suspended = -1;
+ else {
+ if (c_p == p) {
+ state = erts_smp_atomic32_read_bor_relb(&p->state,
+ ERTS_PSFLG_SUSPENDED);
+ state |= ERTS_PSFLG_SUSPENDED;
+ ASSERT(state & ERTS_PSFLG_RUNNING);
+ suspended = 1;
}
- /* Fall through */
- default:
- return;
+ else {
+ while (!(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_EXITING))) {
+ erts_aint32_t e, n;
+ n = e = state;
+ n |= ERTS_PSFLG_SUSPENDED;
+ state = erts_smp_atomic32_cmpxchg_relb(&p->state, n, e);
+ if (state == e) {
+ state = n;
+ suspended = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if (state & ERTS_PSFLG_SUSPENDED) {
+
+ ASSERT(!(ERTS_PSFLG_RUNNING & state)
+ || p == erts_get_current_process());
+
+ if (erts_system_profile_flags.runnable_procs
+ && (p->rcount == 0)
+ && (state & ERTS_PSFLG_ACTIVE)) {
+ profile_runnable_proc(p, am_inactive);
+ }
+
+ p->rcount++; /* count number of suspend */
}
+ return suspended;
+}
+
+static ERTS_INLINE void
+resume_process(Process *p)
+{
+ erts_aint32_t state;
+ ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
ASSERT(p->rcount > 0);
if (--p->rcount > 0) /* multiple suspend */
return;
- switch(p->rstatus) {
- case P_RUNABLE:
- erts_add_to_runq(p);
- break;
- case P_WAITING:
- *statusp = P_WAITING;
- break;
- default:
- erl_exit(1, "bad state in resume_process()\n");
+
+ state = erts_smp_atomic32_read_band_mb(&p->state, ~ERTS_PSFLG_SUSPENDED);
+ state &= ~ERTS_PSFLG_SUSPENDED;
+ if ((state & (ERTS_PSFLG_EXITING
+ | ERTS_PSFLG_ACTIVE
+ | ERTS_PSFLG_IN_RUNQ
+ | ERTS_PSFLG_RUNNING)) == ERTS_PSFLG_ACTIVE) {
+ schedule_process(p, state, 1);
}
- p->rstatus = P_FREE;
}
int
@@ -4353,6 +5295,7 @@ suspend_scheduler(ErtsSchedulerData *esdp)
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:
@@ -4367,6 +5310,8 @@ suspend_scheduler(ErtsSchedulerData *esdp)
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);
@@ -4430,19 +5375,28 @@ suspend_scheduler(ErtsSchedulerData *esdp)
erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
while (1) {
+ 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) {
+ if (aux_work|qmask) {
if (!thr_prgr_active) {
erts_thr_progress_active(esdp, thr_prgr_active = 1);
sched_wall_time_change(esdp, 1);
}
- aux_work = handle_aux_work(&esdp->aux_work_data,
- aux_work,
- 1);
+ if (aux_work)
+ 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 (!aux_work) {
@@ -4512,56 +5466,11 @@ suspend_scheduler(ErtsSchedulerData *esdp)
erts_smp_runq_lock(esdp->run_queue);
non_empty_runq(esdp->run_queue);
+ schedule_bound_processes(esdp->run_queue, &sbp);
+
erts_sched_check_cpu_bind_post_suspend(esdp);
}
-#define ERTS_RUNQ_RESET_SUSPEND_INFO(RQ, DBG_ID) \
-do { \
- int pix__; \
- (RQ)->misc.evac_runq = NULL; \
- (RQ)->ports.info.migrate.runq = NULL; \
- (RQ)->flags &= ~(ERTS_RUNQ_FLGS_IMMIGRATE_QMASK \
- | ERTS_RUNQ_FLGS_EMIGRATE_QMASK \
- | ERTS_RUNQ_FLGS_EVACUATE_QMASK \
- | ERTS_RUNQ_FLG_SUSPENDED); \
- (RQ)->flags |= (ERTS_RUNQ_FLG_OUT_OF_WORK \
- | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK); \
- (RQ)->check_balance_reds = ERTS_RUNQ_CALL_CHECK_BALANCE_REDS; \
- erts_smp_atomic32_read_band_nob(&(RQ)->info_flags, ~ERTS_RUNQ_IFLG_SUSPENDED);\
- 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; \
- ERTS_DBG_SET_INVALID_RUNQP((RQ)->procs.prio_info[pix__].migrate.runq,\
- (DBG_ID)); \
- } \
- (RQ)->ports.info.max_len = 0; \
- (RQ)->ports.info.reds = 0; \
-} while (0)
-
-#define ERTS_RUNQ_RESET_MIGRATION_PATHS__(RQ) \
-do { \
- ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked((RQ))); \
- (RQ)->misc.evac_runq = NULL; \
- (RQ)->ports.info.migrate.runq = NULL; \
- (RQ)->flags &= ~(ERTS_RUNQ_FLGS_IMMIGRATE_QMASK \
- | ERTS_RUNQ_FLGS_EMIGRATE_QMASK \
- | ERTS_RUNQ_FLGS_EVACUATE_QMASK); \
-} while (0)
-
-#ifdef DEBUG
-#define ERTS_RUNQ_RESET_MIGRATION_PATHS(RQ, DBG_ID) \
-do { \
- int pix__; \
- ERTS_RUNQ_RESET_MIGRATION_PATHS__((RQ)); \
- for (pix__ = 0; pix__ < ERTS_NO_PROC_PRIO_LEVELS; pix__++) \
- ERTS_DBG_SET_INVALID_RUNQP((RQ)->procs.prio_info[pix__].migrate.runq,\
- (DBG_ID)); \
-} while (0)
-#else
-#define ERTS_RUNQ_RESET_MIGRATION_PATHS(RQ, DBG_ID) \
- ERTS_RUNQ_RESET_MIGRATION_PATHS__((RQ))
-#endif
-
ErtsSchedSuspendResult
erts_schedulers_state(Uint *total,
Uint *online,
@@ -4631,27 +5540,13 @@ erts_set_schedulers_online(Process *p,
have_unlocked_plocks = 1;
erts_smp_proc_unlock(p, plocks);
}
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
- erts_smp_mtx_lock(&balance_info.update_mtx);
- for (ix = online; ix < no; ix++) {
- ErtsRunQueue *rq = ERTS_RUNQ_IX(ix);
- erts_smp_runq_lock(rq);
- ERTS_RUNQ_RESET_SUSPEND_INFO(rq, 0x5);
- erts_smp_runq_unlock(rq);
- scheduler_ix_resume_wake(ix);
- }
- /*
- * Spread evacuation paths among all online
- * run queues.
- */
- for (ix = no; ix < erts_no_run_queues; ix++) {
- ErtsRunQueue *from_rq = ERTS_RUNQ_IX(ix);
- ErtsRunQueue *to_rq = ERTS_RUNQ_IX(ix % no);
- evacuate_run_queue(from_rq, to_rq);
- }
- set_no_used_runqs(no);
- erts_smp_mtx_unlock(&balance_info.update_mtx);
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
+ 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));
}
res = ERTS_SCHDLR_SSPND_DONE;
}
@@ -4678,25 +5573,11 @@ erts_set_schedulers_online(Process *p,
have_unlocked_plocks = 1;
erts_smp_proc_unlock(p, plocks);
}
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
- erts_smp_mtx_lock(&balance_info.update_mtx);
-
- for (ix = 0; ix < online; ix++) {
- ErtsRunQueue *rq = ERTS_RUNQ_IX(ix);
- erts_smp_runq_lock(rq);
- ERTS_RUNQ_RESET_MIGRATION_PATHS(rq, 0x6);
- erts_smp_runq_unlock(rq);
- }
- /*
- * Evacutation order important! Newly suspended run queues
- * has to be evacuated last.
- */
- for (ix = erts_no_run_queues-1; ix >= no; ix--)
- evacuate_run_queue(ERTS_RUNQ_IX(ix),
- ERTS_RUNQ_IX(ix % no));
- set_no_used_runqs(no);
- erts_smp_mtx_unlock(&balance_info.update_mtx);
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
+
+ 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, 0);
@@ -4793,25 +5674,14 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all)
schdlr_sspnd.msb.wait_active = 2;
}
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
- erts_smp_mtx_lock(&balance_info.update_mtx);
- set_no_used_runqs(1);
- for (ix = 0; ix < online; ix++) {
+ change_no_used_runqs(1);
+ for (ix = 1; ix < erts_no_run_queues; ix++)
+ suspend_run_queue(ERTS_RUNQ_IX(ix));
+
+ for (ix = 1; ix < online; ix++) {
ErtsRunQueue *rq = ERTS_RUNQ_IX(ix);
- erts_smp_runq_lock(rq);
- ASSERT(!(rq->flags & ERTS_RUNQ_FLG_SUSPENDED));
- ERTS_RUNQ_RESET_MIGRATION_PATHS(rq, 0x7);
- erts_smp_runq_unlock(rq);
+ wake_scheduler(rq, 0);
}
- /*
- * Evacuate all activities in all other run queues
- * into the first run queue. Note order is important,
- * online run queues has to be evacuated last.
- */
- for (ix = erts_no_run_queues-1; ix >= 1; ix--)
- evacuate_run_queue(ERTS_RUNQ_IX(ix), ERTS_RUNQ_IX(0));
- erts_smp_mtx_unlock(&balance_info.update_mtx);
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
if (erts_smp_atomic32_read_nob(&schdlr_sspnd.active)
!= schdlr_sspnd.msb.wait_active) {
@@ -4855,15 +5725,6 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all)
plp = proclist_create(p);
plp->next = schdlr_sspnd.msb.procs;
schdlr_sspnd.msb.procs = plp;
-#ifdef DEBUG
- ERTS_FOREACH_RUNQ(srq,
- {
- if (srq != ERTS_RUNQ_IX(0)) {
- ASSERT(ERTS_EMPTY_RUNQ(srq));
- ASSERT(srq->flags & ERTS_RUNQ_FLG_SUSPENDED);
- }
- });
-#endif
ASSERT(p->scheduler_data);
}
}
@@ -4895,27 +5756,6 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all)
res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED;
else {
ERTS_SCHDLR_SSPND_CHNG_SET(ERTS_SCHDLR_SSPND_CHNG_MSB, 0);
-#ifdef DEBUG
- ERTS_FOREACH_RUNQ(rq,
- {
- if (rq != p->scheduler_data->run_queue) {
- if (!ERTS_EMPTY_RUNQ(rq)) {
- Process *rp;
- int pix;
- ASSERT(rq->ports.info.len == 0);
- for (pix = 0; pix < ERTS_NO_PROC_PRIO_LEVELS; pix++) {
- for (rp = rq->procs.prio[pix].first;
- rp;
- rp = rp->next) {
- ASSERT(rp->bound_runq);
- }
- }
- }
-
- ASSERT(rq->flags & ERTS_RUNQ_FLG_SUSPENDED);
- }
- });
-#endif
p->flags &= ~F_HAVE_BLCKD_MSCHED;
schdlr_sspnd.msb.ongoing = 0;
if (schdlr_sspnd.online == 1) {
@@ -4925,35 +5765,19 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all)
}
else {
int online = schdlr_sspnd.online;
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
if (plocks) {
have_unlocked_plocks = 1;
erts_smp_proc_unlock(p, plocks);
}
- erts_smp_mtx_lock(&balance_info.update_mtx);
+
+ change_no_used_runqs(online);
/* Resume all online run queues */
- for (ix = 1; ix < online; ix++) {
- ErtsRunQueue *rq = ERTS_RUNQ_IX(ix);
- erts_smp_runq_lock(rq);
- ERTS_RUNQ_RESET_SUSPEND_INFO(rq, 0x4);
- erts_smp_runq_unlock(rq);
- scheduler_ix_resume_wake(ix);
- }
+ for (ix = 1; ix < online; ix++)
+ resume_run_queue(ERTS_RUNQ_IX(ix));
- /* Spread evacuation paths among all online run queues */
for (ix = online; ix < erts_no_run_queues; ix++)
- evacuate_run_queue(ERTS_RUNQ_IX(ix),
- ERTS_RUNQ_IX(ix % online));
-
- set_no_used_runqs(online);
- /* Make sure that we balance soon... */
- balance_info.forced_check_balance = 1;
- erts_smp_runq_lock(ERTS_RUNQ_IX(0));
- ERTS_RUNQ_IX(0)->check_balance_reds = 0;
- erts_smp_runq_unlock(ERTS_RUNQ_IX(0));
- erts_smp_mtx_unlock(&balance_info.update_mtx);
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
+ suspend_run_queue(ERTS_RUNQ_IX(ix));
}
res = ERTS_SCHDLR_SSPND_DONE;
}
@@ -5260,10 +6084,7 @@ handle_pend_sync_suspend(Process *suspendee,
if (suspender) {
ASSERT(is_nil(suspender->suspendee));
if (suspendee_alive) {
- ErtsRunQueue *rq = erts_get_runq_proc(suspendee);
- erts_smp_runq_lock(rq);
- suspend_process(rq, suspendee);
- erts_smp_runq_unlock(rq);
+ erts_suspend(suspendee, suspendee_locks, NULL);
suspender->suspendee = suspendee->id;
}
/* suspender is suspended waiting for suspendee to suspend;
@@ -5306,10 +6127,9 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks,
resume_process(rp);
}
else {
- ErtsRunQueue *cp_rq, *rp_rq;
rp = erts_pid2proc(c_p, c_p_locks|ERTS_PROC_LOCK_STATUS,
- pid, ERTS_PROC_LOCK_STATUS);
+ pid, pid_locks|ERTS_PROC_LOCK_STATUS);
if (!rp) {
c_p->flags &= ~F_P2PNR_RESCHED;
@@ -5318,58 +6138,36 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks,
ASSERT(!(c_p->flags & F_P2PNR_RESCHED));
- cp_rq = erts_get_runq_proc(c_p);
- rp_rq = erts_get_runq_proc(rp);
- erts_smp_runqs_lock(cp_rq, rp_rq);
- if (rp->runq_flags & ERTS_PROC_RUNQ_FLG_RUNNING) {
- running:
- /* Phiu... */
-
- /*
- * If we got pending suspenders and suspend ourselves waiting
- * to suspend another process we might deadlock.
- * In this case we have to yield, be suspended by
- * someone else and then do it all over again.
- */
- if (!c_p->pending_suspenders) {
- /* Mark rp pending for suspend by c_p */
- add_pend_suspend(rp, c_p->id, handle_pend_sync_suspend);
- ASSERT(is_nil(c_p->suspendee));
-
- /* Suspend c_p; when rp is suspended c_p will be resumed. */
- suspend_process(cp_rq, c_p);
- c_p->flags |= F_P2PNR_RESCHED;
- }
- /* Yield (caller is assumed to yield immediately in bif). */
- erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
- rp = ERTS_PROC_LOCK_BUSY;
+ if (suspend) {
+ if (suspend_process(c_p, rp))
+ goto done;
}
else {
- ErtsProcLocks need_locks = pid_locks & ~ERTS_PROC_LOCK_STATUS;
- if (need_locks && erts_smp_proc_trylock(rp, need_locks) == EBUSY) {
- erts_smp_runqs_unlock(cp_rq, rp_rq);
- 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);
- if (!rp)
- goto done;
- /* run-queues may have changed */
- cp_rq = erts_get_runq_proc(c_p);
- rp_rq = erts_get_runq_proc(rp);
- erts_smp_runqs_lock(cp_rq, rp_rq);
- if (rp->runq_flags & ERTS_PROC_RUNQ_FLG_RUNNING) {
- /* Ahh... */
- erts_smp_proc_unlock(rp,
- pid_locks & ~ERTS_PROC_LOCK_STATUS);
- goto running;
- }
- }
+ if (!(ERTS_PSFLG_RUNNING & erts_smp_atomic32_read_acqb(&rp->state)))
+ goto done;
- /* rp is not running and we got the locks we want... */
- if (suspend)
- suspend_process(rp_rq, rp);
}
- erts_smp_runqs_unlock(cp_rq, rp_rq);
+
+ /* Other process running */
+
+ /*
+ * If we got pending suspenders and suspend ourselves waiting
+ * to suspend another process we might deadlock.
+ * In this case we have to yield, be suspended by
+ * someone else and then do it all over again.
+ */
+ if (!c_p->pending_suspenders) {
+ /* Mark rp pending for suspend by c_p */
+ add_pend_suspend(rp, c_p->id, handle_pend_sync_suspend);
+ ASSERT(is_nil(c_p->suspendee));
+
+ /* Suspend c_p; when rp is suspended c_p will be resumed. */
+ suspend_process(c_p, c_p);
+ c_p->flags |= F_P2PNR_RESCHED;
+ }
+ /* Yield (caller is assumed to yield immediately in bif). */
+ erts_smp_proc_unlock(rp, pid_locks|ERTS_PROC_LOCK_STATUS);
+ rp = ERTS_PROC_LOCK_BUSY;
}
done:
@@ -5426,36 +6224,26 @@ erts_pid2proc_nropt(Process *c_p, ErtsProcLocks c_p_locks,
return erts_pid2proc_not_running(c_p, c_p_locks, pid, pid_locks);
}
-static ERTS_INLINE void
-do_bif_suspend_process(ErtsSuspendMonitor *smon,
- Process *suspendee,
- ErtsRunQueue *locked_runq)
+static ERTS_INLINE int
+do_bif_suspend_process(Process *c_p,
+ ErtsSuspendMonitor *smon,
+ Process *suspendee)
{
ASSERT(suspendee);
- ASSERT(!suspendee->is_exiting);
+ ASSERT(!ERTS_PROC_IS_EXITING(suspendee));
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS
& erts_proc_lc_my_proc_locks(suspendee));
if (smon) {
if (!smon->active) {
- ErtsRunQueue *rq;
-
- if (locked_runq)
- rq = locked_runq;
- else {
- rq = erts_get_runq_proc(suspendee);
- erts_smp_runq_lock(rq);
- }
-
- suspend_process(rq, suspendee);
-
- if (!locked_runq)
- erts_smp_runq_unlock(rq);
+ if (!suspend_process(c_p, suspendee))
+ return 0;
}
smon->active += smon->pending;
ASSERT(smon->active);
smon->pending = 0;
+ return 1;
}
-
+ return 0;
}
static void
@@ -5478,10 +6266,17 @@ handle_pend_bif_sync_suspend(Process *suspendee,
erts_delete_suspend_monitor(&suspender->suspend_monitors,
suspendee->id);
else {
+#ifdef DEBUG
+ int res;
+#endif
ErtsSuspendMonitor *smon;
smon = erts_lookup_suspend_monitor(suspender->suspend_monitors,
suspendee->id);
- do_bif_suspend_process(smon, suspendee, NULL);
+#ifdef DEBUG
+ res =
+#endif
+ do_bif_suspend_process(suspendee, smon, suspendee);
+ ASSERT(!smon || res != 0);
suspender->suspendee = suspendee->id;
}
/* suspender is suspended waiting for suspendee to suspend;
@@ -5513,10 +6308,17 @@ handle_pend_bif_async_suspend(Process *suspendee,
erts_delete_suspend_monitor(&suspender->suspend_monitors,
suspendee->id);
else {
+#ifdef DEBUG
+ int res;
+#endif
ErtsSuspendMonitor *smon;
smon = erts_lookup_suspend_monitor(suspender->suspend_monitors,
suspendee->id);
- do_bif_suspend_process(smon, suspendee, NULL);
+#ifdef DEBUG
+ res =
+#endif
+ do_bif_suspend_process(suspendee, smon, suspendee);
+ ASSERT(!smon || res != 0);
}
erts_smp_proc_unlock(suspender, ERTS_PROC_LOCK_LINK);
}
@@ -5601,7 +6403,8 @@ suspend_process_2(BIF_ALIST_2)
/* This is really a piece of cake without SMP support... */
if (!smon->active) {
- suspend_process(ERTS_RUNQ_IX(0), suspendee);
+ erts_smp_atomic32_read_bor_nob(&suspendee->state, ERTS_PSFLG_SUSPENDED);
+ suspend_process(BIF_P, suspendee);
smon->active++;
res = am_true;
}
@@ -5644,21 +6447,15 @@ suspend_process_2(BIF_ALIST_2)
if (smon->pending && unless_suspending)
res = am_false;
else {
- ErtsRunQueue *rq;
if (smon->pending == INT_MAX)
goto system_limit;
smon->pending++;
- rq = erts_get_runq_proc(suspendee);
- erts_smp_runq_lock(rq);
- if (suspendee->runq_flags & ERTS_PROC_RUNQ_FLG_RUNNING)
+ if (!do_bif_suspend_process(BIF_P, smon, suspendee))
add_pend_suspend(suspendee,
BIF_P->id,
handle_pend_bif_async_suspend);
- else
- do_bif_suspend_process(smon, suspendee, rq);
- erts_smp_runq_unlock(rq);
res = am_true;
}
@@ -5695,7 +6492,6 @@ suspend_process_2(BIF_ALIST_2)
/* done */
}
else {
- ErtsRunQueue *cp_rq, *s_rq;
/* We haven't got any active suspends on the suspendee */
/*
@@ -5712,12 +6508,7 @@ suspend_process_2(BIF_ALIST_2)
if (!unless_suspending || smon->pending == 0)
smon->pending++;
- cp_rq = erts_get_runq_proc(BIF_P);
- s_rq = erts_get_runq_proc(suspendee);
- erts_smp_runqs_lock(cp_rq, s_rq);
- if (!(suspendee->runq_flags & ERTS_PROC_RUNQ_FLG_RUNNING)) {
- do_bif_suspend_process(smon, suspendee, s_rq);
- erts_smp_runqs_unlock(cp_rq, s_rq);
+ if (do_bif_suspend_process(BIF_P, smon, suspendee)) {
res = (!unless_suspending || smon->active == 1
? am_true
: am_false);
@@ -5737,8 +6528,7 @@ suspend_process_2(BIF_ALIST_2)
* This time with BIF_P->suspendee == BIF_ARG_1 (see
* above).
*/
- suspend_process(cp_rq, BIF_P);
- erts_smp_runqs_unlock(cp_rq, s_rq);
+ suspend_process(BIF_P, BIF_P);
goto yield;
}
}
@@ -5746,9 +6536,15 @@ suspend_process_2(BIF_ALIST_2)
}
#endif /* ERTS_SMP */
-
- ASSERT(suspendee->status == P_SUSPENDED || (asynchronous && smon->pending));
- ASSERT(suspendee->status == P_SUSPENDED || !smon->active);
+#ifdef DEBUG
+ {
+ erts_aint32_t state = erts_smp_atomic32_read_acqb(&suspendee->state);
+ ASSERT((state & ERTS_PSFLG_SUSPENDED)
+ || (asynchronous && smon->pending));
+ ASSERT((state & ERTS_PSFLG_SUSPENDED)
+ || !smon->active);
+ }
+#endif
erts_smp_proc_unlock(suspendee, ERTS_PROC_LOCK_STATUS);
erts_smp_proc_unlock(BIF_P, xlocks);
@@ -5843,9 +6639,8 @@ resume_process_1(BIF_ALIST_1)
if (!suspendee)
goto no_suspendee;
- ASSERT(suspendee->status == P_SUSPENDED
- || (suspendee->status == P_GARBING
- && suspendee->gcstatus == P_SUSPENDED));
+ ASSERT(ERTS_PSFLG_SUSPENDED
+ & erts_smp_atomic32_read_nob(&suspendee->state));
resume_process(suspendee);
erts_smp_proc_unlock(suspendee, ERTS_PROC_LOCK_STATUS);
@@ -5874,449 +6669,46 @@ erts_run_queues_len(Uint *qlen)
Uint len = 0;
ERTS_ATOMIC_FOREACH_RUNQ(rq,
{
- if (qlen)
- qlen[i++] = rq->procs.len;
- len += rq->procs.len;
+ Sint pqlen = 0;
+ int pix;
+ for (pix = 0; pix < ERTS_NO_PROC_PRIO_LEVELS; pix++)
+ pqlen += RUNQ_READ_LEN(&rq->procs.prio_info[pix].len);
+
+ if (pqlen < 0)
+ pqlen = 0;
+ if (qlen)
+ qlen[i++] = pqlen;
+ len += pqlen;
}
);
return len;
}
-#ifdef HARDDEBUG_RUNQS
-static void
-check_procs_runq(ErtsRunQueue *runq, Process *p_in_q, Process *p_not_in_q)
-{
- int len[ERTS_NO_PROC_PRIO_LEVELS] = {0};
- int tot_len;
- int prioq, prio;
- int found_p_in_q;
- Process *p, *prevp;
-
- found_p_in_q = 0;
- for (prioq = 0; prioq < ERTS_NO_PROC_PRIO_LEVELS - 1; prioq++) {
- prevp = NULL;
- for (p = runq->procs.prio[prioq].first; p; p = p->next) {
- ASSERT(p != p_not_in_q);
- if (p == p_in_q)
- found_p_in_q = 1;
- switch (p->prio) {
- case PRIORITY_MAX:
- case PRIORITY_HIGH:
- case PRIORITY_NORMAL:
- ASSERT(prioq == p->prio);
- break;
- case PRIORITY_LOW:
- ASSERT(prioq == PRIORITY_NORMAL);
- break;
- default:
- ASSERT(!"Bad prio on process");
- }
- len[p->prio]++;
- ASSERT(prevp == p->prev);
- if (p->prev) {
- ASSERT(p->prev->next == p);
- }
- else {
- ASSERT(runq->procs.prio[prioq].first == p);
- }
- if (p->next) {
- ASSERT(p->next->prev == p);
- }
- else {
- ASSERT(runq->procs.prio[prioq].last == p);
- }
- ASSERT(p->run_queue == runq);
- prevp = p;
- }
- }
-
- ASSERT(!p_in_q || found_p_in_q);
-
- tot_len = 0;
- for (prio = 0; prio < ERTS_NO_PROC_PRIO_LEVELS; prio++) {
- ASSERT(len[prio] == runq->procs.prio_info[prio].len);
- if (len[prio]) {
- ASSERT(runq->flags & (1 << prio));
- }
- else {
- ASSERT(!(runq->flags & (1 << prio)));
- }
- tot_len += len[prio];
- }
- ASSERT(runq->procs.len == tot_len);
-}
-# define ERTS_DBG_CHK_PROCS_RUNQ(RQ) check_procs_runq((RQ), NULL, NULL)
-# define ERTS_DBG_CHK_PROCS_RUNQ_PROC(RQ, P) check_procs_runq((RQ), (P), NULL)
-# define ERTS_DBG_CHK_PROCS_RUNQ_NOPROC(RQ, P) check_procs_runq((RQ), NULL, (P))
-#else
-# define ERTS_DBG_CHK_PROCS_RUNQ(RQ)
-# define ERTS_DBG_CHK_PROCS_RUNQ_PROC(RQ, P)
-# define ERTS_DBG_CHK_PROCS_RUNQ_NOPROC(RQ, P)
-#endif
-
-
-static ERTS_INLINE void
-enqueue_process(ErtsRunQueue *runq, Process *p)
-{
- ErtsRunPrioQueue *rpq;
- ErtsRunQueueInfo *rqi;
-
- ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
-
- ASSERT(p->bound_runq || !(runq->flags & ERTS_RUNQ_FLG_SUSPENDED));
-
- rqi = &runq->procs.prio_info[p->prio];
- rqi->len++;
- if (rqi->max_len < rqi->len)
- rqi->max_len = rqi->len;
-
- runq->procs.len++;
- runq->len++;
- if (runq->max_len < runq->len)
- runq->max_len = runq->len;
-
- runq->flags |= (1 << p->prio);
-
- rpq = (p->prio == PRIORITY_LOW
- ? &runq->procs.prio[PRIORITY_NORMAL]
- : &runq->procs.prio[p->prio]);
-
- p->next = NULL;
- p->prev = rpq->last;
- if (rpq->last)
- rpq->last->next = p;
- else
- rpq->first = p;
- rpq->last = p;
-
- switch (p->status) {
- case P_EXITING:
- break;
- case P_GARBING:
- p->gcstatus = P_RUNABLE;
- break;
- default:
- p->status = P_RUNABLE;
- break;
- }
-
-#ifdef ERTS_SMP
- p->status_flags |= ERTS_PROC_SFLG_INRUNQ;
-#endif
-
- ERTS_DBG_CHK_PROCS_RUNQ_PROC(runq, p);
-}
-
-
-static ERTS_INLINE int
-dequeue_process(ErtsRunQueue *runq, Process *p)
-{
- ErtsRunPrioQueue *rpq;
- int res = 1;
-
- ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
-
- ERTS_DBG_CHK_PROCS_RUNQ(runq);
-
- rpq = &runq->procs.prio[p->prio == PRIORITY_LOW ? PRIORITY_NORMAL : p->prio];
- if (p->prev) {
- p->prev->next = p->next;
- }
- else if (rpq->first == p) {
- rpq->first = p->next;
- }
- else {
- res = 0;
- }
- if (p->next) {
- p->next->prev = p->prev;
- }
- else if (rpq->last == p) {
- rpq->last = p->prev;
- }
- else {
- ASSERT(res == 0);
- }
-
- if (res) {
-
- if (--runq->procs.prio_info[p->prio].len == 0)
- runq->flags &= ~(1 << p->prio);
- runq->procs.len--;
- runq->len--;
-
-#ifdef ERTS_SMP
- p->status_flags &= ~ERTS_PROC_SFLG_INRUNQ;
-#endif
- }
-
- ERTS_DBG_CHK_PROCS_RUNQ_NOPROC(runq, p);
- return res;
-}
-
-/* schedule a process */
-static ERTS_INLINE ErtsRunQueue *
-internal_add_to_runq(ErtsRunQueue *runq, Process *p)
-{
- Uint32 prev_status = p->status;
- ErtsRunQueue *add_runq;
-#ifdef ERTS_SMP
-
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
- ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
-
- if (p->status_flags & ERTS_PROC_SFLG_INRUNQ)
- return NULL;
- else if (p->runq_flags & ERTS_PROC_RUNQ_FLG_RUNNING) {
- ASSERT(ERTS_PROC_IS_EXITING(p) || p->rcount == 0);
- ERTS_DBG_CHK_PROCS_RUNQ_NOPROC(runq, p);
- p->status_flags |= ERTS_PROC_SFLG_PENDADD2SCHEDQ;
- return NULL;
- }
- ASSERT(!p->scheduler_data);
-#endif
-
- ERTS_DBG_CHK_PROCS_RUNQ_NOPROC(runq, p);
-#ifndef ERTS_SMP
- /* Never schedule a suspended process (ok in smp case) */
- ASSERT(ERTS_PROC_IS_EXITING(p) || p->rcount == 0);
- add_runq = runq;
-#else
- ASSERT(!p->bound_runq || p->bound_runq == p->run_queue);
- if (p->bound_runq) {
- if (p->bound_runq == runq)
- add_runq = runq;
- else {
- add_runq = p->bound_runq;
- erts_smp_xrunq_lock(runq, add_runq);
- }
- }
- else {
- add_runq = erts_check_emigration_need(runq, p->prio);
- if (!add_runq)
- add_runq = runq;
- else /* Process emigrated */
- p->run_queue = add_runq;
- }
-#endif
-
- /* Enqueue the process */
- enqueue_process(add_runq, p);
-
- if ((erts_system_profile_flags.runnable_procs)
- && (prev_status == P_WAITING
- || prev_status == P_SUSPENDED)) {
- profile_runnable_proc(p, am_active);
- }
-
- if (add_runq != runq)
- erts_smp_runq_unlock(add_runq);
-
- return add_runq;
-}
-
-
-void
-erts_add_to_runq(Process *p)
-{
- ErtsRunQueue *notify_runq;
- ErtsRunQueue *runq = erts_get_runq_proc(p);
- erts_smp_runq_lock(runq);
- notify_runq = internal_add_to_runq(runq, p);
- erts_smp_runq_unlock(runq);
- smp_notify_inc_runq(notify_runq);
-
-}
-
-/* Possibly remove a scheduled process we need to suspend */
-
-static int
-remove_proc_from_runq(ErtsRunQueue *rq, Process *p, int to_inactive)
-{
- int res;
-
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
-
-#ifdef ERTS_SMP
- if (p->status_flags & ERTS_PROC_SFLG_PENDADD2SCHEDQ) {
- p->status_flags &= ~ERTS_PROC_SFLG_PENDADD2SCHEDQ;
- ASSERT(!remove_proc_from_runq(rq, p, 0));
- return 1;
- }
-#endif
-
- res = dequeue_process(rq, p);
-
- if (res && erts_system_profile_flags.runnable_procs && to_inactive)
- profile_runnable_proc(p, am_inactive);
-
-#ifdef ERTS_SMP
- ASSERT(!(p->status_flags & ERTS_PROC_SFLG_INRUNQ));
-#endif
-
- return res;
-}
-
-#ifdef ERTS_SMP
-
-ErtsMigrateResult
-erts_proc_migrate(Process *p, ErtsProcLocks *plcks,
- ErtsRunQueue *from_rq, int *from_locked,
- ErtsRunQueue *to_rq, int *to_locked)
-{
- ERTS_SMP_LC_ASSERT(*plcks == erts_proc_lc_my_proc_locks(p));
- ERTS_SMP_LC_ASSERT((ERTS_PROC_LOCK_STATUS & *plcks)
- || from_locked);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(from_rq, *from_locked);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(to_rq, *to_locked);
-
- /*
- * If we have the lock on the run queue to migrate to,
- * check that it isn't suspended. If it is suspended,
- * we will refuse to migrate to it anyway.
- */
- if (*to_locked && (to_rq->flags & ERTS_RUNQ_FLG_SUSPENDED))
- return ERTS_MIGRATE_FAILED_RUNQ_SUSPENDED;
-
- /* We need status lock on process and locks on both run queues */
-
- if (!(ERTS_PROC_LOCK_STATUS & *plcks)) {
- if (erts_smp_proc_trylock(p, ERTS_PROC_LOCK_STATUS) == EBUSY) {
- ErtsProcLocks lcks = *plcks;
- Eterm pid = p->id;
- Process *proc = *plcks ? p : NULL;
-
- if (*from_locked) {
- *from_locked = 0;
- erts_smp_runq_unlock(from_rq);
- }
- if (*to_locked) {
- *to_locked = 0;
- erts_smp_runq_unlock(to_rq);
- }
-
- proc = erts_pid2proc_opt(proc,
- lcks,
- pid,
- lcks|ERTS_PROC_LOCK_STATUS,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- if (!proc) {
- *plcks = 0;
- return ERTS_MIGRATE_FAILED_NOT_IN_RUNQ;
- }
- ASSERT(proc == p);
- }
- *plcks |= ERTS_PROC_LOCK_STATUS;
- }
-
- ASSERT(!p->bound_runq);
-
- ERTS_SMP_LC_CHK_RUNQ_LOCK(from_rq, *from_locked);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(to_rq, *to_locked);
-
- if (p->run_queue != from_rq)
- return ERTS_MIGRATE_FAILED_RUNQ_CHANGED;
-
- if (!*from_locked || !*to_locked) {
- if (from_rq < to_rq) {
- if (!*to_locked) {
- if (!*from_locked)
- erts_smp_runq_lock(from_rq);
- erts_smp_runq_lock(to_rq);
- }
- else if (erts_smp_runq_trylock(from_rq) == EBUSY) {
- erts_smp_runq_unlock(to_rq);
- erts_smp_runq_lock(from_rq);
- erts_smp_runq_lock(to_rq);
- }
- }
- else {
- if (!*from_locked) {
- if (!*to_locked)
- erts_smp_runq_lock(to_rq);
- erts_smp_runq_lock(from_rq);
- }
- else if (erts_smp_runq_trylock(to_rq) == EBUSY) {
- erts_smp_runq_unlock(from_rq);
- erts_smp_runq_lock(to_rq);
- erts_smp_runq_lock(from_rq);
- }
- }
- *to_locked = *from_locked = 1;
- }
-
- ERTS_SMP_LC_CHK_RUNQ_LOCK(from_rq, *from_locked);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(to_rq, *to_locked);
-
- /* Ok we now got all locks we need; do it... */
-
- /* Refuse to migrate to a suspended run queue */
- if (to_rq->flags & ERTS_RUNQ_FLG_SUSPENDED)
- return ERTS_MIGRATE_FAILED_RUNQ_SUSPENDED;
-
- if ((p->runq_flags & ERTS_PROC_RUNQ_FLG_RUNNING)
- || !(p->status_flags & ERTS_PROC_SFLG_INRUNQ))
- return ERTS_MIGRATE_FAILED_NOT_IN_RUNQ;
-
- dequeue_process(from_rq, p);
- p->run_queue = to_rq;
- enqueue_process(to_rq, p);
-
- return ERTS_MIGRATE_SUCCESS;
-}
-#endif /* ERTS_SMP */
-
Eterm
erts_process_status(Process *c_p, ErtsProcLocks c_p_locks,
Process *rp, Eterm rpid)
{
Eterm res = am_undefined;
- Process *p;
-
- if (rp) {
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS
- & erts_proc_lc_my_proc_locks(rp));
- p = rp;
- }
- else {
- p = erts_pid2proc_opt(c_p, c_p_locks,
- rpid, ERTS_PROC_LOCK_STATUS,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- }
+ Process *p = rp ? rp : erts_proc_lookup_raw(rpid);
if (p) {
- switch (p->status) {
- case P_RUNABLE:
- res = am_runnable;
- break;
- case P_WAITING:
- res = am_waiting;
- break;
- case P_RUNNING:
- res = am_running;
- break;
- case P_EXITING:
+ 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;
- break;
- case P_GARBING:
+ else if (state & ERTS_PSFLG_GC)
res = am_garbage_collecting;
- break;
- case P_SUSPENDED:
+ else if (state & ERTS_PSFLG_SUSPENDED)
res = am_suspended;
- break;
- case P_FREE: /* We cannot look up a process in P_FREE... */
- default: /* Not a valid status... */
- erl_exit(1, "Bad status (%b32u) found for process %T\n",
- p->status, p->id);
- break;
- }
-
-#ifdef ERTS_SMP
- if (!rp && (p != c_p || !(ERTS_PROC_LOCK_STATUS & c_p_locks)))
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ else if (state & ERTS_PSFLG_RUNNING)
+ res = am_running;
+ else if (state & ERTS_PSFLG_ACTIVE)
+ res = am_runnable;
+ else
+ res = am_waiting;
}
+#ifdef ERTS_SMP
else {
int i;
ErtsSchedulerData *esdp;
@@ -6331,42 +6723,40 @@ erts_process_status(Process *c_p, ErtsProcLocks c_p_locks,
}
erts_smp_runq_unlock(esdp->run_queue);
}
-
-#endif
-
}
-
+#endif
return res;
}
/*
-** Suspend a process
+** Suspend a currently executing process
** If we are to suspend on a port the busy_port is the thing
** otherwise busy_port is NIL
*/
void
-erts_suspend(Process* process, ErtsProcLocks process_locks, Port *busy_port)
+erts_suspend(Process* c_p, ErtsProcLocks c_p_locks, Port *busy_port)
{
- ErtsRunQueue *rq;
-
- ERTS_SMP_LC_ASSERT(process_locks == erts_proc_lc_my_proc_locks(process));
- if (!(process_locks & ERTS_PROC_LOCK_STATUS))
- erts_smp_proc_lock(process, ERTS_PROC_LOCK_STATUS);
-
- rq = erts_get_runq_proc(process);
-
- erts_smp_runq_lock(rq);
+#ifdef DEBUG
+ int res;
+#endif
+ ASSERT(c_p == erts_get_current_process());
+ ERTS_SMP_LC_ASSERT(c_p_locks == erts_proc_lc_my_proc_locks(c_p));
+ if (!(c_p_locks & ERTS_PROC_LOCK_STATUS))
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_STATUS);
- suspend_process(rq, process);
+#ifdef DEBUG
+ res =
+#endif
+ suspend_process(c_p, c_p);
- erts_smp_runq_unlock(rq);
+ ASSERT(res);
if (busy_port)
- erts_wake_process_later(busy_port, process);
+ erts_wake_process_later(busy_port, c_p);
- if (!(process_locks & ERTS_PROC_LOCK_STATUS))
- erts_smp_proc_unlock(process, ERTS_PROC_LOCK_STATUS);
+ if (!(c_p_locks & ERTS_PROC_LOCK_STATUS))
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_STATUS);
}
@@ -6407,57 +6797,54 @@ erts_resume_processes(ErtsProcList *plp)
Eterm
erts_get_process_priority(Process *p)
{
- ErtsRunQueue *rq;
- Eterm value;
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
- rq = erts_get_runq_proc(p);
- erts_smp_runq_lock(rq);
- switch(p->prio) {
- case PRIORITY_MAX: value = am_max; break;
- case PRIORITY_HIGH: value = am_high; break;
- case PRIORITY_NORMAL: value = am_normal; break;
- case PRIORITY_LOW: value = am_low; break;
- default: ASSERT(0); value = am_undefined; break;
+ erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state);
+ switch (state & ERTS_PSFLG_PRIO_MASK) {
+ case PRIORITY_MAX: return am_max;
+ case PRIORITY_HIGH: return am_high;
+ case PRIORITY_NORMAL: return am_normal;
+ case PRIORITY_LOW: return am_low;
+ default: ASSERT(0); return am_undefined;
}
- erts_smp_runq_unlock(rq);
- return value;
}
Eterm
-erts_set_process_priority(Process *p, Eterm new_value)
+erts_set_process_priority(Process *p, Eterm value)
{
- ErtsRunQueue *rq;
- Eterm old_value;
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
- rq = erts_get_runq_proc(p);
-#ifdef ERTS_SMP
- ASSERT(!(p->status_flags & ERTS_PROC_SFLG_INRUNQ));
-#endif
- erts_smp_runq_lock(rq);
- switch(p->prio) {
- case PRIORITY_MAX: old_value = am_max; break;
- case PRIORITY_HIGH: old_value = am_high; break;
- case PRIORITY_NORMAL: old_value = am_normal; break;
- case PRIORITY_LOW: old_value = am_low; break;
- default: ASSERT(0); old_value = am_undefined; break;
- }
- switch (new_value) {
- case am_max: p->prio = PRIORITY_MAX; break;
- case am_high: p->prio = PRIORITY_HIGH; break;
- case am_normal: p->prio = PRIORITY_NORMAL; break;
- case am_low: p->prio = PRIORITY_LOW; break;
- default: old_value = THE_NON_VALUE; break;
+ erts_aint32_t a, oprio, nprio;
+
+ switch (value) {
+ case am_max: nprio = (erts_aint32_t) PRIORITY_MAX; break;
+ case am_high: nprio = (erts_aint32_t) PRIORITY_HIGH; break;
+ case am_normal: nprio = (erts_aint32_t) PRIORITY_NORMAL; break;
+ case am_low: nprio = (erts_aint32_t) PRIORITY_LOW; break;
+ default: return THE_NON_VALUE; break;
}
- erts_smp_runq_unlock(rq);
- return old_value;
-}
-/* note that P_RUNNING is only set so that we don't try to remove
-** running processes from the schedule queue if they exit - a running
-** process not being in the schedule queue!!
-** Schedule for up to INPUT_REDUCTIONS context switches,
-** return 1 if more to do.
-*/
+ a = erts_smp_atomic32_read_nob(&p->state);
+ if (nprio == (a & ERTS_PSFLG_PRIO_MASK))
+ oprio = nprio;
+ else {
+ erts_aint32_t e, n;
+ do {
+ oprio = a & ERTS_PSFLG_PRIO_MASK;
+ n = e = a;
+
+ ASSERT(!(a & ERTS_PSFLG_IN_RUNQ));
+
+ n &= ~ERTS_PSFLG_PRIO_MASK;
+ n |= nprio;
+ a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e);
+ } while (a != e);
+ }
+
+ switch (oprio) {
+ case PRIORITY_MAX: return am_max;
+ case PRIORITY_HIGH: return am_high;
+ case PRIORITY_NORMAL: return am_normal;
+ case PRIORITY_LOW: return am_low;
+ default: ASSERT(0); return am_undefined;
+ }
+}
/*
* schedule() is called from BEAM (process_main()) or HiPE
@@ -6480,7 +6867,6 @@ erts_set_process_priority(Process *p, Eterm new_value)
Process *schedule(Process *p, int calls)
{
ErtsRunQueue *rq;
- ErtsRunPrioQueue *rpq;
erts_aint_t dt;
ErtsSchedulerData *esdp;
int context_reds;
@@ -6488,6 +6874,8 @@ Process *schedule(Process *p, int calls)
int input_reductions;
int actual_reds;
int reds;
+ Uint32 flags;
+ erts_aint32_t state = 0; /* Supress warning... */
#ifdef USE_VM_PROBES
if (p != NULL && DTRACE_ENABLED(process_unscheduled)) {
@@ -6543,92 +6931,60 @@ Process *schedule(Process *p, int calls)
erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- if ((erts_system_profile_flags.runnable_procs)
- && (p->status == P_WAITING)) {
- profile_runnable_proc(p, am_inactive);
- }
+ state = erts_smp_atomic32_read_acqb(&p->state);
if (IS_TRACED(p)) {
- if (IS_TRACED_FL(p, F_TRACE_CALLS) && p->status != P_FREE) {
+ if (IS_TRACED_FL(p, F_TRACE_CALLS) && !(state & ERTS_PSFLG_FREE)) {
erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_OUT);
}
- switch (p->status) {
- case P_EXITING:
+ if (state & (ERTS_PSFLG_FREE|ERTS_PSFLG_EXITING)) {
if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_EXIT))
- trace_sched(p, am_out_exiting);
- break;
- case P_FREE:
- if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_EXIT))
- trace_sched(p, am_out_exited);
- break;
- default:
+ trace_sched(p, ((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);
- break;
}
}
#ifdef ERTS_SMP
- if (ERTS_PROC_PENDING_EXIT(p)) {
- erts_handle_pending_exit(p,
- ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
- p->status_flags |= ERTS_PROC_SFLG_PENDADD2SCHEDQ;
- }
-
- if (p->pending_suspenders) {
- handle_pending_suspend(p,
- ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
- ASSERT(!(p->status_flags & ERTS_PROC_SFLG_PENDADD2SCHEDQ)
- || p->rcount == 0);
- }
+ if (state & ERTS_PSFLG_PENDING_EXIT)
+ erts_handle_pending_exit(p, (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_STATUS));
+ if (p->pending_suspenders)
+ handle_pending_suspend(p, (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_STATUS));
#endif
- erts_smp_runq_lock(rq);
- ERTS_PROC_REDUCTIONS_EXECUTED(rq, p->prio, reds, actual_reds);
+ schedule_out_process(rq, state, p); /* Returns with rq locked! */
+
+ ERTS_PROC_REDUCTIONS_EXECUTED(rq,
+ (int) (state & ERTS_PSFLG_PRIO_MASK),
+ reds,
+ actual_reds);
esdp->current_process = NULL;
#ifdef ERTS_SMP
p->scheduler_data = NULL;
- p->runq_flags &= ~ERTS_PROC_RUNQ_FLG_RUNNING;
- p->status_flags &= ~ERTS_PROC_SFLG_RUNNING;
-
- if (p->status_flags & ERTS_PROC_SFLG_PENDADD2SCHEDQ) {
- ErtsRunQueue *notify_runq;
- p->status_flags &= ~ERTS_PROC_SFLG_PENDADD2SCHEDQ;
- notify_runq = internal_add_to_runq(rq, p);
- if (notify_runq != rq)
- smp_notify_inc_runq(notify_runq);
- }
#endif
- if (p->status == P_FREE) {
+ if (state & ERTS_PSFLG_FREE) {
#ifdef ERTS_SMP
ASSERT(esdp->free_process == p);
esdp->free_process = NULL;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
- erts_smp_proc_dec_refc(p);
#else
erts_free_proc(p);
#endif
- } else {
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
}
-#ifdef ERTS_SMP
- {
- ErtsProcList *pnd_xtrs = rq->procs.pending_exiters;
- rq->procs.pending_exiters = NULL;
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
- if (pnd_xtrs) {
- erts_smp_runq_unlock(rq);
- handle_pending_exiters(pnd_xtrs);
- erts_smp_runq_lock(rq);
- }
-
- }
+#ifdef ERTS_SMP
ASSERT(!esdp->free_process);
#endif
ASSERT(!esdp->current_process);
@@ -6648,8 +7004,23 @@ Process *schedule(Process *p, int calls)
ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking());
check_activities_to_run: {
+#ifdef ERTS_SMP
+ ErtsMigrationPaths *mps;
+ ErtsMigrationPath *mp;
#ifdef ERTS_SMP
+ {
+ ErtsProcList *pnd_xtrs = rq->procs.pending_exiters;
+ rq->procs.pending_exiters = NULL;
+
+ if (pnd_xtrs) {
+ erts_smp_runq_unlock(rq);
+ handle_pending_exiters(pnd_xtrs);
+ erts_smp_runq_lock(rq);
+ }
+
+ }
+#endif
if (rq->check_balance_reds <= 0)
check_balance(rq);
@@ -6657,20 +7028,28 @@ Process *schedule(Process *p, int calls)
ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking());
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
- if (rq->flags & ERTS_RUNQ_FLGS_IMMIGRATE_QMASK)
- immigrate(rq);
+ mps = erts_get_migration_paths_managed();
+ mp = &mps->mpath[rq->ix];
+
+ if (mp->flags & ERTS_RUNQ_FLGS_IMMIGRATE_QMASK)
+ immigrate(rq, mp);
+
+ continue_check_activities_to_run:
+ flags = ERTS_RUNQ_FLGS_GET_NOB(rq);
+ continue_check_activities_to_run_known_flags:
- continue_check_activities_to_run:
- if (rq->flags & (ERTS_RUNQ_FLG_CHK_CPU_BIND
- | ERTS_RUNQ_FLG_SUSPENDED)) {
- if (rq->flags & ERTS_RUNQ_FLG_SUSPENDED) {
- ASSERT(erts_smp_atomic32_read_nob(&esdp->ssi->flags)
- & ERTS_SSI_FLG_SUSPENDED);
+ if (flags & (ERTS_RUNQ_FLG_CHK_CPU_BIND|ERTS_RUNQ_FLG_SUSPENDED)) {
+
+ if (flags & ERTS_RUNQ_FLG_SUSPENDED) {
suspend_scheduler(esdp);
+ flags = ERTS_RUNQ_FLGS_GET_NOB(rq);
}
- if (rq->flags & ERTS_RUNQ_FLG_CHK_CPU_BIND)
+ 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);
+ }
}
{
@@ -6699,14 +7078,12 @@ Process *schedule(Process *p, int calls)
}
#endif /* ERTS_SMP */
- ASSERT(rq->len == rq->procs.len + rq->ports.info.len);
-
- if ((rq->len == 0 && !rq->misc.start)
- || (rq->halt_in_progress
- && rq->ports.info.len == 0 && !rq->misc.start)) {
+ flags = ERTS_RUNQ_FLGS_GET_NOB(rq);
+ 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));
rq->wakeup_other = 0;
@@ -6714,21 +7091,27 @@ Process *schedule(Process *p, int calls)
empty_runq(rq);
- if (rq->flags & ERTS_RUNQ_FLG_SUSPENDED) {
- ASSERT(erts_smp_atomic32_read_nob(&esdp->ssi->flags)
- & ERTS_SSI_FLG_SUSPENDED);
+ flags = ERTS_RUNQ_FLGS_GET_NOB(rq);
+ if (flags & ERTS_RUNQ_FLG_SUSPENDED) {
non_empty_runq(rq);
- goto continue_check_activities_to_run;
+ goto continue_check_activities_to_run_known_flags;
}
- else if (!(rq->flags & ERTS_RUNQ_FLG_INACTIVE)) {
+ else if (!(flags & ERTS_RUNQ_FLG_INACTIVE)) {
+ if (try_steal_task(rq)) {
+ non_empty_runq(rq);
+ goto continue_check_activities_to_run;
+ }
+
+ (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
+
/*
* Check for ERTS_RUNQ_FLG_SUSPENDED has to be done
* after trying to steal a task.
*/
- if (try_steal_task(rq)
- || (rq->flags & ERTS_RUNQ_FLG_SUSPENDED)) {
+ flags = ERTS_RUNQ_FLGS_GET_NOB(rq);
+ if (flags & ERTS_RUNQ_FLG_SUSPENDED) {
non_empty_runq(rq);
- goto continue_check_activities_to_run;
+ goto continue_check_activities_to_run_known_flags;
}
}
@@ -6759,6 +7142,7 @@ Process *schedule(Process *p, int calls)
erl_sys_schedule(1);
dt = erts_do_time_read_and_reset();
if (dt) erts_bump_timer(dt);
+
#ifdef ERTS_SMP
erts_smp_runq_lock(rq);
clear_sys_scheduling();
@@ -6772,14 +7156,14 @@ Process *schedule(Process *p, int calls)
exec_misc_ops(rq);
#ifdef ERTS_SMP
- wakeup_other.check(rq);
+ wakeup_other.check(rq, flags);
#endif
/*
* Find a new port to run.
*/
- if (rq->ports.info.len) {
+ if (RUNQ_READ_LEN(&rq->ports.info.len)) {
int have_outstanding_io;
have_outstanding_io = erts_port_task_execute(rq, &esdp->current_port);
if ((have_outstanding_io && fcalls > 2*input_reductions)
@@ -6806,159 +7190,122 @@ Process *schedule(Process *p, int calls)
/*
* Find a new process to run.
*/
- pick_next_process:
-
- ERTS_DBG_CHK_PROCS_RUNQ(rq);
-
- switch (rq->flags & ERTS_RUNQ_FLGS_PROCS_QMASK) {
- case MAX_BIT:
- case MAX_BIT|HIGH_BIT:
- case MAX_BIT|NORMAL_BIT:
- case MAX_BIT|LOW_BIT:
- case MAX_BIT|HIGH_BIT|NORMAL_BIT:
- case MAX_BIT|HIGH_BIT|LOW_BIT:
- case MAX_BIT|NORMAL_BIT|LOW_BIT:
- case MAX_BIT|HIGH_BIT|NORMAL_BIT|LOW_BIT:
- rpq = &rq->procs.prio[PRIORITY_MAX];
- break;
- case HIGH_BIT:
- case HIGH_BIT|NORMAL_BIT:
- case HIGH_BIT|LOW_BIT:
- case HIGH_BIT|NORMAL_BIT|LOW_BIT:
- rpq = &rq->procs.prio[PRIORITY_HIGH];
- break;
- case NORMAL_BIT:
- rpq = &rq->procs.prio[PRIORITY_NORMAL];
- break;
- case LOW_BIT:
- rpq = &rq->procs.prio[PRIORITY_NORMAL];
- break;
- case NORMAL_BIT|LOW_BIT:
- rpq = &rq->procs.prio[PRIORITY_NORMAL];
- ASSERT(rpq->first != NULL);
- p = rpq->first;
- if (p->prio == PRIORITY_LOW) {
- if (p == rpq->last || p->skipped >= RESCHEDULE_LOW-1)
- p->skipped = 0;
- else {
- /* skip it */
- p->skipped++;
- rpq->first = p->next;
- rpq->first->prev = NULL;
- rpq->last->next = p;
- p->prev = rpq->last;
- p->next = NULL;
- rpq->last = p;
+ pick_next_process: {
+ int prio_q;
+ int qmask;
+
+ flags = ERTS_RUNQ_FLGS_GET_NOB(rq);
+ qmask = (int) (flags & ERTS_RUNQ_FLGS_PROCS_QMASK);
+ switch (qmask & -qmask) {
+ case MAX_BIT:
+ prio_q = PRIORITY_MAX;
+ break;
+ case HIGH_BIT:
+ prio_q = PRIORITY_HIGH;
+ break;
+ case NORMAL_BIT:
+ case LOW_BIT:
+ prio_q = PRIORITY_NORMAL;
+ if (check_requeue_process(rq, PRIORITY_NORMAL))
goto pick_next_process;
- }
+ break;
+ case 0: /* No process at all */
+ default:
+ ASSERT(qmask == 0);
+ goto check_activities_to_run;
}
- break;
- case 0: /* No process at all */
- default:
- ASSERT((rq->flags & ERTS_RUNQ_FLGS_PROCS_QMASK) == 0);
- ASSERT(rq->procs.len == 0);
- goto check_activities_to_run;
- }
- BM_START_TIMER(system);
+ BM_START_TIMER(system);
- /*
- * Take the chosen process out of the queue.
- */
- ASSERT(rpq->first); /* Wrong qmask in rq->flags? */
- p = rpq->first;
-#ifdef ERTS_SMP
- ERTS_SMP_LC_ASSERT(rq == p->run_queue);
-#endif
- rpq->first = p->next;
- if (!rpq->first)
- rpq->last = NULL;
- else
- rpq->first->prev = NULL;
-
- p->next = p->prev = NULL;
+ /*
+ * Take the chosen process out of the queue.
+ */
+ p = dequeue_process(rq, prio_q, &state);
- if (--rq->procs.prio_info[p->prio].len == 0)
- rq->flags &= ~(1 << p->prio);
- ASSERT(rq->procs.len > 0);
- rq->procs.len--;
- ASSERT(rq->len > 0);
- rq->len--;
+ ASSERT(p); /* Wrong qmask in rq->flags? */
- {
- Uint32 ee_flgs = (ERTS_RUNQ_FLG_EVACUATE(p->prio)
- | ERTS_RUNQ_FLG_EMIGRATE(p->prio));
-
- if ((rq->flags & (ERTS_RUNQ_FLG_SUSPENDED|ee_flgs)) == ee_flgs)
- ERTS_UNSET_RUNQ_FLG_EVACUATE(rq->flags, p->prio);
- }
+ while (1) {
+ erts_aint32_t exp, new, tmp;
+ tmp = new = exp = state;
+ new &= ~ERTS_PSFLG_IN_RUNQ;
+ tmp = state & (ERTS_PSFLG_SUSPENDED|ERTS_PSFLG_PENDING_EXIT);
+ if (tmp != ERTS_PSFLG_SUSPENDED)
+ new |= ERTS_PSFLG_RUNNING;
+ state = erts_smp_atomic32_cmpxchg_relb(&p->state, new, exp);
+ if (state == exp) {
+ tmp = state & (ERTS_PSFLG_SUSPENDED|ERTS_PSFLG_PENDING_EXIT);
+ if (tmp == ERTS_PSFLG_SUSPENDED)
+ goto pick_next_process;
+ state = new;
+ break;
+ }
+ }
- ERTS_DBG_CHK_PROCS_RUNQ_NOPROC(rq, p);
+ rq->procs.context_switches++;
- rq->procs.context_switches++;
+ esdp->current_process = p;
- esdp->current_process = p;
+ }
#ifdef ERTS_SMP
- p->runq_flags |= ERTS_PROC_RUNQ_FLG_RUNNING;
erts_smp_runq_unlock(rq);
+ if (flags & ERTS_RUNQ_FLG_PROTECTED)
+ (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
+
ERTS_SMP_CHK_NO_PROC_LOCKS;
erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
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);
int migrated = old && old != esdp->no;
+ prio = (int) (state & ERTS_PSFLG_PRIO_MASK);
+
erts_smp_spin_lock(&erts_sched_stat.lock);
- erts_sched_stat.prio[p->prio].total_executed++;
- erts_sched_stat.prio[p->prio].executed++;
+ erts_sched_stat.prio[prio].total_executed++;
+ erts_sched_stat.prio[prio].executed++;
if (migrated) {
- erts_sched_stat.prio[p->prio].total_migrated++;
- erts_sched_stat.prio[p->prio].migrated++;
+ erts_sched_stat.prio[prio].total_migrated++;
+ erts_sched_stat.prio[prio].migrated++;
}
erts_smp_spin_unlock(&erts_sched_stat.lock);
}
- p->status_flags |= ERTS_PROC_SFLG_RUNNING;
- p->status_flags &= ~ERTS_PROC_SFLG_INRUNQ;
if (ERTS_PROC_PENDING_EXIT(p)) {
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
- ASSERT(p->status != P_SUSPENDED); /* Never run a suspended process */
+ /* Never run a suspended process */
+ ASSERT(!(ERTS_PSFLG_SUSPENDED & erts_smp_atomic32_read_nob(&p->state)));
reds = context_reds;
if (IS_TRACED(p)) {
- switch (p->status) {
- case P_EXITING:
+ if (state & ERTS_PSFLG_EXITING) {
if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_EXIT))
trace_sched(p, am_in_exiting);
- break;
- default:
+ }
+ 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);
- break;
}
if (IS_TRACED_FL(p, F_TRACE_CALLS)) {
erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_IN);
}
}
- if (p->status != P_EXITING)
- p->status = P_RUNNING;
-
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
#ifdef ERTS_SMP
@@ -6966,7 +7313,7 @@ Process *schedule(Process *p, int calls)
erts_check_my_tracer_proc(p);
#endif
- if (!ERTS_PROC_IS_EXITING(p)
+ 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);
@@ -7057,17 +7404,19 @@ erts_schedule_misc_op(void (*func)(void *), void *arg)
ErtsSchedulerData *esdp = erts_get_scheduler_data();
ErtsRunQueue *rq = esdp ? esdp->run_queue : ERTS_RUNQ_IX(0);
ErtsMiscOpList *molp = misc_op_list_alloc();
+#ifdef ERTS_SMP
+ ErtsMigrationPaths *mpaths = erts_get_migration_paths();
- erts_smp_runq_lock(rq);
-
- while (rq->misc.evac_runq) {
- ErtsRunQueue *tmp_rq = rq->misc.evac_runq;
- erts_smp_runq_unlock(rq);
- rq = tmp_rq;
- erts_smp_runq_lock(rq);
+ if (!mpaths)
+ rq = ERTS_RUNQ_IX(0);
+ else {
+ ErtsRunQueue *erq = mpaths->mpath[rq->ix].misc_evac_runq;
+ if (erq)
+ rq = erq;
}
+#endif
- ASSERT(!(rq->flags & ERTS_RUNQ_FLG_SUSPENDED));
+ erts_smp_runq_lock(rq);
molp->next = NULL;
molp->func = func;
@@ -7077,7 +7426,9 @@ erts_schedule_misc_op(void (*func)(void *), void *arg)
else
rq->misc.start = molp;
rq->misc.end = molp;
+
erts_smp_runq_unlock(rq);
+
smp_notify_inc_runq(rq);
}
@@ -7167,44 +7518,50 @@ erts_get_exact_total_reductions(Process *c_p, Uint *redsp, Uint *diffp)
Sint
erts_test_next_pid(int set, Uint next)
{
+ Uint64 lpd;
Sint res;
- Sint p_prev;
+ Eterm pid_data;
+ int first_pix = -1;
+ erts_smp_rwmtx_rwlock(&erts_proc_tab_rwmtx);
- erts_smp_mtx_lock(&proc_tab_mtx);
-
- if (!set) {
- res = p_next < 0 ? -1 : (p_serial << p_serial_shift | p_next);
- }
+ if (!set)
+ lpd = last_pid_data_read_nob();
else {
- p_serial = (Sint) ((next >> p_serial_shift) & p_serial_mask);
- p_next = (Sint) (erts_process_tab_index_mask & next);
-
- if (p_next >= erts_max_processes) {
- p_next = 0;
- p_serial++;
- p_serial &= p_serial_mask;
+ lpd = (Uint64) next;
+ pid_data = (Eterm) (lpd & ERTS_PID_DATA_MASK__);
+ if (ERTS_INVALID_PID == make_internal_pid(pid_data)) {
+ lpd += erts_proc.max;
+ ASSERT(erts_pid_data2ix(pid_data)
+ == erts_pid_data2ix(lpd & ERTS_PID_DATA_MASK__));
}
+ last_pid_data_set_relb(lpd);
+ }
- p_prev = p_next;
-
- do {
- if (!process_tab[p_next])
- break;
- p_next++;
- if(p_next >= erts_max_processes) {
- p_next = 0;
- p_serial++;
- p_serial &= p_serial_mask;
+ while (1) {
+ int pix;
+ lpd++;
+ pix = (int) (lpd % erts_proc.max);
+ if (first_pix < 0)
+ first_pix = pix;
+ else if (pix == first_pix) {
+ res = -1;
+ break;
+ }
+ if (ERTS_AINT_NULL == erts_smp_atomic_read_nob(&erts_proc.tab[pix])) {
+ pid_data = (Eterm) (lpd & ERTS_PID_DATA_MASK__);
+ if (ERTS_INVALID_PID == make_internal_pid(pid_data)) {
+ lpd += erts_proc.max;
+ ASSERT(erts_pid_data2ix(pid_data)
+ == erts_pid_data2ix(lpd & ERTS_PID_DATA_MASK__));
}
- } while (p_prev != p_next);
-
- res = process_tab[p_next] ? -1 : (p_serial << p_serial_shift | p_next);
-
+ res = lpd & ERTS_PID_DATA_MASK__;
+ break;
+ }
}
- erts_smp_mtx_unlock(&proc_tab_mtx);
+ erts_smp_rwmtx_rwunlock(&erts_proc_tab_rwmtx);
return res;
@@ -7213,6 +7570,8 @@ erts_test_next_pid(int set, Uint next)
Uint erts_process_count(void)
{
erts_aint32_t res = erts_smp_atomic32_read_nob(&process_count);
+ if (res > erts_proc.max)
+ return erts_proc.max;
ASSERT(res >= 0);
return (Uint) res;
}
@@ -7220,97 +7579,138 @@ Uint erts_process_count(void)
void
erts_free_proc(Process *p)
{
-#if defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP)
- erts_lcnt_proc_lock_destroy(p);
+#ifdef ERTS_SMP
+ erts_proc_lock_fin(p);
#endif
erts_free(ERTS_ALC_T_PROC, (void *) p);
}
-
/*
** Allocate process and find out where to place next process.
*/
static Process*
-alloc_process(void)
+alloc_process(ErtsRunQueue *rq, erts_aint32_t state)
{
-#ifdef ERTS_SMP
- erts_pix_lock_t *pix_lock;
-#endif
+ int pix;
Process* p;
- int p_prev;
+ Uint64 lpd, exp_lpd;
+ Eterm pid_data;
+ erts_aint32_t proc_count;
+#ifdef DEBUG
+ Eterm pid;
+#endif
- erts_smp_mtx_lock(&proc_tab_mtx);
+ erts_smp_rwmtx_rlock(&erts_proc_tab_rwmtx);
- if (p_next == -1) {
- p = NULL;
- goto error; /* Process table full! */
+ proc_count = erts_smp_atomic32_inc_read_acqb(&process_count);
+ if (proc_count > erts_proc.max) {
+ while (1) {
+ erts_aint32_t act_proc_count;
+
+ act_proc_count = erts_smp_atomic32_cmpxchg_relb(&process_count,
+ proc_count-1,
+ proc_count);
+ if (act_proc_count == proc_count)
+ goto system_limit;
+ proc_count = act_proc_count;
+ if (proc_count <= erts_proc.max)
+ break;
+ }
}
p = (Process*) erts_alloc_fnf(ERTS_ALC_T_PROC, sizeof(Process));
if (!p)
- goto error; /* ENOMEM */
+ goto enomem;
- p_last = p_next;
+ p->approx_started = erts_get_approx_time();
+ p->started_interval = get_proc_interval();
- erts_get_emu_time(&p->started);
+ lpd = last_pid_data_read_acqb();
-#ifdef ERTS_SMP
- pix_lock = ERTS_PIX2PIXLOCK(p_next);
- erts_pix_lock(pix_lock);
-#endif
- ASSERT(!process_tab[p_next]);
+ /* Reserve slot */
+ while (1) {
+ lpd++;
+ pix = erts_pid_data2ix((Eterm) (lpd & ERTS_PID_DATA_MASK__));
+ if (erts_smp_atomic_read_nob(&erts_proc.tab[pix]) == ERTS_AINT_NULL) {
+ erts_aint_t val;
+ val = erts_smp_atomic_cmpxchg_relb(&erts_proc.tab[pix],
+ ((erts_aint_t)
+ ERTS_PROC_LOCK_BUSY),
+ ERTS_AINT_NULL);
+
+ if (ERTS_AINT_NULL == val)
+ break;
+ }
+ }
- process_tab[p_next] = p;
- erts_smp_atomic32_inc_nob(&process_count);
- p->id = make_internal_pid(p_serial << p_serial_shift | p_next);
+ pid_data = (Eterm) lpd & ERTS_PID_DATA_MASK__;
+
+ p->id = make_internal_pid(pid_data);
if (p->id == ERTS_INVALID_PID) {
/* Do not use the invalid pid; change serial */
- p_serial++;
- p_serial &= p_serial_mask;
- p->id = make_internal_pid(p_serial << p_serial_shift | p_next);
+ lpd += erts_proc.max;
+ ASSERT(pix == erts_pid_data2ix((Eterm) (lpd & ERTS_PID_DATA_MASK__)));
+ pid_data = (Eterm) lpd & ERTS_PID_DATA_MASK__;
+ p->id = make_internal_pid(pid_data);
ASSERT(p->id != ERTS_INVALID_PID);
}
- ASSERT(internal_pid_serial(p->id) <= (erts_use_r9_pids_ports
- ? ERTS_MAX_PID_R9_SERIAL
- : ERTS_MAX_PID_SERIAL));
+
+ exp_lpd = last_pid_data_read_nob();
+
+ /* Move last pid data forward */
+ while (1) {
+ Uint64 act_lpd;
+ if (last_pid_data_cmp(lpd, exp_lpd) < 0)
+ break;
+ act_lpd = last_pid_data_cmpxchg_relb(lpd, exp_lpd);
+ if (act_lpd == exp_lpd)
+ break;
+ exp_lpd = act_lpd;
+ }
#ifdef ERTS_SMP
- erts_proc_lock_init(p); /* All locks locked */
- erts_pix_unlock(pix_lock);
+ RUNQ_SET_RQ(&p->run_queue, rq);
#endif
- p->rstatus = P_FREE;
- p->rcount = 0;
+ erts_smp_atomic32_init_relb(&p->state, state);
- /*
- * set p_next to the next available slot
- */
+#ifdef DEBUG
+ pid = p->id;
+#endif
- p_prev = p_next;
+#ifdef ERTS_SMP
+ erts_proc_lock_init(p); /* All locks locked */
+#endif
- while (1) {
- p_next++;
- if(p_next >= erts_max_processes) {
- p_serial++;
- p_serial &= p_serial_mask;
- p_next = 0;
- }
+ /* Move into slot reserved */
+#ifdef DEBUG
+ ASSERT(ERTS_PROC_LOCK_BUSY
+ == (Process *) erts_smp_atomic_xchg_relb(&erts_proc.tab[pix],
+ (erts_aint_t) p));
+#else
+ erts_smp_atomic_set_relb(&erts_proc.tab[pix], (erts_aint_t) p);
+#endif
- if (p_prev == p_next) {
- p_next = -1;
- break; /* Table full! */
- }
+ ASSERT(internal_pid_serial(p->id) <= (erts_use_r9_pids_ports
+ ? ERTS_MAX_PID_R9_SERIAL
+ : ERTS_MAX_PID_SERIAL));
- if (!process_tab[p_next])
- break; /* found a free slot */
- }
+ erts_smp_rwmtx_runlock(&erts_proc_tab_rwmtx);
- error:
+ p->rcount = 0;
- erts_smp_mtx_unlock(&proc_tab_mtx);
+ ASSERT(p == (Process *)
+ erts_smp_atomic_read_nob(
+ &erts_proc.tab[internal_pid_index(pid)]));
return p;
+enomem:
+system_limit:
+
+ erts_smp_rwmtx_runlock(&erts_proc_tab_rwmtx);
+ return NULL;
+
}
Eterm
@@ -7320,13 +7720,15 @@ 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. */
{
- ErtsRunQueue *rq, *notify_runq;
+ ErtsRunQueue *rq = NULL;
Process *p;
Sint arity; /* Number of arguments. */
Uint arg_size; /* Size of arguments. */
Uint sz; /* Needed words on heap. */
Uint heap_need; /* Size needed on heap. */
Eterm res = THE_NON_VALUE;
+ erts_aint32_t state = 0;
+ erts_aint32_t prio = (erts_aint32_t) PRIORITY_NORMAL;
#ifdef ERTS_SMP
erts_smp_proc_lock(parent, ERTS_PROC_LOCKS_ALL_MINOR);
@@ -7340,8 +7742,24 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
so->error_code = BADARG;
goto error;
}
- p = alloc_process(); /* All proc locks are locked by this thread
- on success */
+
+ if (so->flags & SPO_USE_ARGS) {
+ if (so->scheduler) {
+ int ix = so->scheduler-1;
+ ASSERT(0 <= ix && ix < erts_no_run_queues);
+ rq = ERTS_RUNQ_IX(ix);
+ state |= ERTS_PSFLG_BOUND;
+ }
+ prio = (erts_aint32_t) so->priority;
+ }
+
+ state |= (prio & ERTS_PSFLG_PRIO_MASK);
+
+ if (!rq)
+ rq = erts_get_runq_proc(parent);
+
+ p = alloc_process(rq, state); /* All proc locks are locked by this thread
+ on success */
if (!p) {
erts_send_error_to_logger_str(parent->group_leader,
"Too many processes\n");
@@ -7361,22 +7779,16 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->flags = erts_default_process_flags;
- /* Scheduler queue mutex should be locked when changeing
- * prio. In this case we don't have to lock it, since
- * noone except us has access to the process.
- */
if (so->flags & SPO_USE_ARGS) {
p->min_heap_size = so->min_heap_size;
p->min_vheap_size = so->min_vheap_size;
- p->prio = so->priority;
p->max_gen_gcs = so->max_gen_gcs;
} else {
p->min_heap_size = H_MIN_SIZE;
p->min_vheap_size = BIN_VH_MIN_SIZE;
- p->prio = PRIORITY_NORMAL;
p->max_gen_gcs = (Uint16) erts_smp_atomic32_read_nob(&erts_max_gen_gcs);
}
- p->skipped = 0;
+ p->schedule_count = 0;
ASSERT(p->min_heap_size == erts_next_heap_size(p->min_heap_size, 0));
p->initial[INITIAL_MOD] = mod;
@@ -7445,9 +7857,9 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->reds = 0;
#ifdef ERTS_SMP
- p->u.ptimer = NULL;
+ p->u.alive.ptimer = NULL;
#else
- sys_memset(&p->u.tm, 0, sizeof(ErlTimer));
+ sys_memset(&p->u.alive.tm, 0, sizeof(ErlTimer));
#endif
p->reg = NULL;
@@ -7475,10 +7887,9 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->msg.save = &p->msg.first;
p->msg.len = 0;
#ifdef ERTS_SMP
- p->msg_inq.first = NULL;
- p->msg_inq.last = &p->msg_inq.first;
- p->msg_inq.len = 0;
- p->bound_runq = NULL;
+ p->u.alive.msg_inq.first = NULL;
+ p->u.alive.msg_inq.last = &p->u.alive.msg_inq.first;
+ p->u.alive.msg_inq.len = 0;
#endif
p->bif_timers = NULL;
p->mbuf = NULL;
@@ -7564,47 +7975,25 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
#ifdef ERTS_SMP
p->scheduler_data = NULL;
- p->is_exiting = 0;
- p->status_flags = 0;
- p->runq_flags = 0;
p->suspendee = NIL;
p->pending_suspenders = NULL;
- p->pending_exit.reason = THE_NON_VALUE;
- p->pending_exit.bp = NULL;
+ p->u.alive.pending_exit.reason = THE_NON_VALUE;
+ p->u.alive.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);
+
+ res = p->id;
+
/*
* Schedule process for execution.
*/
- if (!((so->flags & SPO_USE_ARGS) && so->scheduler))
- rq = erts_get_runq_proc(parent);
- else {
- int ix = so->scheduler-1;
- ASSERT(0 <= ix && ix < erts_no_run_queues);
- rq = ERTS_RUNQ_IX(ix);
- p->bound_runq = rq;
- }
-
- erts_smp_runq_lock(rq);
-
-#ifdef ERTS_SMP
- p->run_queue = rq;
-#endif
-
- p->status = P_WAITING;
- notify_runq = internal_add_to_runq(rq, p);
-
- erts_smp_runq_unlock(rq);
-
- smp_notify_inc_runq(notify_runq);
-
- res = p->id;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
+ schedule_process(p, state, 0);
VERBOSE(DEBUG_PROCESSES, ("Created a new process: %T\n",p->id));
@@ -7640,12 +8029,8 @@ void erts_init_empty_process(Process *p)
p->max_gen_gcs = 0;
p->min_heap_size = 0;
p->min_vheap_size = 0;
- p->status = P_RUNABLE;
- p->gcstatus = P_RUNABLE;
- p->rstatus = P_RUNABLE;
p->rcount = 0;
p->id = ERTS_INVALID_PID;
- p->prio = PRIORITY_NORMAL;
p->reds = 0;
p->tracer_proc = NIL;
p->trace_flags = F_INITIAL_TRACE_FLAGS;
@@ -7661,10 +8046,9 @@ void erts_init_empty_process(Process *p)
p->bin_old_vheap = 0;
p->bin_vheap_mature = 0;
#ifdef ERTS_SMP
- p->u.ptimer = NULL;
- p->bound_runq = NULL;
+ p->u.alive.ptimer = NULL;
#else
- memset(&(p->u.tm), 0, sizeof(ErlTimer));
+ memset(&(p->u.alive.tm), 0, sizeof(ErlTimer));
#endif
p->next = NULL;
p->off_heap.first = NULL;
@@ -7713,8 +8097,8 @@ void erts_init_empty_process(Process *p)
p->def_arg_reg[5] = 0;
p->parent = NIL;
- p->started.tv_sec = 0;
- p->started.tv_usec = 0;
+ p->approx_started = 0;
+ p->started_interval = 0;
#ifdef HIPE
hipe_init_process(&p->hipe);
@@ -7728,22 +8112,20 @@ void erts_init_empty_process(Process *p)
p->last_old_htop = NULL;
#endif
+ erts_smp_atomic32_init_nob(&p->state, (erts_aint32_t) PRIORITY_NORMAL);
#ifdef ERTS_SMP
p->scheduler_data = NULL;
- p->is_exiting = 0;
- p->status_flags = 0;
- p->runq_flags = 0;
- p->msg_inq.first = NULL;
- p->msg_inq.last = &p->msg_inq.first;
- p->msg_inq.len = 0;
+ p->u.alive.msg_inq.first = NULL;
+ p->u.alive.msg_inq.last = &p->u.alive.msg_inq.first;
+ p->u.alive.msg_inq.len = 0;
p->suspendee = NIL;
p->pending_suspenders = NULL;
- p->pending_exit.reason = THE_NON_VALUE;
- p->pending_exit.bp = NULL;
+ p->u.alive.pending_exit.reason = THE_NON_VALUE;
+ p->u.alive.pending_exit.bp = NULL;
erts_proc_lock_init(p);
erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
- p->run_queue = ERTS_RUNQ_IX(0);
+ RUNQ_SET_RQ(&p->run_queue, ERTS_RUNQ_IX(0));
#endif
#if !defined(NO_FPE_SIGNALS) || defined(HIPE)
@@ -7790,12 +8172,12 @@ erts_debug_verify_clean_empty_process(Process* p)
ASSERT(p->parent == NIL);
#ifdef ERTS_SMP
- ASSERT(p->msg_inq.first == NULL);
- ASSERT(p->msg_inq.len == 0);
+ ASSERT(p->u.alive.msg_inq.first == NULL);
+ ASSERT(p->u.alive.msg_inq.len == 0);
ASSERT(p->suspendee == NIL);
ASSERT(p->pending_suspenders == NULL);
- ASSERT(p->pending_exit.reason == THE_NON_VALUE);
- ASSERT(p->pending_exit.bp == NULL);
+ ASSERT(p->u.alive.pending_exit.reason == THE_NON_VALUE);
+ ASSERT(p->u.alive.pending_exit.bp == NULL);
#endif
/* Thing that erts_cleanup_empty_process() cleans up */
@@ -7821,8 +8203,8 @@ erts_cleanup_empty_process(Process* p)
free_message_buffer(p->mbuf);
p->mbuf = NULL;
}
-#if defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP)
- erts_lcnt_proc_lock_destroy(p);
+#ifdef ERTS_SMP
+ erts_proc_lock_fin(p);
#endif
#ifdef DEBUG
erts_debug_verify_clean_empty_process(p);
@@ -7919,26 +8301,34 @@ delete_process(Process* p)
p->fvalue = NIL;
}
+static ERTS_INLINE erts_aint32_t
+set_proc_exiting_state(Process *p, erts_aint32_t state)
+{
+ erts_aint32_t a, n, e;
+ a = state;
+ while (1) {
+ n = e = a;
+ n &= ~(ERTS_PSFLG_SUSPENDED|ERTS_PSFLG_PENDING_EXIT);
+ n |= ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE;
+ if (!(a & (ERTS_PSFLG_IN_RUNQ|ERTS_PSFLG_RUNNING)))
+ n |= ERTS_PSFLG_IN_RUNQ;
+ a = erts_smp_atomic32_cmpxchg_relb(&p->state, n, e);
+ if (a == e)
+ break;
+ }
+ return a;
+}
+
static ERTS_INLINE void
-set_proc_exiting(Process *p, Eterm reason, ErlHeapFragment *bp)
+set_proc_exiting(Process *p,
+ erts_aint32_t state,
+ Eterm reason,
+ ErlHeapFragment *bp)
{
-#ifdef ERTS_SMP
- erts_pix_lock_t *pix_lock = ERTS_PID2PIXLOCK(p->id);
ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(p) == ERTS_PROC_LOCKS_ALL);
- /*
- * You are required to have all proc locks and the pix lock when going
- * to status P_EXITING. This makes it is enough to take any lock when
- * looking up a process (pid2proc()) to prevent the looked up process
- * from exiting until the lock has been released.
- */
- erts_pix_lock(pix_lock);
- p->is_exiting = 1;
-#endif
- p->status = P_EXITING;
-#ifdef ERTS_SMP
- erts_pix_unlock(pix_lock);
-#endif
+ state = set_proc_exiting_state(p, state);
+
p->fvalue = reason;
if (bp)
erts_link_mbuf_to_proc(p, bp);
@@ -7951,6 +8341,14 @@ set_proc_exiting(Process *p, Eterm reason, ErlHeapFragment *bp)
KILL_CATCHES(p);
cancel_timer(p);
p->i = (BeamInstr *) beam_exit;
+
+ if (erts_system_profile_flags.runnable_procs
+ && !(state & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED))) {
+ profile_runnable_proc(p, am_active);
+ }
+
+ if (!(state & (ERTS_PSFLG_IN_RUNQ|ERTS_PSFLG_RUNNING)))
+ add2runq(p, state);
}
@@ -7960,11 +8358,11 @@ void
erts_handle_pending_exit(Process *c_p, ErtsProcLocks locks)
{
ErtsProcLocks xlocks;
- ASSERT(is_value(c_p->pending_exit.reason));
+ ASSERT(is_value(c_p->u.alive.pending_exit.reason));
ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == locks);
ERTS_SMP_LC_ASSERT(locks & ERTS_PROC_LOCK_MAIN);
- ERTS_SMP_LC_ASSERT(c_p->status != P_EXITING);
- ERTS_SMP_LC_ASSERT(c_p->status != P_FREE);
+ ERTS_SMP_LC_ASSERT(!((ERTS_PSFLG_EXITING|ERTS_PSFLG_FREE)
+ & erts_smp_atomic32_read_nob(&c_p->state)));
/* Ensure that all locks on c_p are locked before proceeding... */
if (locks == ERTS_PROC_LOCKS_ALL)
@@ -7977,9 +8375,12 @@ erts_handle_pending_exit(Process *c_p, ErtsProcLocks locks)
}
}
- set_proc_exiting(c_p, c_p->pending_exit.reason, c_p->pending_exit.bp);
- c_p->pending_exit.reason = THE_NON_VALUE;
- c_p->pending_exit.bp = NULL;
+ set_proc_exiting(c_p,
+ erts_smp_atomic32_read_acqb(&c_p->state),
+ c_p->u.alive.pending_exit.reason,
+ c_p->u.alive.pending_exit.bp);
+ c_p->u.alive.pending_exit.reason = THE_NON_VALUE;
+ c_p->u.alive.pending_exit.bp = NULL;
if (xlocks)
erts_smp_proc_unlock(c_p, xlocks);
@@ -7993,11 +8394,12 @@ handle_pending_exiters(ErtsProcList *pnd_xtrs)
while (plp) {
Process *p = erts_pid2proc(NULL, 0, plp->pid, ERTS_PROC_LOCKS_ALL);
if (p) {
- if (proclist_same(plp, p)
- && !(p->status_flags & ERTS_PROC_SFLG_RUNNING)) {
- ASSERT(p->status_flags & ERTS_PROC_SFLG_INRUNQ);
- ASSERT(ERTS_PROC_PENDING_EXIT(p));
- erts_handle_pending_exit(p, ERTS_PROC_LOCKS_ALL);
+ if (proclist_same(plp, p)) {
+ erts_aint32_t state = erts_smp_atomic32_read_acqb(&p->state);
+ if (!(state & ERTS_PSFLG_RUNNING)) {
+ ASSERT(state & ERTS_PSFLG_PENDING_EXIT);
+ erts_handle_pending_exit(p, ERTS_PROC_LOCKS_ALL);
+ }
}
erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
}
@@ -8025,7 +8427,7 @@ save_pending_exiter(Process *p)
rq->procs.pending_exiters = plp;
erts_smp_runq_unlock(rq);
-
+ wake_scheduler(rq, 1);
}
#endif
@@ -8087,7 +8489,7 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp,
* SMP emulator). When the signal is received the receiver receives an
* 'EXIT' message if it is trapping exits; otherwise, it will either
* ignore the signal if the exit reason is normal, or go into an
- * exiting state (status P_EXITING). When a process has gone into the
+ * exiting state (ERTS_PSFLG_EXITING). When a process has gone into the
* exiting state it will not execute any more Erlang code, but it might
* take a while before it actually exits. The exit signal is being
* received when the 'EXIT' message is put in the message queue, the
@@ -8160,6 +8562,7 @@ send_exit_signal(Process *c_p, /* current process if and only
Uint32 flags /* flags */
)
{
+ erts_aint32_t state = erts_smp_atomic32_read_nob(&rp->state);
Eterm rsn = reason == am_kill ? am_killed : reason;
ERTS_SMP_LC_ASSERT(*rp_locks == erts_proc_lc_my_proc_locks(rp));
@@ -8181,7 +8584,7 @@ send_exit_signal(Process *c_p, /* current process if and only
}
#endif
- if (ERTS_PROC_IS_TRAPPING_EXITS(rp)
+ if ((state & ERTS_PSFLG_TRAP_EXIT)
&& (reason != am_kill || (flags & ERTS_XSIG_FLG_IGN_KILL))) {
if (is_not_nil(token)
#ifdef USE_VM_PROBES
@@ -8197,10 +8600,8 @@ send_exit_signal(Process *c_p, /* current process if and only
}
else if (reason != am_normal || (flags & ERTS_XSIG_FLG_NO_IGN_NORMAL)) {
#ifdef ERTS_SMP
- if (!ERTS_PROC_PENDING_EXIT(rp) && !rp->is_exiting) {
- ASSERT(rp->status != P_EXITING);
- ASSERT(rp->status != P_FREE);
- ASSERT(!rp->pending_exit.bp);
+ if (!(state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT))) {
+ ASSERT(!rp->u.alive.pending_exit.bp);
if (rp == c_p && (*rp_locks & ERTS_PROC_LOCK_MAIN)) {
/* Ensure that all locks on c_p are locked before
@@ -8215,9 +8616,9 @@ send_exit_signal(Process *c_p, /* current process if and only
}
*rp_locks = ERTS_PROC_LOCKS_ALL;
}
- set_proc_exiting(c_p, rsn, NULL);
+ set_proc_exiting(c_p, state, rsn, NULL);
}
- else if (!(rp->status_flags & ERTS_PROC_SFLG_RUNNING)) {
+ else if (!(state & ERTS_PSFLG_RUNNING)) {
/* Process not running ... */
ErtsProcLocks need_locks = ~(*rp_locks) & ERTS_PROC_LOCKS_ALL;
if (need_locks
@@ -8234,6 +8635,7 @@ 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)),
@@ -8249,7 +8651,7 @@ send_exit_signal(Process *c_p, /* current process if and only
set_pending_exit:
if (is_immed(rsn)) {
- rp->pending_exit.reason = rsn;
+ rp->u.alive.pending_exit.reason = rsn;
}
else {
Eterm *hp;
@@ -8257,17 +8659,15 @@ send_exit_signal(Process *c_p, /* current process if and only
ErlHeapFragment *bp = new_message_buffer(sz);
hp = &bp->mem[0];
- rp->pending_exit.reason = copy_struct(rsn,
- sz,
- &hp,
- &bp->off_heap);
- rp->pending_exit.bp = bp;
+ rp->u.alive.pending_exit.reason = copy_struct(rsn,
+ sz,
+ &hp,
+ &bp->off_heap);
+ rp->u.alive.pending_exit.bp = bp;
}
- ASSERT(ERTS_PROC_PENDING_EXIT(rp));
+ erts_smp_atomic32_read_bor_relb(&rp->state,
+ ERTS_PSFLG_PENDING_EXIT);
}
- if (!(rp->status_flags
- & (ERTS_PROC_SFLG_INRUNQ|ERTS_PROC_SFLG_RUNNING)))
- erts_add_to_runq(rp);
}
/* else:
*
@@ -8279,17 +8679,14 @@ send_exit_signal(Process *c_p, /* current process if and only
* exit or by itself before seeing the pending exit.
*/
#else /* !ERTS_SMP */
- if (c_p == rp) {
- rp->status = P_EXITING;
- c_p->fvalue = rsn;
- }
- else if (rp->status != P_EXITING) { /* No recursive process exits /PaN */
- Eterm old_status = rp->status;
+ erts_aint32_t state = erts_smp_atomic32_read_nob(&rp->state);
+ if (!(state & ERTS_PSFLG_EXITING)) {
set_proc_exiting(rp,
- is_immed(rsn) ? rsn : copy_object(rsn, rp),
+ state,
+ (is_immed(rsn) || c_p == rp
+ ? rsn
+ : copy_object(rsn, rp)),
NULL);
- if (old_status != P_RUNABLE && old_status != P_RUNNING)
- erts_add_to_runq(rp);
}
#endif
return -1; /* Receiver will exit */
@@ -8592,12 +8989,14 @@ resume_suspend_monitor(ErtsSuspendMonitor *smon, void *vc_p)
erts_destroy_suspend_monitor(smon);
}
-static void
-continue_exit_process(Process *p
#ifdef ERTS_SMP
- , erts_pix_lock_t *pix_lock
+static void
+proc_dec_refc(void *vproc)
+{
+ erts_smp_proc_dec_refc((Process *) vproc);
+}
#endif
- );
+
/* this function fishishes a process and propagates exit messages - called
by process_main when a process dies */
@@ -8605,9 +9004,8 @@ void
erts_do_exit_process(Process* p, Eterm reason)
{
#ifdef ERTS_SMP
- erts_pix_lock_t *pix_lock = ERTS_PID2PIXLOCK(p->id);
+ erts_aint32_t state;
#endif
-
p->arity = 0; /* No live registers */
p->fvalue = reason;
@@ -8625,32 +9023,22 @@ erts_do_exit_process(Process* p, Eterm reason)
#ifdef ERTS_SMP
ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
/* By locking all locks (main lock is already locked) when going
- to status P_EXITING, it is enough to take any lock when
+ to exiting state (ERTS_PSFLG_EXITING), it is enough to take any lock when
looking up a process (erts_pid2proc()) to prevent the looked up
process from exiting until the lock has been released. */
erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
#endif
-
- if (erts_system_profile_flags.runnable_procs && (p->status != P_WAITING)) {
- profile_runnable_proc(p, am_inactive);
- }
-
-#ifdef ERTS_SMP
- erts_pix_lock(pix_lock);
- p->is_exiting = 1;
-#endif
-
- p->status = P_EXITING;
-#ifdef ERTS_SMP
- erts_pix_unlock(pix_lock);
-
- if (ERTS_PROC_PENDING_EXIT(p)) {
+#ifndef ERTS_SMP
+ set_proc_exiting_state(p, erts_smp_atomic32_read_nob(&p->state));
+#else
+ state = set_proc_exiting_state(p, erts_smp_atomic32_read_nob(&p->state));
+ if (state & ERTS_PSFLG_PENDING_EXIT) {
/* Process exited before pending exit was received... */
- p->pending_exit.reason = THE_NON_VALUE;
- if (p->pending_exit.bp) {
- free_message_buffer(p->pending_exit.bp);
- p->pending_exit.bp = NULL;
+ p->u.alive.pending_exit.reason = THE_NON_VALUE;
+ if (p->u.alive.pending_exit.bp) {
+ free_message_buffer(p->u.alive.pending_exit.bp);
+ p->u.alive.pending_exit.bp = NULL;
}
}
@@ -8684,29 +9072,11 @@ erts_do_exit_process(Process* p, Eterm reason)
erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
-#ifdef ERTS_SMP
- continue_exit_process(p, pix_lock);
-#else
- continue_exit_process(p);
-#endif
+ erts_continue_exit_process(p);
}
void
-erts_continue_exit_process(Process *c_p)
-{
-#ifdef ERTS_SMP
- continue_exit_process(c_p, ERTS_PID2PIXLOCK(c_p->id));
-#else
- continue_exit_process(c_p);
-#endif
-}
-
-static void
-continue_exit_process(Process *p
-#ifdef ERTS_SMP
- , erts_pix_lock_t *pix_lock
-#endif
- )
+erts_continue_exit_process(Process *p)
{
ErtsLink* lnk;
ErtsMonitor *mon;
@@ -8722,11 +9092,7 @@ continue_exit_process(Process *p
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(p));
-#ifdef DEBUG
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- ASSERT(p->status == P_EXITING);
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
-#endif
+ ASSERT(ERTS_PROC_IS_EXITING(p));
#ifdef ERTS_SMP
if (p->flags & F_HAVE_BLCKD_MSCHED) {
@@ -8794,48 +9160,47 @@ continue_exit_process(Process *p
#endif
{
+ int maybe_save;
int pix;
/* Do *not* use erts_get_runq_proc() */
ErtsRunQueue *rq;
rq = erts_get_runq_current(ERTS_GET_SCHEDULER_DATA_FROM_PROC(p));
- ASSERT(internal_pid_index(p->id) < erts_max_processes);
pix = internal_pid_index(p->id);
- erts_smp_mtx_lock(&proc_tab_mtx);
+ erts_smp_rwmtx_rlock(&erts_proc_tab_rwmtx);
+ maybe_save = saved_term_procs.end != NULL;
+ if (maybe_save) {
+ erts_smp_rwmtx_runlock(&erts_proc_tab_rwmtx);
+ erts_smp_rwmtx_rwlock(&erts_proc_tab_rwmtx);
+ }
+
erts_smp_runq_lock(rq);
#ifdef ERTS_SMP
- erts_pix_lock(pix_lock);
-
ASSERT(p->scheduler_data);
ASSERT(p->scheduler_data->current_process == p);
ASSERT(p->scheduler_data->free_process == NULL);
p->scheduler_data->current_process = NULL;
p->scheduler_data->free_process = p;
- p->status_flags = 0;
#endif
- process_tab[pix] = NULL; /* Time of death! */
+ /* Time of death! */
+ erts_smp_atomic_set_relb(&erts_proc.tab[pix], ERTS_AINT_NULL);
+
ASSERT(erts_smp_atomic32_read_nob(&process_count) > 0);
- erts_smp_atomic32_dec_nob(&process_count);
+ erts_smp_atomic32_dec_relb(&process_count);
-#ifdef ERTS_SMP
- erts_pix_unlock(pix_lock);
-#endif
erts_smp_runq_unlock(rq);
- if (p_next < 0) {
- if (p_last >= p_next) {
- p_serial++;
- p_serial &= p_serial_mask;
- }
- p_next = pix;
+ if (!maybe_save)
+ erts_smp_rwmtx_runlock(&erts_proc_tab_rwmtx);
+ else {
+ if (saved_term_procs.end)
+ save_terminating_process(p);
+ erts_smp_rwmtx_rwunlock(&erts_proc_tab_rwmtx);
}
- ERTS_MAYBE_SAVE_TERMINATING_PROCESS(p);
-
- erts_smp_mtx_unlock(&proc_tab_mtx);
}
/*
@@ -8850,7 +9215,21 @@ continue_exit_process(Process *p
lnk = p->nlinks;
p->nlinks = NULL;
- p->status = P_FREE;
+
+ {
+ /* Inactivate and notify free */
+ erts_aint32_t n, e, a = erts_smp_atomic32_read_nob(&p->state);
+ while (1) {
+ n = e = a;
+ ASSERT(a & ERTS_PSFLG_EXITING);
+ n |= ERTS_PSFLG_FREE;
+ n &= ~ERTS_PSFLG_ACTIVE;
+ a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e);
+ if (a == e)
+ break;
+ }
+ }
+
dep = ((p->flags & F_DISTRIBUTION)
? ERTS_PROC_SET_DIST_ENTRY(p, ERTS_PROC_LOCKS_ALL, NULL)
: NULL);
@@ -8905,8 +9284,13 @@ continue_exit_process(Process *p
delete_process(p);
+#ifdef ERTS_SMP
+ erts_schedule_thr_prgr_later_op(proc_dec_refc,
+ (void *) p,
+ &p->u.release_data);
erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
+#endif
return;
@@ -8919,8 +9303,6 @@ continue_exit_process(Process *p
ERTS_SMP_LC_ASSERT(curr_locks == erts_proc_lc_my_proc_locks(p));
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN & curr_locks);
- ASSERT(p->status == P_EXITING);
-
p->i = (BeamInstr *) beam_continue_exit;
if (!(curr_locks & ERTS_PROC_LOCK_STATUS)) {
@@ -8928,8 +9310,6 @@ continue_exit_process(Process *p
curr_locks |= ERTS_PROC_LOCK_STATUS;
}
- erts_add_to_runq(p);
-
if (curr_locks != ERTS_PROC_LOCK_MAIN)
erts_smp_proc_unlock(p, ~ERTS_PROC_LOCK_MAIN & curr_locks);
@@ -8941,33 +9321,15 @@ continue_exit_process(Process *p
static void
timeout_proc(Process* p)
{
+ erts_aint32_t state;
BeamInstr** pi = (BeamInstr **) p->def_arg_reg;
p->i = *pi;
p->flags |= F_TIMO;
p->flags &= ~F_INSLPQUEUE;
- switch (p->status) {
- case P_GARBING:
- switch (p->gcstatus) {
- case P_SUSPENDED:
- goto suspended;
- case P_WAITING:
- goto waiting;
- default:
- break;
- }
- break;
- case P_WAITING:
- waiting:
- erts_add_to_runq(p);
- break;
- case P_SUSPENDED:
- suspended:
- p->rstatus = P_RUNABLE; /* MUST set resume status to runnable */
- break;
- default:
- break;
- }
+ state = erts_smp_atomic32_read_acqb(&p->state);
+ if (!(state & ERTS_PSFLG_ACTIVE))
+ schedule_process(p, state, 0);
}
@@ -8977,9 +9339,9 @@ cancel_timer(Process* p)
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(p));
p->flags &= ~(F_INSLPQUEUE|F_TIMO);
#ifdef ERTS_SMP
- erts_cancel_smp_ptimer(p->u.ptimer);
+ erts_cancel_smp_ptimer(p->u.alive.ptimer);
#else
- erts_cancel_timer(&p->u.tm);
+ erts_cancel_timer(&p->u.alive.tm);
#endif
}
@@ -9000,12 +9362,12 @@ set_timer(Process* p, Uint timeout)
p->flags &= ~F_TIMO;
#ifdef ERTS_SMP
- erts_create_smp_ptimer(&p->u.ptimer,
+ erts_create_smp_ptimer(&p->u.alive.ptimer,
p->id,
(ErlTimeoutProc) timeout_proc,
timeout);
#else
- erts_set_timer(&p->u.tm,
+ erts_set_timer(&p->u.alive.tm,
(ErlTimeoutProc) timeout_proc,
NULL,
(void*) p,
@@ -9035,6 +9397,7 @@ erts_stack_dump(int to, void *to_arg, Process *p)
void
erts_program_counter_info(int to, void *to_arg, Process *p)
{
+ erts_aint32_t state;
int i;
erts_print(to, to_arg, "Program counter: %p (", p->i);
@@ -9043,7 +9406,8 @@ erts_program_counter_info(int to, void *to_arg, Process *p)
erts_print(to, to_arg, "CP: %p (", p->cp);
print_function_from_pc(to, to_arg, p->cp);
erts_print(to, to_arg, ")\n");
- if (!((p->status == P_RUNNING) || (p->status == P_GARBING))) {
+ state = erts_smp_atomic32_read_acqb(&p->state);
+ if (!(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_GC))) {
erts_print(to, to_arg, "arity = %d\n",p->arity);
if (!ERTS_IS_CRASH_DUMPING) {
/*
@@ -9089,7 +9453,7 @@ stack_element_dump(int to, void *to_arg, Process* p, Eterm* sp, int yreg)
erts_print(to, to_arg, "\n%p ", sp);
} else {
char sbuf[16];
- sprintf(sbuf, "y(%d)", yreg);
+ erts_snprintf(sbuf, sizeof(sbuf), "y(%d)", yreg);
erts_print(to, to_arg, "%-8s ", sbuf);
yreg++;
}
@@ -9216,13 +9580,13 @@ do { \
#endif
#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_FOUND_PIDS
-# define ERTS_PROCS_DBG_CHK_PID_FOUND(PBDP, PID, TVP) \
- debug_processes_check_found_pid((PBDP), (PID), (TVP), 1)
-# define ERTS_PROCS_DBG_CHK_PID_NOT_FOUND(PBDP, PID, TVP) \
- debug_processes_check_found_pid((PBDP), (PID), (TVP), 0)
+# define ERTS_PROCS_DBG_CHK_PID_FOUND(PBDP, PID, IC) \
+ debug_processes_check_found_pid((PBDP), (PID), (IC), 1)
+# define ERTS_PROCS_DBG_CHK_PID_NOT_FOUND(PBDP, PID, IC) \
+ debug_processes_check_found_pid((PBDP), (PID), (IC), 0)
#else
-# define ERTS_PROCS_DBG_CHK_PID_FOUND(PBDP, PID, TVP)
-# define ERTS_PROCS_DBG_CHK_PID_NOT_FOUND(PBDP, PID, TVP)
+# define ERTS_PROCS_DBG_CHK_PID_FOUND(PBDP, PID, IC)
+# define ERTS_PROCS_DBG_CHK_PID_NOT_FOUND(PBDP, PID, IC)
#endif
#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_TERM_PROC_LIST
@@ -9267,7 +9631,7 @@ static Uint processes_bif_tab_chunks;
static Export processes_trap_export;
typedef struct {
- SysTimeval time;
+ Uint64 interval;
} ErtsProcessesBifChunkInfo;
typedef enum {
@@ -9292,7 +9656,7 @@ typedef struct {
struct {
Eterm caller;
#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_FOUND_PIDS
- SysTimeval *pid_started;
+ Uint64 *pid_started;
#endif
#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_HALLOC
Eterm *heap;
@@ -9321,11 +9685,10 @@ static void debug_processes_verify_all_pids(ErtsProcessesBifData *pbdp);
#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_FOUND_PIDS
static void debug_processes_check_found_pid(ErtsProcessesBifData *pbdp,
Eterm pid,
- SysTimeval *started,
+ Uint64 ic,
int pid_should_be_found);
#endif
#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_TERM_PROC_LIST
-static SysTimeval debug_tv_start;
static void debug_processes_check_term_proc_list(void);
static void debug_processes_check_term_proc_free_list(ErtsTermProcElement *tpep);
#endif
@@ -9336,7 +9699,7 @@ save_terminating_process(Process *p)
ErtsTermProcElement *tpep = erts_alloc(ERTS_ALC_T_PROCS_TPROC_EL,
sizeof(ErtsTermProcElement));
ERTS_PROCS_ASSERT(saved_term_procs.start && saved_term_procs.end);
- ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&proc_tab_mtx));
+ ERTS_SMP_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&erts_proc_tab_rwmtx));
ERTS_PROCS_DBG_CHK_TPLIST();
@@ -9344,19 +9707,19 @@ save_terminating_process(Process *p)
tpep->next = NULL;
tpep->ix = internal_pid_index(p->id);
tpep->u.process.pid = p->id;
- tpep->u.process.spawned = p->started;
- erts_get_emu_time(&tpep->u.process.exited);
+ tpep->u.process.spawned = p->started_interval;
+ tpep->u.process.exited = get_proc_interval();
saved_term_procs.end->next = tpep;
saved_term_procs.end = tpep;
ERTS_PROCS_DBG_CHK_TPLIST();
- ERTS_PROCS_ASSERT((tpep->prev->ix >= 0
- ? erts_cmp_timeval(&tpep->u.process.exited,
- &tpep->prev->u.process.exited)
- : erts_cmp_timeval(&tpep->u.process.exited,
- &tpep->prev->u.bif_invocation.time)) > 0);
+ ERTS_PROCS_ASSERT(tpep->prev->ix >= 0
+ ? (tpep->u.process.exited
+ >= tpep->prev->u.process.exited)
+ : (tpep->u.process.exited
+ >= tpep->prev->u.bif_invocation.interval));
}
static void
@@ -9387,7 +9750,7 @@ cleanup_processes_bif_data(Binary *bp)
if (pbdp->bif_invocation) {
ErtsTermProcElement *tpep;
- erts_smp_mtx_lock(&proc_tab_mtx);
+ erts_smp_rwmtx_rwlock(&erts_proc_tab_rwmtx);
ERTS_PROCS_DBG_TRACE(pbdp->debug.caller,
cleanup_processes_bif_data,
@@ -9441,7 +9804,7 @@ cleanup_processes_bif_data(Binary *bp)
ERTS_PROCS_DBG_CHK_TPLIST();
- erts_smp_mtx_unlock(&proc_tab_mtx);
+ erts_smp_rwmtx_rwunlock(&erts_proc_tab_rwmtx);
}
}
@@ -9469,7 +9832,7 @@ processes_bif_engine(Process *p, Eterm *res_accp, Binary *mbp)
pbdp->tix = 0;
pbdp->pid_ix = 0;
- erts_smp_mtx_lock(&proc_tab_mtx);
+ erts_smp_rwmtx_rwlock(&erts_proc_tab_rwmtx);
locked = 1;
ERTS_PROCS_DBG_TRACE(p->id, processes_bif_engine, init);
@@ -9480,7 +9843,7 @@ processes_bif_engine(Process *p, Eterm *res_accp, Binary *mbp)
#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_FOUND_PIDS
pbdp->debug.pid_started = erts_alloc(ERTS_ALC_T_PROCS_PIDS,
- sizeof(SysTimeval)*pbdp->pid_sz);
+ sizeof(Uint64)*pbdp->pid_sz);
#endif
ERTS_PROCS_DBG_SAVE_PIDS(pbdp);
@@ -9495,7 +9858,8 @@ processes_bif_engine(Process *p, Eterm *res_accp, Binary *mbp)
pbdp->bif_invocation = erts_alloc(ERTS_ALC_T_PROCS_TPROC_EL,
sizeof(ErtsTermProcElement));
pbdp->bif_invocation->ix = -1;
- erts_get_emu_time(&pbdp->bif_invocation->u.bif_invocation.time);
+ pbdp->bif_invocation->u.bif_invocation.interval
+ = step_proc_interval();
ERTS_PROCS_DBG_CHK_TPLIST();
pbdp->bif_invocation->next = NULL;
@@ -9522,30 +9886,31 @@ processes_bif_engine(Process *p, Eterm *res_accp, Binary *mbp)
int indices = ERTS_PROCESSES_BIF_TAB_CHUNK_SIZE;
int cix = ix / ERTS_PROCESSES_BIF_TAB_CHUNK_SIZE;
int end_ix = ix + indices;
- SysTimeval *invocation_timep;
+ Uint64 *invocation_interval_p;
- invocation_timep = (pbdp->bif_invocation
- ? &pbdp->bif_invocation->u.bif_invocation.time
- : NULL);
+ invocation_interval_p
+ = (pbdp->bif_invocation
+ ? &pbdp->bif_invocation->u.bif_invocation.interval
+ : NULL);
ERTS_PROCS_ASSERT(is_nil(*res_accp));
if (!locked) {
- erts_smp_mtx_lock(&proc_tab_mtx);
+ erts_smp_rwmtx_rwlock(&erts_proc_tab_rwmtx);
locked = 1;
}
- ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&proc_tab_mtx));
+ ERTS_SMP_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&erts_proc_tab_rwmtx));
ERTS_PROCS_DBG_TRACE(p->id, processes_bif_engine, insp_table);
if (cix != 0)
- erts_get_emu_time(&pbdp->chunk[cix].time);
+ pbdp->chunk[cix].interval = step_proc_interval();
else if (pbdp->bif_invocation)
- pbdp->chunk[0].time = *invocation_timep;
- /* else: Time is irrelevant */
+ pbdp->chunk[0].interval = *invocation_interval_p;
+ /* else: interval is irrelevant */
- if (end_ix >= erts_max_processes) {
+ if (end_ix >= erts_proc.max) {
ERTS_PROCS_ASSERT(cix+1 == processes_bif_tab_chunks);
- end_ix = erts_max_processes;
+ end_ix = erts_proc.max;
indices = end_ix - ix;
/* What to do when done with this chunk */
pbdp->state = (processes_bif_tab_chunks == 1
@@ -9554,16 +9919,15 @@ processes_bif_engine(Process *p, Eterm *res_accp, Binary *mbp)
}
for (; ix < end_ix; ix++) {
- Process *rp = process_tab[ix];
+ Process *rp = erts_pix2proc(ix);
if (rp
- && (!invocation_timep
- || erts_cmp_timeval(&rp->started,
- invocation_timep) < 0)) {
+ && (!invocation_interval_p
+ || rp->started_interval < *invocation_interval_p)) {
ERTS_PROCS_ASSERT(is_internal_pid(rp->id));
pbdp->pid[pbdp->pid_ix] = rp->id;
#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_FOUND_PIDS
- pbdp->debug.pid_started[pbdp->pid_ix] = rp->started;
+ pbdp->debug.pid_started[pbdp->pid_ix] = rp->started_interval;
#endif
pbdp->pid_ix++;
@@ -9573,7 +9937,7 @@ processes_bif_engine(Process *p, Eterm *res_accp, Binary *mbp)
pbdp->tix = end_ix;
- erts_smp_mtx_unlock(&proc_tab_mtx);
+ erts_smp_rwmtx_rwunlock(&erts_proc_tab_rwmtx);
locked = 0;
reds = indices/ERTS_PROCESSES_BIF_TAB_INSPECT_INDICES_PER_RED;
@@ -9585,8 +9949,8 @@ processes_bif_engine(Process *p, Eterm *res_accp, Binary *mbp)
ix = pbdp->tix;
indices = ERTS_PROCESSES_BIF_TAB_CHUNK_SIZE;
end_ix = ix + indices;
- if (end_ix > erts_max_processes) {
- end_ix = erts_max_processes;
+ if (end_ix > erts_proc.max) {
+ end_ix = erts_proc.max;
indices = end_ix - ix;
}
@@ -9605,20 +9969,20 @@ processes_bif_engine(Process *p, Eterm *res_accp, Binary *mbp)
int i;
int max_reds;
int free_term_procs = 0;
- SysTimeval *invocation_timep;
+ Uint64 invocation_interval;
ErtsTermProcElement *tpep;
ErtsTermProcElement *free_list = NULL;
tpep = pbdp->bif_invocation;
ERTS_PROCS_ASSERT(tpep);
- invocation_timep = &tpep->u.bif_invocation.time;
+ invocation_interval = tpep->u.bif_invocation.interval;
max_reds = have_reds = ERTS_BIF_REDS_LEFT(p);
if (max_reds > ERTS_PROCESSES_INSPECT_TERM_PROC_MAX_REDS)
max_reds = ERTS_PROCESSES_INSPECT_TERM_PROC_MAX_REDS;
reds = 0;
- erts_smp_mtx_lock(&proc_tab_mtx);
+ erts_smp_rwmtx_rwlock(&erts_proc_tab_rwmtx);
ERTS_PROCS_DBG_TRACE(p->id, processes_bif_engine, insp_term_procs);
ERTS_PROCS_DBG_CHK_TPLIST();
@@ -9657,20 +10021,19 @@ processes_bif_engine(Process *p, Eterm *res_accp, Binary *mbp)
}
else {
int cix = tpep->ix/ERTS_PROCESSES_BIF_TAB_CHUNK_SIZE;
- SysTimeval *chunk_timep = &pbdp->chunk[cix].time;
+ Uint64 chunk_interval = pbdp->chunk[cix].interval;
Eterm pid = tpep->u.process.pid;
ERTS_PROCS_ASSERT(is_internal_pid(pid));
- if (erts_cmp_timeval(&tpep->u.process.spawned,
- invocation_timep) < 0) {
- if (erts_cmp_timeval(&tpep->u.process.exited,
- chunk_timep) < 0) {
+ if (tpep->u.process.spawned < invocation_interval) {
+ if (tpep->u.process.exited < chunk_interval) {
ERTS_PROCS_DBG_CHK_PID_NOT_FOUND(pbdp,
pid,
- &tpep->u.process.spawned);
+ tpep->u.process.spawned);
pbdp->pid[pbdp->pid_ix] = pid;
#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_FOUND_PIDS
- pbdp->debug.pid_started[pbdp->pid_ix] = tpep->u.process.spawned;
+ pbdp->debug.pid_started[pbdp->pid_ix]
+ = tpep->u.process.spawned;
#endif
pbdp->pid_ix++;
ERTS_PROCS_ASSERT(pbdp->pid_ix <= pbdp->pid_sz);
@@ -9678,13 +10041,13 @@ processes_bif_engine(Process *p, Eterm *res_accp, Binary *mbp)
else {
ERTS_PROCS_DBG_CHK_PID_FOUND(pbdp,
pid,
- &tpep->u.process.spawned);
+ tpep->u.process.spawned);
}
}
else {
ERTS_PROCS_DBG_CHK_PID_NOT_FOUND(pbdp,
pid,
- &tpep->u.process.spawned);
+ tpep->u.process.spawned);
}
i++;
@@ -9733,7 +10096,7 @@ processes_bif_engine(Process *p, Eterm *res_accp, Binary *mbp)
ERTS_PROCS_DBG_CHK_TPLIST();
ERTS_PROCS_DBG_CHK_FREELIST(free_list);
- erts_smp_mtx_unlock(&proc_tab_mtx);
+ erts_smp_rwmtx_rwunlock(&erts_proc_tab_rwmtx);
/*
* We do the actual free of term proc structures now when we
@@ -9793,8 +10156,9 @@ processes_bif_engine(Process *p, Eterm *res_accp, Binary *mbp)
sizeof(Eterm)*pbdp->pid_sz);
#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_FOUND_PIDS
pbdp->debug.pid_started = erts_realloc(ERTS_ALC_T_PROCS_PIDS,
- pbdp->debug.pid_started,
- sizeof(SysTimeval)*pbdp->pid_sz);
+ pbdp->debug.pid_started,
+ (sizeof(Uint64)
+ * pbdp->pid_sz));
#endif
}
reds = conses/ERTS_PROCESSES_BIF_BUILD_RESULT_CONSES_PER_RED;
@@ -9914,22 +10278,13 @@ init_processes_bif(void)
{
saved_term_procs.start = NULL;
saved_term_procs.end = NULL;
- processes_bif_tab_chunks = (((erts_max_processes - 1)
+ processes_bif_tab_chunks = (((erts_proc.max - 1)
/ ERTS_PROCESSES_BIF_TAB_CHUNK_SIZE)
+ 1);
/* processes_trap/2 is a hidden BIF that the processes/0 BIF traps to. */
- sys_memset((void *) &processes_trap_export, 0, sizeof(Export));
- processes_trap_export.address = &processes_trap_export.code[3];
- processes_trap_export.code[0] = am_erlang;
- processes_trap_export.code[1] = am_processes_trap;
- processes_trap_export.code[2] = 2;
- processes_trap_export.code[3] = (BeamInstr) em_apply_bif;
- processes_trap_export.code[4] = (BeamInstr) &processes_trap;
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_TERM_PROC_LIST
- erts_get_emu_time(&debug_tv_start);
-#endif
+ erts_init_trap_export(&processes_trap_export, am_erlang, am_processes_trap, 2,
+ &processes_trap);
}
@@ -9962,31 +10317,29 @@ erts_debug_processes(Process *c_p)
Eterm res;
Eterm* hp;
Process *p;
-#ifdef DEBUG
Eterm *hp_end;
-#endif
- erts_smp_mtx_lock(&proc_tab_mtx);
+ erts_smp_rwmtx_rwlock(&erts_proc_tab_rwmtx);
res = NIL;
need = erts_process_count() * 2;
hp = HAlloc(c_p, need); /* we need two heap words for each pid */
-#ifdef DEBUG
hp_end = hp + need;
-#endif
/* make the list by scanning bakward */
- for (i = erts_max_processes-1; i >= 0; i--) {
- if ((p = process_tab[i]) != NULL) {
- res = CONS(hp, process_tab[i]->id, res);
+ for (i = erts_proc.max-1; i >= 0; i--) {
+ p = erts_pix2proc(i);
+ if (p) {
+ res = CONS(hp, p->id, res);
hp += 2;
}
}
- ASSERT(hp == hp_end);
- erts_smp_mtx_unlock(&proc_tab_mtx);
+ erts_smp_rwmtx_rwunlock(&erts_proc_tab_rwmtx);
+
+ HRelease(c_p, hp_end, hp);
return res;
}
@@ -10018,14 +10371,12 @@ erts_debug_processes_bif_info(Process *c_p)
static void
debug_processes_check_found_pid(ErtsProcessesBifData *pbdp,
Eterm pid,
- SysTimeval *tvp,
+ Uint64 ic,
int pid_should_be_found)
{
int i;
for (i = 0; i < pbdp->pid_ix; i++) {
- if (pbdp->pid[i] == pid
- && pbdp->debug.pid_started[i].tv_sec == tvp->tv_sec
- && pbdp->debug.pid_started[i].tv_usec == tvp->tv_usec) {
+ if (pbdp->pid[i] == pid && pbdp->debug.pid_started[i] == ic) {
ERTS_PROCS_ASSERT(pid_should_be_found);
return;
}
@@ -10059,8 +10410,8 @@ debug_processes_save_all_pids(ErtsProcessesBifData *pbdp)
pbdp->debug.correct_pids = erts_alloc(ERTS_ALC_T_PROCS_PIDS,
sizeof(Eterm)*pbdp->pid_sz);
- for (tix = 0, cpix = 0; tix < erts_max_processes; tix++) {
- Process *rp = process_tab[tix];
+ for (tix = 0, cpix = 0; tix < erts_proc.max; tix++) {
+ Process *rp = erts_pix2proc(tix);
if (rp) {
ERTS_PROCS_ASSERT(is_internal_pid(rp->id));
pbdp->debug.correct_pids[cpix++] = rp->id;
@@ -10113,14 +10464,13 @@ debug_processes_verify_all_pids(ErtsProcessesBifData *pbdp)
static void
debug_processes_check_term_proc_list(void)
{
- ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&proc_tab_mtx));
+ ERTS_SMP_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&erts_proc_tab_rwmtx));
if (!saved_term_procs.start)
ERTS_PROCS_ASSERT(!saved_term_procs.end);
else {
- SysTimeval tv_now;
- SysTimeval *prev_xtvp = NULL;
+ Uint64 curr_interval = get_proc_interval();
+ Uint64 *prev_x_interval_p = NULL;
ErtsTermProcElement *tpep;
- erts_get_emu_time(&tv_now);
for (tpep = saved_term_procs.start; tpep; tpep = tpep->next) {
if (!tpep->prev)
@@ -10132,20 +10482,17 @@ debug_processes_check_term_proc_list(void)
else
ERTS_PROCS_ASSERT(tpep->next->prev == tpep);
if (tpep->ix < 0) {
- SysTimeval *tvp = &tpep->u.bif_invocation.time;
- ERTS_PROCS_ASSERT(erts_cmp_timeval(&debug_tv_start, tvp) < 0
- && erts_cmp_timeval(tvp, &tv_now) < 0);
+ Uint64 interval = tpep->u.bif_invocation.interval;
+ ERTS_PROCS_ASSERT(interval <= curr_interval);
}
else {
- SysTimeval *stvp = &tpep->u.process.spawned;
- SysTimeval *xtvp = &tpep->u.process.exited;
+ Uint64 s_interval = tpep->u.process.spawned;
+ Uint64 x_interval = tpep->u.process.exited;
- ERTS_PROCS_ASSERT(erts_cmp_timeval(&debug_tv_start,
- stvp) < 0);
- ERTS_PROCS_ASSERT(erts_cmp_timeval(stvp, xtvp) < 0);
- if (prev_xtvp)
- ERTS_PROCS_ASSERT(erts_cmp_timeval(prev_xtvp, xtvp) < 0);
- prev_xtvp = xtvp;
+ ERTS_PROCS_ASSERT(s_interval <= x_interval);
+ if (prev_x_interval_p)
+ ERTS_PROCS_ASSERT(*prev_x_interval_p <= x_interval);
+ prev_x_interval_p = &tpep->u.process.exited;
ERTS_PROCS_ASSERT(is_internal_pid(tpep->u.process.pid));
ERTS_PROCS_ASSERT(tpep->ix
== internal_pid_index(tpep->u.process.pid));
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 5b79c40d93..1436e246d6 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -112,28 +112,29 @@ extern int erts_sched_thread_suggested_stack_size;
#define PRIORITY_NORMAL 2
#define PRIORITY_LOW 3
#define ERTS_NO_PROC_PRIO_LEVELS 4
+#define ERTS_NO_PROC_PRIO_QUEUES 3
#define ERTS_PORT_PRIO_LEVEL ERTS_NO_PROC_PRIO_LEVELS
+#define ERTS_NO_PRIO_LEVELS (ERTS_NO_PROC_PRIO_LEVELS + 1)
#define ERTS_RUNQ_FLGS_PROCS_QMASK \
((((Uint32) 1) << ERTS_NO_PROC_PRIO_LEVELS) - 1)
-#define ERTS_NO_PRIO_LEVELS (ERTS_NO_PROC_PRIO_LEVELS + 1)
-#define ERTS_RUNQ_FLGS_MIGRATE_QMASK \
+#define ERTS_RUNQ_FLGS_QMASK \
((((Uint32) 1) << ERTS_NO_PRIO_LEVELS) - 1)
#define ERTS_RUNQ_FLGS_EMIGRATE_SHFT \
- ERTS_NO_PROC_PRIO_LEVELS
+ ERTS_NO_PRIO_LEVELS
#define ERTS_RUNQ_FLGS_IMMIGRATE_SHFT \
(ERTS_RUNQ_FLGS_EMIGRATE_SHFT + ERTS_NO_PRIO_LEVELS)
#define ERTS_RUNQ_FLGS_EVACUATE_SHFT \
(ERTS_RUNQ_FLGS_IMMIGRATE_SHFT + ERTS_NO_PRIO_LEVELS)
#define ERTS_RUNQ_FLGS_EMIGRATE_QMASK \
- (ERTS_RUNQ_FLGS_MIGRATE_QMASK << ERTS_RUNQ_FLGS_EMIGRATE_SHFT)
+ (ERTS_RUNQ_FLGS_QMASK << ERTS_RUNQ_FLGS_EMIGRATE_SHFT)
#define ERTS_RUNQ_FLGS_IMMIGRATE_QMASK \
- (ERTS_RUNQ_FLGS_MIGRATE_QMASK << ERTS_RUNQ_FLGS_IMMIGRATE_SHFT)
+ (ERTS_RUNQ_FLGS_QMASK << ERTS_RUNQ_FLGS_IMMIGRATE_SHFT)
#define ERTS_RUNQ_FLGS_EVACUATE_QMASK \
- (ERTS_RUNQ_FLGS_MIGRATE_QMASK << ERTS_RUNQ_FLGS_EVACUATE_SHFT)
+ (ERTS_RUNQ_FLGS_QMASK << ERTS_RUNQ_FLGS_EVACUATE_SHFT)
#define ERTS_RUNQ_FLG_BASE2 \
(ERTS_RUNQ_FLGS_EVACUATE_SHFT + ERTS_NO_PRIO_LEVELS)
@@ -148,14 +149,18 @@ extern int erts_sched_thread_suggested_stack_size;
(((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 3))
#define ERTS_RUNQ_FLG_INACTIVE \
(((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 4))
+#define ERTS_RUNQ_FLG_NONEMPTY \
+ (((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 5))
+#define ERTS_RUNQ_FLG_PROTECTED \
+ (((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 6))
#define ERTS_RUNQ_FLGS_MIGRATION_QMASKS \
(ERTS_RUNQ_FLGS_EMIGRATE_QMASK \
| ERTS_RUNQ_FLGS_IMMIGRATE_QMASK \
| ERTS_RUNQ_FLGS_EVACUATE_QMASK)
+
#define ERTS_RUNQ_FLGS_MIGRATION_INFO \
- (ERTS_RUNQ_FLGS_MIGRATION_QMASKS \
- | ERTS_RUNQ_FLG_INACTIVE \
+ (ERTS_RUNQ_FLG_INACTIVE \
| ERTS_RUNQ_FLG_OUT_OF_WORK \
| ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK)
@@ -186,33 +191,45 @@ extern int erts_sched_thread_suggested_stack_size;
#define ERTS_UNSET_RUNQ_FLG_EVACUATE(FLGS, PRIO) \
((FLGS) &= ~ERTS_RUNQ_FLG_EVACUATE((PRIO)))
-#define ERTS_RUNQ_IFLG_SUSPENDED (((erts_aint32_t) 1) << 0)
-#define ERTS_RUNQ_IFLG_NONEMPTY (((erts_aint32_t) 1) << 1)
-
-
-#ifdef DEBUG
-# if defined(ARCH_64) && !HALFWORD_HEAP
-# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N) \
- (*((char **) &(RQP)) = (char *) (0xdeadbeefdead0003 | ((N) << 4)))
-# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP) \
-do { \
- ASSERT((RQP) != NULL); \
- ASSERT(((((Uint) (RQP)) & ((Uint) 0x3))) == ((Uint) 0)); \
- ASSERT((((Uint) (RQP)) & ~((Uint) 0xffff)) != ((Uint) 0xdeadbeefdead0000));\
-} while (0)
-# else
-# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N) \
- (*((char **) &(RQP)) = (char *) (0xdead0003 | ((N) << 4)))
-# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP) \
-do { \
- ASSERT((RQP) != NULL); \
- ASSERT(((((UWord) (RQP)) & ((UWord) 1))) == ((UWord) 0)); \
- ASSERT((((UWord) (RQP)) & ~((UWord) 0xffff)) != ((UWord) 0xdead0000)); \
-} while (0)
-# endif
-#else
-# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N)
-# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP)
+#define ERTS_RUNQ_FLGS_INIT(RQ, INIT) \
+ erts_smp_atomic32_init_nob(&(RQ)->flags, (erts_aint32_t) (INIT))
+#define ERTS_RUNQ_FLGS_SET(RQ, FLGS) \
+ ((Uint32) erts_smp_atomic32_read_bor_relb(&(RQ)->flags, \
+ (erts_aint32_t) (FLGS)))
+#define ERTS_RUNQ_FLGS_UNSET(RQ, FLGS) \
+ ((Uint32) erts_smp_atomic32_read_band_relb(&(RQ)->flags, \
+ (erts_aint32_t) ~(FLGS)))
+#define ERTS_RUNQ_FLGS_GET(RQ) \
+ ((Uint32) erts_smp_atomic32_read_acqb(&(RQ)->flags))
+#define ERTS_RUNQ_FLGS_GET_NOB(RQ) \
+ ((Uint32) erts_smp_atomic32_read_nob(&(RQ)->flags))
+#define ERTS_RUNQ_FLGS_GET_MB(RQ) \
+ ((Uint32) erts_smp_atomic32_read_mb(&(RQ)->flags))
+#define ERTS_RUNQ_FLGS_MASK_SET(RQ, MSK, FLGS) \
+ ((Uint32) erts_smp_atomic32_mask_set_relb(&(RQ)->flags, \
+ (erts_aint32_t) (MSK), \
+ (erts_aint32_t) (FLGS)))
+
+ERTS_GLB_INLINE erts_aint32_t
+erts_smp_atomic32_mask_set_relb(erts_smp_atomic32_t *a32p,
+ erts_aint32_t mask,
+ erts_aint32_t set);
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE erts_aint32_t
+erts_smp_atomic32_mask_set_relb(erts_smp_atomic32_t *a32p,
+ erts_aint32_t mask,
+ erts_aint32_t set)
+{
+ erts_aint32_t act = erts_smp_atomic32_read_nob(a32p);
+ while (1) {
+ erts_aint32_t exp = act;
+ erts_aint32_t new = exp & ~mask;
+ new |= (mask & set);
+ act = erts_smp_atomic32_cmpxchg_relb(a32p, new, exp);
+ if (act == exp)
+ return act;
+ }
+}
#endif
typedef enum {
@@ -253,18 +270,22 @@ typedef enum {
* eachother. Most frequent - lowest bit number.
*/
-#define ERTS_SSI_AUX_WORK_DD (((erts_aint32_t) 1) << 0)
-#define ERTS_SSI_AUX_WORK_DD_THR_PRGR (((erts_aint32_t) 1) << 1)
-#define ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC (((erts_aint32_t) 1) << 2)
-#define ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM (((erts_aint32_t) 1) << 3)
-#define ERTS_SSI_AUX_WORK_ASYNC_READY (((erts_aint32_t) 1) << 4)
-#define ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN (((erts_aint32_t) 1) << 5)
-#define ERTS_SSI_AUX_WORK_MISC_THR_PRGR (((erts_aint32_t) 1) << 6)
-#define ERTS_SSI_AUX_WORK_MISC (((erts_aint32_t) 1) << 7)
-#define ERTS_SSI_AUX_WORK_CHECK_CHILDREN (((erts_aint32_t) 1) << 8)
-#define ERTS_SSI_AUX_WORK_SET_TMO (((erts_aint32_t) 1) << 9)
-#define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK (((erts_aint32_t) 1) << 10)
-#define ERTS_SSI_AUX_WORK_REAP_PORTS (((erts_aint32_t) 1) << 11)
+#define ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP (((erts_aint32_t) 1) << 0)
+#define ERTS_SSI_AUX_WORK_DD (((erts_aint32_t) 1) << 1)
+#define ERTS_SSI_AUX_WORK_DD_THR_PRGR (((erts_aint32_t) 1) << 2)
+#define ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC (((erts_aint32_t) 1) << 3)
+#define ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM (((erts_aint32_t) 1) << 4)
+#define ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP (((erts_aint32_t) 1) << 5)
+#define ERTS_SSI_AUX_WORK_ASYNC_READY (((erts_aint32_t) 1) << 6)
+#define ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN (((erts_aint32_t) 1) << 7)
+#define ERTS_SSI_AUX_WORK_MISC_THR_PRGR (((erts_aint32_t) 1) << 8)
+#define ERTS_SSI_AUX_WORK_MISC (((erts_aint32_t) 1) << 9)
+#define ERTS_SSI_AUX_WORK_CHECK_CHILDREN (((erts_aint32_t) 1) << 10)
+#define ERTS_SSI_AUX_WORK_SET_TMO (((erts_aint32_t) 1) << 11)
+#define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK (((erts_aint32_t) 1) << 12)
+#define ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION (((erts_aint32_t) 1) << 13)
+#define ERTS_SSI_AUX_WORK_REAP_PORTS (((erts_aint32_t) 1) << 14)
+#define ERTS_SSI_AUX_WORK_FINISH_BP (((erts_aint32_t) 1) << 15)
typedef struct ErtsSchedulerSleepInfo_ ErtsSchedulerSleepInfo;
@@ -290,7 +311,7 @@ struct ErtsSchedulerSleepInfo_ {
typedef struct ErtsProcList_ ErtsProcList;
struct ErtsProcList_ {
Eterm pid;
- SysTimeval started;
+ Uint64 started_interval;
ErtsProcList* next;
};
@@ -311,21 +332,39 @@ typedef struct ErtsSchedulerData_ ErtsSchedulerData;
typedef struct ErtsRunQueue_ ErtsRunQueue;
typedef struct {
- int len;
- int max_len;
+ erts_smp_atomic32_t len;
+ erts_aint32_t max_len;
int reds;
+} ErtsRunQueueInfo;
+
+#ifdef ERTS_SMP
+
+typedef struct {
+ Uint32 flags;
+ ErtsRunQueue *misc_evac_runq;
struct {
struct {
int this;
int other;
} limit;
ErtsRunQueue *runq;
- } migrate;
-} ErtsRunQueueInfo;
+ Uint32 flags;
+ } prio[ERTS_NO_PRIO_LEVELS];
+} ErtsMigrationPath;
+
+typedef struct ErtsMigrationPaths_ ErtsMigrationPaths;
+
+struct ErtsMigrationPaths_ {
+ void *block;
+ ErtsMigrationPaths *next;
+ ErtsThrPrgrVal thr_prgr;
+ ErtsMigrationPath mpath[1];
+};
+
+#endif /* ERTS_SMP */
struct ErtsRunQueue_ {
int ix;
- erts_smp_atomic32_t info_flags;
erts_smp_mtx_t mtx;
erts_smp_cnd_t cnd;
@@ -333,19 +372,18 @@ struct ErtsRunQueue_ {
ErtsSchedulerData *scheduler;
int waiting; /* < 0 in sys schedule; > 0 on cnd variable */
int woken;
- Uint32 flags;
+ erts_smp_atomic32_t flags;
int check_balance_reds;
int full_reds_history_sum;
int full_reds_history[ERTS_FULL_REDS_HISTORY_SIZE];
int out_of_work_count;
- int max_len;
- int len;
+ erts_aint32_t max_len;
+ erts_aint32_t len;
int wakeup_other;
int wakeup_other_reds;
int halt_in_progress;
struct {
- int len;
ErtsProcList *pending_exiters;
Uint context_switches;
Uint reductions;
@@ -360,7 +398,7 @@ struct ErtsRunQueue_ {
struct {
ErtsMiscOpList *start;
ErtsMiscOpList *end;
- ErtsRunQueue *evac_runq;
+ erts_smp_atomic_t evac_runq;
} misc;
struct {
@@ -380,7 +418,7 @@ extern ErtsAlignedRunQueue *erts_aligned_run_queues;
#define ERTS_PROC_REDUCTIONS_EXECUTED(RQ, PRIO, REDS, AREDS) \
do { \
(RQ)->procs.reductions += (AREDS); \
- (RQ)->procs.prio_info[p->prio].reds += (REDS); \
+ (RQ)->procs.prio_info[(PRIO)].reds += (REDS); \
(RQ)->check_balance_reds -= (REDS); \
(RQ)->wakeup_other_reds += (AREDS); \
} while (0)
@@ -403,6 +441,11 @@ typedef struct {
} ErtsSchedWallTime;
typedef struct {
+ int sched;
+ erts_aint32_t aux_work;
+} ErtsDelayedAuxWorkWakeupJob;
+
+typedef struct {
int sched_id;
ErtsSchedulerData *esdp;
ErtsSchedulerSleepInfo *ssi;
@@ -421,6 +464,10 @@ typedef struct {
void (*completed_callback)(void *);
void (*completed_arg)(void *);
} dd;
+ struct {
+ ErtsThrPrgrLaterOp *first;
+ ErtsThrPrgrLaterOp *last;
+ } later_op;
#endif
#ifdef ERTS_USE_ASYNC_READY_Q
struct {
@@ -431,6 +478,21 @@ typedef struct {
void *queue;
} async_ready;
#endif
+#ifdef ERTS_SMP
+ struct {
+ Process* code_stager;
+ ErtsThrPrgrVal thr_prgr;
+ } code_ix_activation;
+ struct {
+ Process* stager;
+ ErtsThrPrgrVal thr_prgr;
+ } bp_ix_activation;
+ struct {
+ int *sched2jix;
+ int jix;
+ ErtsDelayedAuxWorkWakeupJob *job;
+ } delayed_wakeup;
+#endif
} ErtsAuxWorkData;
struct ErtsSchedulerData_ {
@@ -464,7 +526,6 @@ struct ErtsSchedulerData_ {
int virtual_reds;
int cpu_id; /* >= 0 when bound */
ErtsAuxWorkData aux_work_data;
-
ErtsAtomCacheMap atom_cache_map;
ErtsSchedAllocData alloc_data;
@@ -488,6 +549,90 @@ extern ErtsAlignedSchedulerData *erts_aligned_scheduler_data;
extern ErtsSchedulerData *erts_scheduler_data;
#endif
+#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
+int erts_smp_lc_runq_is_locked(ErtsRunQueue *);
+#endif
+
+#ifdef ERTS_INCLUDE_SCHEDULER_INTERNALS
+
+/*
+ * Run queue locked during modifications. We use atomic ops since
+ * other threads peek at values without run queue lock.
+ */
+
+ERTS_GLB_INLINE void erts_smp_inc_runq_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi, int prio);
+ERTS_GLB_INLINE void erts_smp_dec_runq_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi, int prio);
+ERTS_GLB_INLINE void erts_smp_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE void
+erts_smp_inc_runq_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi, int prio)
+{
+ erts_aint32_t len;
+
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
+
+ len = erts_smp_atomic32_read_nob(&rqi->len);
+ ASSERT(len >= 0);
+ if (len == 0) {
+ ASSERT((erts_smp_atomic32_read_nob(&rq->flags)
+ & ((erts_aint32_t) (1 << prio))) == 0);
+ erts_smp_atomic32_read_bor_nob(&rq->flags,
+ (erts_aint32_t) (1 << prio));
+ }
+ len++;
+ if (rqi->max_len < len)
+ rqi->max_len = len;
+
+ erts_smp_atomic32_set_relb(&rqi->len, len);
+
+ rq->len++;
+ if (rq->max_len < rq->len)
+ rq->max_len = len;
+ ASSERT(rq->len > 0);
+}
+
+ERTS_GLB_INLINE void
+erts_smp_dec_runq_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi, int prio)
+{
+ erts_aint32_t len;
+
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
+
+ len = erts_smp_atomic32_read_nob(&rqi->len);
+ len--;
+ ASSERT(len >= 0);
+ if (len == 0) {
+ ASSERT((erts_smp_atomic32_read_nob(&rq->flags)
+ & ((erts_aint32_t) (1 << prio))));
+ erts_smp_atomic32_read_band_nob(&rq->flags,
+ ~((erts_aint32_t) (1 << prio)));
+ }
+ erts_smp_atomic32_set_relb(&rqi->len, len);
+
+ rq->len--;
+ ASSERT(rq->len >= 0);
+}
+
+ERTS_GLB_INLINE void
+erts_smp_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi)
+{
+ erts_aint32_t len;
+
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
+
+ len = erts_smp_atomic32_read_nob(&rqi->len);
+ ASSERT(rqi->max_len >= len);
+ rqi->max_len = len;
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+#define RUNQ_READ_LEN(X) erts_smp_atomic32_read_nob((X))
+
+#endif /* ERTS_INCLUDE_SCHEDULER_INTERNALS */
+
/*
* Process Specific Data.
*
@@ -600,6 +745,8 @@ struct ErtsPendingSuspend_ {
# define BIN_OLD_VHEAP(p) (p)->bin_old_vheap
struct process {
+ Eterm id; /* The pid of this process
+ (need to be first in struct) */
/* All fields in the PCB that differs between different heap
* architectures, have been moved to the end of this struct to
* make sure that as few offsets as possible differ. Different
@@ -642,13 +789,9 @@ struct process {
* Number of reductions left to execute.
* Only valid for the current process.
*/
- Uint32 status; /* process STATE */
- Uint32 gcstatus; /* process gc STATE */
- Uint32 rstatus; /* process resume STATE */
Uint32 rcount; /* suspend count */
- Eterm id; /* The pid of this process */
int prio; /* Priority of process */
- int skipped; /* Times a low prio process has been rescheduled */
+ int schedule_count; /* Times left to reschedule a low prio process */
Uint reds; /* No of reductions for this process */
Eterm tracer_proc; /* If proc is traced, this is the tracer
(can NOT be boxed) */
@@ -661,7 +804,6 @@ struct process {
Eterm ftrace; /* Latest exception stack trace dump */
Process *next; /* Pointer to next process in run queue */
- Process *prev; /* Pointer to prev process in run queue */
struct reg_proc *reg; /* NULL iff not registered */
ErtsLink *nlinks;
@@ -699,8 +841,8 @@ struct process {
* Information mainly for post-mortem use (erl crash dump).
*/
Eterm parent; /* Pid of process that created this process. */
- SysTimeval started; /* Time when started. */
-
+ erts_approx_time_t approx_started; /* Time when started. */
+ Uint64 started_interval;
/* This is the place, where all fields that differs between memory
* architectures, have gone to.
@@ -723,27 +865,29 @@ struct process {
Uint64 bin_old_vheap; /* Virtual old heap size for binaries */
union {
+ struct {
#ifdef ERTS_SMP
- ErtsSmpPTimer *ptimer;
+ ErtsSmpPTimer *ptimer;
+ ErlMessageInQueue msg_inq;
+ ErtsPendExit pending_exit;
#else
- ErlTimer tm; /* Timer entry */
+ ErlTimer tm; /* Timer entry */
+#endif
+ } alive; /* when process is alive */
+#ifdef ERTS_SMP
+ ErtsThrPrgrLaterOp release_data; /* when releasing process struct */
#endif
void *exit_data; /* Misc data referred during termination */
} u;
- ErtsRunQueue *bound_runq;
+ erts_smp_atomic32_t state; /* Process state flags (see ERTS_PSFLG_*) */
#ifdef ERTS_SMP
erts_proc_lock_t lock;
ErtsSchedulerData *scheduler_data;
- int is_exiting;
- Uint32 runq_flags;
- Uint32 status_flags;
- ErlMessageInQueue msg_inq;
Eterm suspendee;
ErtsPendingSuspend *pending_suspenders;
- ErtsPendExit pending_exit;
- ErtsRunQueue *run_queue;
+ erts_smp_atomic_t run_queue;
#ifdef HIPE
struct hipe_process_state_smp hipe_smp;
#endif
@@ -797,6 +941,29 @@ void erts_check_for_holes(Process* p);
#define SEQ_TRACE_TOKEN(p) ((p)->seq_trace_token)
+#if ERTS_NO_PROC_PRIO_LEVELS > 4
+# error "Need to increase ERTS_PSFLG_PRIO_SHIFT"
+#endif
+
+#define ERTS_PSFLG_PRIO_SHIFT 2
+
+#define ERTS_PSFLG_BIT(N) \
+ (((erts_aint32_t) 1) << (ERTS_PSFLG_PRIO_SHIFT + (N)))
+
+#define ERTS_PSFLG_PRIO_MASK (ERTS_PSFLG_BIT(0) - 1)
+
+#define ERTS_PSFLG_FREE ERTS_PSFLG_BIT(0)
+#define ERTS_PSFLG_EXITING ERTS_PSFLG_BIT(1)
+#define ERTS_PSFLG_PENDING_EXIT ERTS_PSFLG_BIT(2)
+#define ERTS_PSFLG_ACTIVE ERTS_PSFLG_BIT(3)
+#define ERTS_PSFLG_IN_RUNQ ERTS_PSFLG_BIT(4)
+#define ERTS_PSFLG_RUNNING ERTS_PSFLG_BIT(5)
+#define ERTS_PSFLG_SUSPENDED ERTS_PSFLG_BIT(6)
+#define ERTS_PSFLG_GC ERTS_PSFLG_BIT(7)
+#define ERTS_PSFLG_BOUND ERTS_PSFLG_BIT(8)
+#define ERTS_PSFLG_TRAP_EXIT ERTS_PSFLG_BIT(9)
+
+
/* The sequential tracing token is a tuple of size 5:
*
* {Flags, Label, Serial, Sender}
@@ -869,9 +1036,8 @@ Eterm* erts_heap_alloc(Process* p, Uint need, Uint xtra);
Eterm* erts_set_hole_marker(Eterm* ptr, Uint sz);
#endif
-extern Process** process_tab;
-extern Uint erts_max_processes;
-extern Uint erts_process_tab_index_mask;
+extern erts_smp_rwmtx_t erts_proc_tab_rwmtx;
+extern erts_smp_atomic_t *erts_proc_tab;
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),
@@ -899,17 +1065,13 @@ struct erts_system_profile_flags_t {
unsigned int exclusive : 1;
};
extern struct erts_system_profile_flags_t erts_system_profile_flags;
-
-#define INVALID_PID(p, pid) ((p) == NULL \
- || (p)->id != (pid) \
- || (p)->status == P_EXITING)
#define IS_TRACED(p) ( (p)->tracer_proc != NIL )
#define ARE_TRACE_FLAGS_ON(p,tf) ( ((p)->trace_flags & (tf|F_SENSITIVE)) == (tf) )
#define IS_TRACED_FL(p,tf) ( IS_TRACED(p) && ARE_TRACE_FLAGS_ON(p,tf) )
/* process flags */
-#define F_TRAPEXIT (1 << 0)
+#define F_HIBERNATE_SCHED (1 << 0) /* Schedule out after hibernate op */
#define F_INSLPQUEUE (1 << 1) /* Set if in timer queue */
#define F_TIMO (1 << 2) /* Set if timeout */
#define F_HEAP_GROW (1 << 3)
@@ -920,7 +1082,6 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags;
#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_HIBERNATE_SCHED (1 << 11) /* Schedule out after hibernate op */
/* process trace_flags */
#define F_SENSITIVE (1 << 0)
@@ -987,67 +1148,10 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags;
#define DT_UTAG_FLAGS(P) ((P)->dt_utag_flags)
#endif
-
-#ifdef ERTS_SMP
-/* Status flags ... */
-#define ERTS_PROC_SFLG_PENDADD2SCHEDQ (((Uint32) 1) << 0) /* Pending
- add to
- schedule q */
-#define ERTS_PROC_SFLG_INRUNQ (((Uint32) 1) << 1) /* Process is
- in run q */
-#define ERTS_PROC_SFLG_TRAPEXIT (((Uint32) 1) << 2) /* Process is
- trapping
- exit */
-#define ERTS_PROC_SFLG_RUNNING (((Uint32) 1) << 3) /* Process is
- running */
-/* Scheduler flags in process struct... */
-#define ERTS_PROC_RUNQ_FLG_RUNNING (((Uint32) 1) << 0) /* Process is
- running */
-
-#endif
-
-
-#ifdef ERTS_SMP
-#define ERTS_PROC_IS_TRAPPING_EXITS(P) \
- (ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks((P)) \
- & ERTS_PROC_LOCK_STATUS), \
- (P)->status_flags & ERTS_PROC_SFLG_TRAPEXIT)
-
-#define ERTS_PROC_SET_TRAP_EXIT(P) \
- (ERTS_SMP_LC_ASSERT(((ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS) \
- & erts_proc_lc_my_proc_locks((P))) \
- == (ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS)), \
- (P)->status_flags |= ERTS_PROC_SFLG_TRAPEXIT, \
- (P)->flags |= F_TRAPEXIT, \
- 1)
-
-#define ERTS_PROC_UNSET_TRAP_EXIT(P) \
- (ERTS_SMP_LC_ASSERT(((ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS) \
- & erts_proc_lc_my_proc_locks((P))) \
- == (ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS)), \
- (P)->status_flags &= ~ERTS_PROC_SFLG_TRAPEXIT, \
- (P)->flags &= ~F_TRAPEXIT, \
- 0)
-#else
-#define ERTS_PROC_IS_TRAPPING_EXITS(P) ((P)->flags & F_TRAPEXIT)
-#define ERTS_PROC_SET_TRAP_EXIT(P) ((P)->flags |= F_TRAPEXIT, 1)
-#define ERTS_PROC_UNSET_TRAP_EXIT(P) ((P)->flags &= ~F_TRAPEXIT, 0)
-#endif
-
/* Option flags to erts_send_exit_signal() */
#define ERTS_XSIG_FLG_IGN_KILL (((Uint32) 1) << 0)
#define ERTS_XSIG_FLG_NO_IGN_NORMAL (((Uint32) 1) << 1)
-
-/* Process status values */
-#define P_FREE 0
-#define P_RUNABLE 1
-#define P_WAITING 2
-#define P_RUNNING 3
-#define P_EXITING 4
-#define P_GARBING 5
-#define P_SUSPENDED 6
-
#define CANCEL_TIMER(p) \
do { \
if ((p)->flags & (F_INSLPQUEUE)) \
@@ -1069,6 +1173,9 @@ void erts_early_init_scheduling(int);
void erts_init_scheduling(int, int);
Eterm erts_sched_wall_time_request(Process *c_p, int set, int enable);
+Uint64 erts_get_proc_interval(void);
+Uint64 erts_ensure_later_proc_interval(Uint64);
+Uint64 erts_step_proc_interval(void);
ErtsProcList *erts_proclist_create(Process *);
void erts_proclist_destroy(ErtsProcList *);
@@ -1078,6 +1185,10 @@ int erts_sched_set_wakeup_other_thresold(char *str);
int erts_sched_set_wakeup_other_type(char *str);
int erts_sched_set_busy_wait_threshold(char *str);
+void erts_schedule_thr_prgr_later_op(void (*)(void *),
+ void *,
+ ErtsThrPrgrLaterOp *);
+
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
int erts_dbg_check_halloc_lock(Process *p);
#endif
@@ -1104,6 +1215,10 @@ void erts_smp_notify_check_children_needed(void);
#if ERTS_USE_ASYNC_READY_Q
void erts_notify_check_async_ready_queue(void *);
#endif
+#ifdef ERTS_SMP
+void erts_notify_code_ix_activation(Process* p, ErtsThrPrgrVal later);
+void erts_notify_finish_breakpointing(Process* p);
+#endif
void erts_schedule_misc_aux_work(int sched_id,
void (*func)(void *),
void *arg);
@@ -1124,14 +1239,6 @@ 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);
-#ifdef ERTS_SMP
-ErtsMigrateResult erts_proc_migrate(Process *,
- ErtsProcLocks *,
- ErtsRunQueue *,
- int *,
- ErtsRunQueue *,
- int *);
-#endif
Process *schedule(Process*, int);
void erts_schedule_misc_op(void (*)(void *), void *);
Eterm erl_create_process(Process*, Eterm, Eterm, Eterm, ErlSpawnOpts*);
@@ -1180,8 +1287,7 @@ int erts_send_exit_signal(Process *,
#ifdef ERTS_SMP
void erts_handle_pending_exit(Process *, ErtsProcLocks);
#define ERTS_PROC_PENDING_EXIT(P) \
- (ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks((P)) & ERTS_PROC_LOCK_STATUS),\
- (P)->pending_exit.reason != THE_NON_VALUE)
+ (ERTS_PSFLG_PENDING_EXIT & erts_smp_atomic32_read_acqb(&(P)->state))
#else
#define ERTS_PROC_PENDING_EXIT(P) 0
#endif
@@ -1233,13 +1339,26 @@ ErtsSchedulerData *erts_get_scheduler_data(void)
#endif
#endif
+void erts_schedule_process(Process *, erts_aint32_t);
+
+ERTS_GLB_INLINE void erts_proc_notify_new_message(Process *p);
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE void
+erts_proc_notify_new_message(Process *p)
+{
+ /* No barrier needed, due to msg lock */
+ erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state);
+ if (!(state & ERTS_PSFLG_ACTIVE))
+ erts_schedule_process(p, state);
+}
+#endif
+
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
#define ERTS_PROCESS_LOCK_ONLY_LOCK_CHECK_PROTO__
#include "erl_process_lock.h"
#undef ERTS_PROCESS_LOCK_ONLY_LOCK_CHECK_PROTO__
-int erts_smp_lc_runq_is_locked(ErtsRunQueue *);
#define ERTS_SMP_LC_CHK_RUNQ_LOCK(RQ, L) \
do { \
if ((L)) \
@@ -1365,13 +1484,91 @@ erts_proc_set_error_handler(Process *p, ErtsProcLocks plocks, Eterm handler)
#endif
+#ifdef ERTS_INCLUDE_SCHEDULER_INTERNALS
+
#ifdef ERTS_SMP
-ErtsRunQueue *erts_prepare_emigrate(ErtsRunQueue *c_rq,
- ErtsRunQueueInfo *c_rqi,
- int prio);
+#include "erl_thr_progress.h"
+
+extern erts_atomic_t erts_migration_paths;
+
+ERTS_GLB_INLINE ErtsMigrationPaths *erts_get_migration_paths_managed(void);
+ERTS_GLB_INLINE ErtsMigrationPaths *erts_get_migration_paths(void);
ERTS_GLB_INLINE ErtsRunQueue *erts_check_emigration_need(ErtsRunQueue *c_rq,
int prio);
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE ErtsMigrationPaths *
+erts_get_migration_paths_managed(void)
+{
+ return (ErtsMigrationPaths *) erts_atomic_read_ddrb(&erts_migration_paths);
+}
+
+ERTS_GLB_INLINE ErtsMigrationPaths *
+erts_get_migration_paths(void)
+{
+ if (erts_thr_progress_is_managed_thread())
+ return erts_get_migration_paths_managed();
+ else
+ return NULL;
+}
+
+ERTS_GLB_INLINE ErtsRunQueue *
+erts_check_emigration_need(ErtsRunQueue *c_rq, int prio)
+{
+ ErtsMigrationPaths *mps = erts_get_migration_paths();
+ ErtsMigrationPath *mp;
+ Uint32 flags;
+
+ if (!mps)
+ return NULL;
+
+ mp = &mps->mpath[c_rq->ix];
+ flags = mp->flags;
+
+ if (ERTS_CHK_RUNQ_FLG_EMIGRATE(flags, prio)) {
+ int len;
+
+ if (ERTS_CHK_RUNQ_FLG_EVACUATE(flags, prio)) {
+ /* force emigration */
+ return mp->prio[prio].runq;
+ }
+
+ if (flags & ERTS_RUNQ_FLG_INACTIVE) {
+ /*
+ * Run queue was inactive at last balance. Verify that
+ * it still is before forcing emigration.
+ */
+ if (ERTS_RUNQ_FLGS_GET(c_rq) & ERTS_RUNQ_FLG_INACTIVE)
+ return mp->prio[prio].runq;
+ }
+
+
+ if (prio == ERTS_PORT_PRIO_LEVEL)
+ len = RUNQ_READ_LEN(&c_rq->ports.info.len);
+ else
+ len = RUNQ_READ_LEN(&c_rq->procs.prio_info[prio].len);
+
+ if (len > mp->prio[prio].limit.this) {
+ ErtsRunQueue *n_rq = mp->prio[prio].runq;
+ if (n_rq) {
+ if (prio == ERTS_PORT_PRIO_LEVEL)
+ len = RUNQ_READ_LEN(&n_rq->ports.info.len);
+ else
+ len = RUNQ_READ_LEN(&n_rq->procs.prio_info[prio].len);
+
+ if (len < mp->prio[prio].limit.other)
+ return n_rq;
+ }
+ }
+ }
+ return NULL;
+}
+
+#endif
+
+#endif
+
#endif
ERTS_GLB_INLINE int erts_is_scheduler_bound(ErtsSchedulerData *esdp);
@@ -1394,29 +1591,6 @@ ERTS_GLB_INLINE void erts_smp_runqs_unlock(ErtsRunQueue *rq1, ErtsRunQueue *rq2)
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-#ifdef ERTS_SMP
-ERTS_GLB_INLINE ErtsRunQueue *
-erts_check_emigration_need(ErtsRunQueue *c_rq, int prio)
-{
- ErtsRunQueueInfo *c_rqi;
-
- if (!ERTS_CHK_RUNQ_FLG_EMIGRATE(c_rq->flags, prio))
- return NULL;
-
- if (prio == ERTS_PORT_PRIO_LEVEL)
- c_rqi = &c_rq->ports.info;
- else
- c_rqi = &c_rq->procs.prio_info[prio];
-
- if (!ERTS_CHK_RUNQ_FLG_EVACUATE(c_rq->flags, prio)
- && !(c_rq->flags & ERTS_RUNQ_FLG_INACTIVE)
- && c_rqi->len <= c_rqi->migrate.limit.this)
- return NULL;
-
- return erts_prepare_emigrate(c_rq, c_rqi, prio);
-}
-#endif
-
ERTS_GLB_INLINE
int erts_is_scheduler_bound(ErtsSchedulerData *esdp)
{
@@ -1454,10 +1628,9 @@ Uint erts_get_scheduler_id(void)
ERTS_GLB_INLINE ErtsRunQueue *
erts_get_runq_proc(Process *p)
{
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
#ifdef ERTS_SMP
- ASSERT(p->run_queue);
- return p->run_queue;
+ ASSERT(ERTS_AINT_NULL != erts_atomic_read_nob(&p->run_queue));
+ return (ErtsRunQueue *) erts_atomic_read_nob(&p->run_queue);
#else
return ERTS_RUNQ_IX(0);
#endif
@@ -1628,25 +1801,13 @@ extern int erts_disable_proc_not_running_opt;
#ifdef DEBUG
#define ERTS_SMP_ASSERT_IS_NOT_EXITING(P) \
- do { ASSERT(!(P)->is_exiting); } while (0)
+ do { ASSERT(!ERTS_PROC_IS_EXITING((P))); } while (0)
#else
#define ERTS_SMP_ASSERT_IS_NOT_EXITING(P)
#endif
-/* NOTE: At least one process lock has to be held on P! */
-#ifdef ERTS_ENABLE_LOCK_CHECK
-#define ERTS_PROC_IS_EXITING(P) \
- (ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks((P)) != 0 \
- || erts_lc_pix_lock_is_locked(ERTS_PID2PIXLOCK((P)->id))),\
- (P)->is_exiting)
-#else
-#define ERTS_PROC_IS_EXITING(P) ((P)->is_exiting)
-#endif
-
#else /* !ERTS_SMP */
-#define ERTS_PROC_IS_EXITING(P) ((P)->status == P_EXITING)
-
#define ERTS_SMP_ASSERT_IS_NOT_EXITING(P)
#define erts_pid2proc_not_running erts_pid2proc
@@ -1654,6 +1815,10 @@ extern int erts_disable_proc_not_running_opt;
#endif
+#define ERTS_PROC_IS_EXITING(P) \
+ (ERTS_PSFLG_EXITING & erts_smp_atomic32_read_acqb(&(P)->state))
+
+
/* Minimum NUMBER of processes for a small system to start */
#ifdef ERTS_SMP
#define ERTS_MIN_PROCESSES ERTS_NO_OF_PIX_LOCKS
diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c
index 3550f1396c..542c5ed0d9 100644
--- a/erts/emulator/beam/erl_process_dump.c
+++ b/erts/emulator/beam/erl_process_dump.c
@@ -65,14 +65,11 @@ erts_deep_process_dump(int to, void *to_arg)
all_binaries = NULL;
for (i = 0; i < erts_max_processes; i++) {
- if ((process_tab[i] != NULL) && (process_tab[i]->i != ENULL)) {
- if (process_tab[i]->status != P_EXITING) {
- Process* p = process_tab[i];
-
- if (p->status != P_GARBING) {
- dump_process_info(to, to_arg, p);
- }
- }
+ Process *p = erts_pix2proc(i);
+ if (p && p->i != ENULL) {
+ erts_aint32_t state = erts_smp_atomic32_read_acqb(&p->state);
+ if (!(state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_GC)))
+ dump_process_info(to, to_arg, p);
}
}
@@ -326,7 +323,7 @@ heap_dump(int to, void *to_arg, Eterm x)
int i;
GET_DOUBLE_DATA((ptr+1), f);
- i = sys_double_to_chars(f.fd, (char*) sbuf);
+ i = sys_double_to_chars(f.fd, (char*) sbuf, sizeof(sbuf));
sys_memset(sbuf+i, 0, 31-i);
erts_print(to, to_arg, "F%X:%s\n", i, sbuf);
*ptr = OUR_NIL;
diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c
index b3b4601a31..7777ba1d3d 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-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2012. 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
@@ -67,10 +67,12 @@
#include "erl_process.h"
-const Process erts_proc_lock_busy;
+const Process erts_proc_lock_busy = {ERTS_INVALID_PID};
#ifdef ERTS_SMP
+#if ERTS_PROC_LOCK_OWN_IMPL
+
#define ERTS_PROC_LOCK_SPIN_COUNT_MAX 2000
#define ERTS_PROC_LOCK_SPIN_COUNT_SCHED_INC 32
#define ERTS_PROC_LOCK_SPIN_COUNT_BASE 1000
@@ -90,15 +92,12 @@ static void check_queue(erts_proc_lock_t *lck);
#error "The size of the 'uflgs' field of the erts_tse_t type is too small"
#endif
-struct erts_proc_lock_queues_t_ {
- erts_proc_lock_queues_t *next;
- erts_tse_t *queue[ERTS_PROC_LOCK_MAX_BIT+1];
-};
+static int proc_lock_spin_count;
+static int aux_thr_proc_lock_spin_count;
-static erts_proc_lock_queues_t zeroqs = {0};
+static void cleanup_tse(void);
-static erts_smp_spinlock_t qs_lock;
-static erts_proc_lock_queues_t *queue_free_list;
+#endif /* ERTS_PROC_LOCK_OWN_IMPL */
#ifdef ERTS_ENABLE_LOCK_CHECK
static struct {
@@ -111,16 +110,11 @@ static struct {
erts_pix_lock_t erts_pix_locks[ERTS_NO_OF_PIX_LOCKS];
-static int proc_lock_spin_count;
-static int aux_thr_proc_lock_spin_count;
-
-static void cleanup_tse(void);
void
erts_init_proc_lock(int cpus)
{
int i;
- erts_smp_spinlock_init(&qs_lock, "proc_lck_qs_alloc");
for (i = 0; i < ERTS_NO_OF_PIX_LOCKS; i++) {
#ifdef ERTS_ENABLE_LOCK_COUNT
erts_mtx_init_x(&erts_pix_locks[i].u.mtx,
@@ -129,14 +123,8 @@ erts_init_proc_lock(int cpus)
erts_mtx_init(&erts_pix_locks[i].u.mtx, "pix_lock");
#endif
}
- queue_free_list = NULL;
+#if ERTS_PROC_LOCK_OWN_IMPL
erts_thr_install_exit_handler(cleanup_tse);
-#ifdef ERTS_ENABLE_LOCK_CHECK
- lc_id.proc_lock_main = erts_lc_get_lock_order_id("proc_main");
- lc_id.proc_lock_link = erts_lc_get_lock_order_id("proc_link");
- lc_id.proc_lock_msgq = erts_lc_get_lock_order_id("proc_msgq");
- lc_id.proc_lock_status = erts_lc_get_lock_order_id("proc_status");
-#endif
if (cpus > 1) {
proc_lock_spin_count = ERTS_PROC_LOCK_SPIN_COUNT_BASE;
proc_lock_spin_count += (ERTS_PROC_LOCK_SPIN_COUNT_SCHED_INC
@@ -153,19 +141,19 @@ erts_init_proc_lock(int cpus)
}
if (proc_lock_spin_count > ERTS_PROC_LOCK_SPIN_COUNT_MAX)
proc_lock_spin_count = ERTS_PROC_LOCK_SPIN_COUNT_MAX;
+#endif
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ lc_id.proc_lock_main = erts_lc_get_lock_order_id("proc_main");
+ lc_id.proc_lock_link = erts_lc_get_lock_order_id("proc_link");
+ lc_id.proc_lock_msgq = erts_lc_get_lock_order_id("proc_msgq");
+ lc_id.proc_lock_status = erts_lc_get_lock_order_id("proc_status");
+#endif
}
+#if ERTS_PROC_LOCK_OWN_IMPL
+
#ifdef ERTS_ENABLE_LOCK_CHECK
-static void
-check_unused_tse(erts_tse_t *wtr)
-{
- int i;
- erts_proc_lock_queues_t *queues = wtr->udata;
- ERTS_LC_ASSERT(wtr->uflgs == 0);
- for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++)
- ERTS_LC_ASSERT(!queues->queue[i]);
-}
-#define CHECK_UNUSED_TSE(W) check_unused_tse((W))
+#define CHECK_UNUSED_TSE(W) ERTS_LC_ASSERT((W)->uflgs == 0)
#else
#define CHECK_UNUSED_TSE(W)
#endif
@@ -174,112 +162,64 @@ static ERTS_INLINE erts_tse_t *
tse_fetch(erts_pix_lock_t *pix_lock)
{
erts_tse_t *tse = erts_tse_fetch();
- if (!tse->udata) {
- erts_proc_lock_queues_t *qs;
-#if ERTS_PROC_LOCK_SPINLOCK_IMPL && !ERTS_PROC_LOCK_ATOMIC_IMPL
- if (pix_lock)
- erts_pix_unlock(pix_lock);
-#endif
- erts_smp_spin_lock(&qs_lock);
- qs = queue_free_list;
- if (qs) {
- queue_free_list = queue_free_list->next;
- erts_smp_spin_unlock(&qs_lock);
- }
- else {
- erts_smp_spin_unlock(&qs_lock);
- qs = erts_alloc(ERTS_ALC_T_PROC_LCK_QS,
- sizeof(erts_proc_lock_queues_t));
- sys_memcpy((void *) qs,
- (void *) &zeroqs,
- sizeof(erts_proc_lock_queues_t));
- }
- tse->udata = qs;
-#if ERTS_PROC_LOCK_SPINLOCK_IMPL && !ERTS_PROC_LOCK_ATOMIC_IMPL
- if (pix_lock)
- erts_pix_lock(pix_lock);
-#endif
- }
tse->uflgs = 0;
return tse;
}
static ERTS_INLINE void
-tse_return(erts_tse_t *tse, int force_free_q)
+tse_return(erts_tse_t *tse)
{
CHECK_UNUSED_TSE(tse);
- if (force_free_q || erts_tse_is_tmp(tse)) {
- erts_proc_lock_queues_t *qs = tse->udata;
- ASSERT(qs);
- erts_smp_spin_lock(&qs_lock);
- qs->next = queue_free_list;
- queue_free_list = qs;
- erts_smp_spin_unlock(&qs_lock);
- tse->udata = NULL;
- }
erts_tse_return(tse);
}
-void
-erts_proc_lock_prepare_proc_lock_waiter(void)
-{
- tse_return(tse_fetch(NULL), 0);
-}
-
-
static void
cleanup_tse(void)
{
erts_tse_t *tse = erts_tse_fetch();
- if (tse) {
- if (tse->udata)
- tse_return(tse, 1);
- else
- erts_tse_return(tse);
- }
+ if (tse)
+ erts_tse_return(tse);
}
/*
* Waiters are queued in a circular double linked list;
- * where qs->queue[lock_ix] is the first waiter in queue, and
- * qs->queue[lock_ix]->prev is the last waiter in queue.
+ * where lck->queue[lock_ix] is the first waiter in queue, and
+ * lck->queue[lock_ix]->prev is the last waiter in queue.
*/
static ERTS_INLINE void
-enqueue_waiter(erts_proc_lock_queues_t *qs,
- int ix,
- erts_tse_t *wtr)
+enqueue_waiter(erts_proc_lock_t *lck, int ix, erts_tse_t *wtr)
{
- if (!qs->queue[ix]) {
- qs->queue[ix] = wtr;
+ if (!lck->queue[ix]) {
+ lck->queue[ix] = wtr;
wtr->next = wtr;
wtr->prev = wtr;
}
else {
- ERTS_LC_ASSERT(qs->queue[ix]->next && qs->queue[ix]->prev);
- wtr->next = qs->queue[ix];
- wtr->prev = qs->queue[ix]->prev;
+ ERTS_LC_ASSERT(lck->queue[ix]->next && lck->queue[ix]->prev);
+ wtr->next = lck->queue[ix];
+ wtr->prev = lck->queue[ix]->prev;
wtr->prev->next = wtr;
- qs->queue[ix]->prev = wtr;
+ lck->queue[ix]->prev = wtr;
}
}
static erts_tse_t *
-dequeue_waiter(erts_proc_lock_queues_t *qs, int ix)
+dequeue_waiter(erts_proc_lock_t *lck, int ix)
{
- erts_tse_t *wtr = qs->queue[ix];
- ERTS_LC_ASSERT(qs->queue[ix]);
+ erts_tse_t *wtr = lck->queue[ix];
+ ERTS_LC_ASSERT(lck->queue[ix]);
if (wtr->next == wtr) {
- ERTS_LC_ASSERT(qs->queue[ix]->prev == wtr);
- qs->queue[ix] = NULL;
+ ERTS_LC_ASSERT(lck->queue[ix]->prev == wtr);
+ lck->queue[ix] = NULL;
}
else {
ERTS_LC_ASSERT(wtr->next != wtr);
ERTS_LC_ASSERT(wtr->prev != wtr);
wtr->next->prev = wtr->prev;
wtr->prev->next = wtr->next;
- qs->queue[ix] = wtr->next;
+ lck->queue[ix] = wtr->next;
}
return wtr;
}
@@ -300,19 +240,18 @@ try_aquire(erts_proc_lock_t *lck, erts_tse_t *wtr)
ErtsProcLocks locks = wtr->uflgs;
int lock_no;
- ERTS_LC_ASSERT(lck->queues);
ERTS_LC_ASSERT(got_locks != locks);
for (lock_no = 0; lock_no <= ERTS_PROC_LOCK_MAX_BIT; lock_no++) {
ErtsProcLocks lock = ((ErtsProcLocks) 1) << lock_no;
if (locks & lock) {
ErtsProcLocks wflg, old_lflgs;
- if (lck->queues->queue[lock_no]) {
+ if (lck->queue[lock_no]) {
/* Others already waiting */
enqueue:
ERTS_LC_ASSERT(ERTS_PROC_LOCK_FLGS_READ_(lck)
& (lock << ERTS_PROC_LOCK_WAITER_SHIFT));
- enqueue_waiter(lck->queues, lock_no, wtr);
+ enqueue_waiter(lck, lock_no, wtr);
break;
}
wflg = lock << ERTS_PROC_LOCK_WAITER_SHIFT;
@@ -364,7 +303,6 @@ transfer_locks(Process *p,
for (lock_no = 0; tlocks && lock_no <= ERTS_PROC_LOCK_MAX_BIT; lock_no++) {
ErtsProcLocks lock = ((ErtsProcLocks) 1) << lock_no;
if (tlocks & lock) {
- erts_proc_lock_queues_t *qs = p->lock.queues;
/* Transfer lock */
#ifdef ERTS_ENABLE_LOCK_CHECK
tlocks &= ~lock;
@@ -372,9 +310,9 @@ transfer_locks(Process *p,
ERTS_LC_ASSERT(ERTS_PROC_LOCK_FLGS_READ_(&p->lock)
& (lock << ERTS_PROC_LOCK_WAITER_SHIFT));
transferred++;
- wtr = dequeue_waiter(qs, lock_no);
+ wtr = dequeue_waiter(&p->lock, lock_no);
ERTS_LC_ASSERT(wtr);
- if (!qs->queue[lock_no])
+ if (!p->lock.queue[lock_no])
unset_waiter |= lock;
ERTS_LC_ASSERT(wtr->uflgs & lock);
wtr->uflgs &= ~lock;
@@ -463,7 +401,6 @@ wait_for_locks(Process *p,
{
erts_pix_lock_t *pix_lock = pixlck ? pixlck : ERTS_PID2PIXLOCK(p->id);
erts_tse_t *wtr;
- erts_proc_lock_queues_t *qs;
/* Acquire a waiter object on which this thread can wait. */
wtr = tse_fetch(pix_lock);
@@ -479,18 +416,6 @@ wait_for_locks(Process *p,
ERTS_LC_ASSERT(erts_lc_pix_lock_is_locked(pix_lock));
- qs = wtr->udata;
- ASSERT(qs);
- /* Provide the process with waiter queues, if it doesn't have one. */
- if (!p->lock.queues) {
- qs->next = NULL;
- p->lock.queues = qs;
- }
- else {
- qs->next = p->lock.queues->next;
- p->lock.queues->next = qs;
- }
-
#ifdef ERTS_PROC_LOCK_HARD_DEBUG
check_queue(&p->lock);
#endif
@@ -504,7 +429,9 @@ wait_for_locks(Process *p,
check_queue(&p->lock);
#endif
- if (wtr->uflgs) {
+ if (wtr->uflgs == 0)
+ erts_pix_unlock(pix_lock);
+ else {
/* We didn't get them all; need to wait... */
ASSERT((wtr->uflgs & ~ERTS_PROC_LOCKS_ALL) == 0);
@@ -529,28 +456,12 @@ wait_for_locks(Process *p,
} while (res != 0);
}
- erts_pix_lock(pix_lock);
-
ASSERT(wtr->uflgs == 0);
}
- /* Recover some queues to store in the waiter. */
- ERTS_LC_ASSERT(p->lock.queues);
- if (p->lock.queues->next) {
- qs = p->lock.queues->next;
- p->lock.queues->next = qs->next;
- }
- else {
- qs = p->lock.queues;
- p->lock.queues = NULL;
- }
- wtr->udata = qs;
-
- erts_pix_unlock(pix_lock);
-
ERTS_LC_ASSERT(locks == (ERTS_PROC_LOCK_FLGS_READ_(&p->lock) & locks));
- tse_return(wtr, 0);
+ tse_return(wtr);
}
/*
@@ -651,6 +562,16 @@ erts_proc_unlock_failed(Process *p,
transfer_locks(p, wait_locks, pix_lock, 1); /* unlocks pix_lock */
}
+#endif /* ERTS_PROC_LOCK_OWN_IMPL */
+
+void
+erts_proc_lock_prepare_proc_lock_waiter(void)
+{
+#if ERTS_PROC_LOCK_OWN_IMPL
+ tse_return(tse_fetch(NULL));
+#endif
+}
+
/*
* proc_safelock() locks process locks on two processes. In order
* to avoid a deadlock, proc_safelock() unlocks those locks that
@@ -659,12 +580,11 @@ erts_proc_unlock_failed(Process *p,
*/
static void
-proc_safelock(Process *a_proc,
- erts_pix_lock_t *a_pix_lck,
+proc_safelock(int is_sched,
+ Process *a_proc,
ErtsProcLocks a_have_locks,
ErtsProcLocks a_need_locks,
Process *b_proc,
- erts_pix_lock_t *b_pix_lck,
ErtsProcLocks b_have_locks,
ErtsProcLocks b_need_locks)
{
@@ -672,7 +592,6 @@ proc_safelock(Process *a_proc,
#ifdef ERTS_ENABLE_LOCK_CHECK
Eterm pid1, pid2;
#endif
- erts_pix_lock_t *pix_lck1, *pix_lck2;
ErtsProcLocks need_locks1, have_locks1, need_locks2, have_locks2;
ErtsProcLocks unlock_mask;
int lock_no, refc1 = 0, refc2 = 0;
@@ -689,14 +608,12 @@ proc_safelock(Process *a_proc,
#ifdef ERTS_ENABLE_LOCK_CHECK
pid1 = a_proc->id;
#endif
- pix_lck1 = a_pix_lck;
need_locks1 = a_need_locks;
have_locks1 = a_have_locks;
p2 = b_proc;
#ifdef ERTS_ENABLE_LOCK_CHECK
pid2 = b_proc->id;
#endif
- pix_lck2 = b_pix_lck;
need_locks2 = b_need_locks;
have_locks2 = b_have_locks;
}
@@ -705,14 +622,12 @@ proc_safelock(Process *a_proc,
#ifdef ERTS_ENABLE_LOCK_CHECK
pid1 = b_proc->id;
#endif
- pix_lck1 = b_pix_lck;
need_locks1 = b_need_locks;
have_locks1 = b_have_locks;
p2 = a_proc;
#ifdef ERTS_ENABLE_LOCK_CHECK
pid2 = a_proc->id;
#endif
- pix_lck2 = a_pix_lck;
need_locks2 = a_need_locks;
have_locks2 = a_have_locks;
}
@@ -723,14 +638,12 @@ proc_safelock(Process *a_proc,
#ifdef ERTS_ENABLE_LOCK_CHECK
pid1 = a_proc->id;
#endif
- pix_lck1 = a_pix_lck;
need_locks1 = a_need_locks | b_need_locks;
have_locks1 = a_have_locks | b_have_locks;
p2 = NULL;
#ifdef ERTS_ENABLE_LOCK_CHECK
pid2 = 0;
#endif
- pix_lck2 = NULL;
need_locks2 = 0;
have_locks2 = 0;
}
@@ -740,14 +653,12 @@ proc_safelock(Process *a_proc,
#ifdef ERTS_ENABLE_LOCK_CHECK
pid1 = b_proc->id;
#endif
- pix_lck1 = b_pix_lck;
need_locks1 = b_need_locks;
have_locks1 = b_have_locks;
p2 = NULL;
#ifdef ERTS_ENABLE_LOCK_CHECK
pid2 = 0;
#endif
- pix_lck2 = NULL;
need_locks2 = 0;
have_locks2 = 0;
#ifdef ERTS_ENABLE_LOCK_CHECK
@@ -795,21 +706,21 @@ proc_safelock(Process *a_proc,
if (unlock_locks) {
have_locks1 &= ~unlock_locks;
need_locks1 |= unlock_locks;
- if (!have_locks1) {
+ if (!is_sched && !have_locks1) {
refc1 = 1;
erts_smp_proc_inc_refc(p1);
}
- erts_smp_proc_unlock__(p1, pix_lck1, unlock_locks);
+ erts_smp_proc_unlock(p1, unlock_locks);
}
unlock_locks = unlock_mask & have_locks2;
if (unlock_locks) {
have_locks2 &= ~unlock_locks;
need_locks2 |= unlock_locks;
- if (!have_locks2) {
+ if (!is_sched && !have_locks2) {
refc2 = 1;
erts_smp_proc_inc_refc(p2);
}
- erts_smp_proc_unlock__(p2, pix_lck2, unlock_locks);
+ erts_smp_proc_unlock(p2, unlock_locks);
}
}
@@ -840,7 +751,7 @@ proc_safelock(Process *a_proc,
if (need_locks2 & lock)
lock_no--;
locks = need_locks1 & lock_mask;
- erts_smp_proc_lock__(p1, pix_lck1, locks);
+ erts_smp_proc_lock(p1, locks);
have_locks1 |= locks;
need_locks1 &= ~locks;
}
@@ -851,7 +762,7 @@ proc_safelock(Process *a_proc,
lock = (1 << ++lock_no);
}
locks = need_locks2 & lock_mask;
- erts_smp_proc_lock__(p2, pix_lck2, locks);
+ erts_smp_proc_lock(p2, locks);
have_locks2 |= locks;
need_locks2 &= ~locks;
}
@@ -886,10 +797,12 @@ proc_safelock(Process *a_proc,
}
#endif
- if (refc1)
- erts_smp_proc_dec_refc(p1);
- if (refc2)
- erts_smp_proc_dec_refc(p2);
+ if (!is_sched) {
+ if (refc1)
+ erts_smp_proc_dec_refc(p1);
+ if (refc2)
+ erts_smp_proc_dec_refc(p2);
+ }
}
void
@@ -900,77 +813,192 @@ erts_proc_safelock(Process *a_proc,
ErtsProcLocks b_have_locks,
ErtsProcLocks b_need_locks)
{
- proc_safelock(a_proc,
- a_proc ? ERTS_PID2PIXLOCK(a_proc->id) : NULL,
+ proc_safelock(erts_get_scheduler_id() != 0,
+ a_proc,
a_have_locks,
a_need_locks,
b_proc,
- b_proc ? ERTS_PID2PIXLOCK(b_proc->id) : NULL,
b_have_locks,
b_need_locks);
}
-/*
- * erts_pid2proc_safelock() is called from erts_pid2proc_opt() when
- * it wasn't possible to trylock all locks needed.
- * c_p - current process
- * c_p_have_locks - locks held on c_p
- * pid - process id of process we are looking up
- * proc - process struct of process we are looking
- * up (both in and out argument)
- * need_locks - all locks we need (including have_locks)
- * pix_lock - pix lock for process we are looking up
- * flags - option flags
- */
-void
-erts_pid2proc_safelock(Process *c_p,
- ErtsProcLocks c_p_have_locks,
- Process **proc,
- ErtsProcLocks need_locks,
- erts_pix_lock_t *pix_lock,
- int flags)
+Process *
+erts_pid2proc_opt(Process *c_p,
+ ErtsProcLocks c_p_have_locks,
+ Eterm pid,
+ ErtsProcLocks pid_need_locks,
+ int flags)
{
- Process *p = *proc;
- ERTS_LC_ASSERT(p->lock.refc > 0);
- ERTS_LC_ASSERT(process_tab[internal_pid_index(p->id)] == p);
- p->lock.refc++;
- erts_pix_unlock(pix_lock);
-
- proc_safelock(c_p,
- c_p ? ERTS_PID2PIXLOCK(c_p->id) : NULL,
- c_p_have_locks,
- c_p_have_locks,
- p,
- pix_lock,
- 0,
- need_locks);
+ Process *dec_refc_proc = NULL;
+ int need_ptl;
+ ErtsProcLocks need_locks;
+ Uint pix;
+ Process *proc;
+#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_ENABLE_LOCK_COUNT)
+ ErtsProcLocks lcnt_locks;
+#endif
- erts_pix_lock(pix_lock);
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ if (c_p) {
+ ErtsProcLocks might_unlock = c_p_have_locks & pid_need_locks;
+ if (might_unlock)
+ erts_proc_lc_might_unlock(c_p, might_unlock);
+ }
+#endif
- if (!p->is_exiting
- || ((flags & ERTS_P2P_FLG_ALLOW_OTHER_X)
- && process_tab[internal_pid_index(p->id)] == p)) {
- ERTS_LC_ASSERT(p->lock.refc > 1);
- p->lock.refc--;
+ if (is_not_internal_pid(pid))
+ return NULL;
+ pix = internal_pid_index(pid);
+
+ ERTS_LC_ASSERT((pid_need_locks & ERTS_PROC_LOCKS_ALL) == pid_need_locks);
+ need_locks = pid_need_locks;
+
+ if (c_p && c_p->id == pid) {
+ ASSERT(c_p->id != ERTS_INVALID_PID);
+ ASSERT(c_p == erts_pix2proc(pix));
+
+ if (!(flags & ERTS_P2P_FLG_ALLOW_OTHER_X)
+ && ERTS_PROC_IS_EXITING(c_p))
+ return NULL;
+ need_locks &= ~c_p_have_locks;
+ if (!need_locks) {
+ if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
+ erts_smp_proc_inc_refc(c_p);
+ return c_p;
+ }
}
- else {
- /* No proc. Note, we need to keep refc until after process unlock */
- erts_pix_unlock(pix_lock);
- erts_smp_proc_unlock__(p, pix_lock, need_locks);
- *proc = NULL;
- erts_pix_lock(pix_lock);
- ERTS_LC_ASSERT(p->lock.refc > 0);
- if (--p->lock.refc == 0) {
- erts_pix_unlock(pix_lock);
- erts_free_proc(p);
- erts_pix_lock(pix_lock);
+
+ need_ptl = !erts_get_scheduler_id();
+
+ if (need_ptl)
+ erts_smp_rwmtx_rwlock(&erts_proc_tab_rwmtx);
+
+ proc = (Process *) erts_smp_atomic_read_ddrb(&erts_proc.tab[pix]);
+
+ if (proc) {
+ if (proc->id != pid)
+ proc = NULL;
+ else if (!need_locks) {
+ if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
+ erts_smp_proc_inc_refc(proc);
}
+ else {
+ int busy;
+
+#if ERTS_PROC_LOCK_OWN_IMPL
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ lcnt_locks = need_locks;
+ if (!(flags & ERTS_P2P_FLG_TRY_LOCK)) {
+ erts_lcnt_proc_lock(&proc->lock, need_locks);
+ }
+#endif
+
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ /* Make sure erts_pid2proc_safelock() is enough to handle
+ a potential lock order violation situation... */
+ busy = erts_proc_lc_trylock_force_busy(proc, need_locks);
+ if (!busy)
+#endif
+#endif /* ERTS_PROC_LOCK_OWN_IMPL */
+ {
+ /* Try a quick trylock to grab all the locks we need. */
+ busy = (int) erts_smp_proc_raw_trylock__(proc, need_locks);
+
+#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_ENABLE_LOCK_CHECK)
+ erts_proc_lc_trylock(proc, need_locks, !busy);
+#endif
+#ifdef ERTS_PROC_LOCK_DEBUG
+ if (!busy)
+ erts_proc_lock_op_debug(proc, need_locks, 1);
+#endif
+ }
+
+#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_ENABLE_LOCK_COUNT)
+ if (flags & ERTS_P2P_FLG_TRY_LOCK)
+ erts_lcnt_proc_trylock(&proc->lock, need_locks,
+ busy ? EBUSY : 0);
+#endif
+
+ if (!busy) {
+ if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
+ erts_smp_proc_inc_refc(proc);
+
+#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_ENABLE_LOCK_COUNT)
+ /* all is great */
+ if (!(flags & ERTS_P2P_FLG_TRY_LOCK))
+ erts_lcnt_proc_lock_post_x(&proc->lock, lcnt_locks,
+ __FILE__, __LINE__);
+#endif
+
+ }
+ else {
+ if (flags & ERTS_P2P_FLG_TRY_LOCK)
+ proc = ERTS_PROC_LOCK_BUSY;
+ else {
+ if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
+ erts_smp_proc_inc_refc(proc);
+
+#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_ENABLE_LOCK_COUNT)
+ erts_lcnt_proc_lock_unaquire(&proc->lock, lcnt_locks);
+#endif
+
+ if (need_ptl) {
+ erts_smp_proc_inc_refc(proc);
+ dec_refc_proc = proc;
+ erts_smp_rwmtx_rwunlock(&erts_proc_tab_rwmtx);
+ need_ptl = 0;
+ }
+
+ proc_safelock(!need_ptl,
+ c_p,
+ c_p_have_locks,
+ c_p_have_locks,
+ proc,
+ 0,
+ need_locks);
+ }
+ }
+ }
+ }
+
+ if (need_ptl)
+ erts_smp_rwmtx_rwunlock(&erts_proc_tab_rwmtx);
+
+ if (need_locks
+ && proc
+ && proc != ERTS_PROC_LOCK_BUSY
+ && (!(flags & ERTS_P2P_FLG_ALLOW_OTHER_X)
+ ? ERTS_PROC_IS_EXITING(proc)
+ : (proc
+ != (Process *) erts_smp_atomic_read_nob(&erts_proc.tab[pix])))) {
+
+ erts_smp_proc_unlock(proc, need_locks);
+
+ if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
+ dec_refc_proc = proc;
+ proc = NULL;
+
}
+
+ if (dec_refc_proc)
+ erts_smp_proc_dec_refc(dec_refc_proc);
+
+#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_PROC_LOCK_DEBUG)
+ ERTS_LC_ASSERT(!proc
+ || proc == ERTS_PROC_LOCK_BUSY
+ || (pid_need_locks ==
+ (ERTS_PROC_LOCK_FLGS_READ_(&proc->lock)
+ & pid_need_locks)));
+#endif
+
+ return proc;
}
void
erts_proc_lock_init(Process *p)
{
+#if ERTS_PROC_LOCK_OWN_IMPL
+ int i;
/* We always start with all locks locked */
#if ERTS_PROC_LOCK_ATOMIC_IMPL
erts_smp_atomic32_init_nob(&p->lock.flags,
@@ -978,29 +1006,62 @@ erts_proc_lock_init(Process *p)
#else
p->lock.flags = ERTS_PROC_LOCKS_ALL;
#endif
- p->lock.queues = NULL;
- p->lock.refc = 1;
+ for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++)
+ p->lock.queue[i] = NULL;
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_proc_lc_trylock(p, ERTS_PROC_LOCKS_ALL, 1);
+#endif
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ erts_mtx_init_x(&p->lock.main, "proc_main", p->id);
+ ethr_mutex_lock(&p->lock.main.mtx);
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_trylock(1, &p->lock.main.lc);
+#endif
+ erts_mtx_init_x(&p->lock.link, "proc_link", p->id);
+ ethr_mutex_lock(&p->lock.link.mtx);
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_trylock(1, &p->lock.link.lc);
+#endif
+ erts_mtx_init_x(&p->lock.msgq, "proc_msgq", p->id);
+ ethr_mutex_lock(&p->lock.msgq.mtx);
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_trylock(1, &p->lock.msgq.lc);
+#endif
+ erts_mtx_init_x(&p->lock.status, "proc_status", p->id);
+ ethr_mutex_lock(&p->lock.status.mtx);
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_trylock(1, &p->lock.status.lc);
+#endif
+#endif
+ erts_atomic32_init_nob(&p->lock.refc, 1);
+#ifdef ERTS_PROC_LOCK_DEBUG
+ for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++)
+ erts_smp_atomic32_init_nob(&p->lock.locked[i], (erts_aint32_t) 1);
+#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
erts_lcnt_proc_lock_init(p);
erts_lcnt_proc_lock(&(p->lock), ERTS_PROC_LOCKS_ALL);
erts_lcnt_proc_lock_post_x(&(p->lock), ERTS_PROC_LOCKS_ALL, __FILE__, __LINE__);
#endif
-
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_proc_lc_trylock(p, ERTS_PROC_LOCKS_ALL, 1);
+}
+
+void
+erts_proc_lock_fin(Process *p)
+{
+#if ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ erts_mtx_destroy(&p->lock.main);
+ erts_mtx_destroy(&p->lock.link);
+ erts_mtx_destroy(&p->lock.msgq);
+ erts_mtx_destroy(&p->lock.status);
#endif
-#ifdef ERTS_PROC_LOCK_DEBUG
- {
- int i;
- for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++)
- erts_smp_atomic32_init_nob(&p->lock.locked[i], (erts_aint32_t) 1);
- }
+#if defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP)
+ erts_lcnt_proc_lock_destroy(p);
#endif
}
/* --- Process lock counting ----------------------------------------------- */
-#ifdef ERTS_ENABLE_LOCK_COUNT
+#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_ENABLE_LOCK_COUNT)
void erts_lcnt_proc_lock_init(Process *p) {
if (erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK) {
if (p->id != ERTS_INVALID_PID) {
@@ -1119,7 +1180,7 @@ void erts_lcnt_enable_proc_lock_count(int enable) {
int i;
for (i = 0; i < erts_max_processes; ++i) {
- Process* p = process_tab[i];
+ Process* p = erts_pix2proc(i);
if (p) {
if (enable) {
if (!ERTS_LCNT_LOCK_TYPE(&(p->lock.lcnt_main))) {
@@ -1141,6 +1202,8 @@ void erts_lcnt_enable_proc_lock_count(int enable) {
#ifdef ERTS_ENABLE_LOCK_CHECK
+#if ERTS_PROC_LOCK_OWN_IMPL
+
void
erts_proc_lc_lock(Process *p, ErtsProcLocks locks)
{
@@ -1213,9 +1276,12 @@ erts_proc_lc_unlock(Process *p, ErtsProcLocks locks)
}
}
+#endif /* ERTS_PROC_LOCK_OWN_IMPL */
+
void
erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks)
{
+#if ERTS_PROC_LOCK_OWN_IMPL
erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1,
p->id,
ERTS_LC_FLG_LT_PROCLOCK);
@@ -1235,11 +1301,22 @@ erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks)
lck.id = lc_id.proc_lock_main;
erts_lc_might_unlock(&lck);
}
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ erts_lc_might_unlock(&p->lock.main.lc);
+ if (locks & ERTS_PROC_LOCK_LINK)
+ erts_lc_might_unlock(&p->lock.link.lc);
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ erts_lc_might_unlock(&p->lock.msgq.lc);
+ if (locks & ERTS_PROC_LOCK_STATUS)
+ erts_lc_might_unlock(&p->lock.status.lc);
+#endif
}
void
erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks)
{
+#if ERTS_PROC_LOCK_OWN_IMPL
erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1,
p->id,
ERTS_LC_FLG_LT_PROCLOCK);
@@ -1259,11 +1336,22 @@ erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks)
lck.id = lc_id.proc_lock_status;
erts_lc_require_lock(&lck);
}
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ erts_lc_require_lock(&p->lock.main.lc);
+ if (locks & ERTS_PROC_LOCK_LINK)
+ erts_lc_require_lock(&p->lock.link.lc);
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ erts_lc_require_lock(&p->lock.msgq.lc);
+ if (locks & ERTS_PROC_LOCK_STATUS)
+ erts_lc_require_lock(&p->lock.status.lc);
+#endif
}
void
erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks)
{
+#if ERTS_PROC_LOCK_OWN_IMPL
erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1,
p->id,
ERTS_LC_FLG_LT_PROCLOCK);
@@ -1283,8 +1371,19 @@ erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks)
lck.id = lc_id.proc_lock_main;
erts_lc_unrequire_lock(&lck);
}
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ erts_lc_unrequire_lock(&p->lock.main.lc);
+ if (locks & ERTS_PROC_LOCK_LINK)
+ erts_lc_unrequire_lock(&p->lock.link.lc);
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ erts_lc_unrequire_lock(&p->lock.msgq.lc);
+ if (locks & ERTS_PROC_LOCK_STATUS)
+ erts_lc_unrequire_lock(&p->lock.status.lc);
+#endif
}
+#if ERTS_PROC_LOCK_OWN_IMPL
int
erts_proc_lc_trylock_force_busy(Process *p, ErtsProcLocks locks)
@@ -1310,21 +1409,30 @@ erts_proc_lc_trylock_force_busy(Process *p, ErtsProcLocks locks)
return 0;
}
+#endif /* ERTS_PROC_LOCK_OWN_IMPL */
+
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->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
}
+#if ERTS_PROC_LOCK_OWN_IMPL
#define ERTS_PROC_LC_EMPTY_LOCK_INIT \
ERTS_LC_LOCK_INIT(-1, THE_NON_VALUE, ERTS_LC_FLG_LT_PROCLOCK)
+#endif /* ERTS_PROC_LOCK_OWN_IMPL */
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[4] = {ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT,
@@ -1345,7 +1453,17 @@ 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->id;
}
-
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ erts_lc_lock_t have_locks[4];
+ 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_STATUS)
+ have_locks[have_locks_len++] = p->lock.status.lc;
+#endif
erts_lc_check(have_locks, have_locks_len, NULL, 0);
}
@@ -1354,6 +1472,7 @@ 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[4] = {ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT,
@@ -1395,6 +1514,27 @@ 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->id;
}
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ erts_lc_lock_t have_locks[4];
+ erts_lc_lock_t have_not_locks[4];
+
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ have_locks[have_locks_len++] = p->lock.main.lc;
+ else
+ have_not_locks[have_not_locks_len++] = p->lock.main.lc;
+ if (locks & ERTS_PROC_LOCK_LINK)
+ have_locks[have_locks_len++] = p->lock.link.lc;
+ else
+ have_not_locks[have_not_locks_len++] = p->lock.link.lc;
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ have_locks[have_locks_len++] = p->lock.msgq.lc;
+ else
+ have_not_locks[have_not_locks_len++] = p->lock.msgq.lc;
+ if (locks & ERTS_PROC_LOCK_STATUS)
+ have_locks[have_locks_len++] = p->lock.status.lc;
+ else
+ have_not_locks[have_not_locks_len++] = p->lock.status.lc;
+#endif
erts_lc_check(have_locks, have_locks_len,
have_not_locks, have_not_locks_len);
@@ -1404,6 +1544,8 @@ ErtsProcLocks
erts_proc_lc_my_proc_locks(Process *p)
{
int resv[4];
+ ErtsProcLocks res = 0;
+#if ERTS_PROC_LOCK_OWN_IMPL
erts_lc_lock_t locks[4] = {ERTS_LC_LOCK_INIT(lc_id.proc_lock_main,
p->id,
ERTS_LC_FLG_LT_PROCLOCK),
@@ -1416,8 +1558,12 @@ erts_proc_lc_my_proc_locks(Process *p)
ERTS_LC_LOCK_INIT(lc_id.proc_lock_status,
p->id,
ERTS_LC_FLG_LT_PROCLOCK)};
-
- ErtsProcLocks res = 0;
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ erts_lc_lock_t locks[4] = {p->lock.main.lc,
+ p->lock.link.lc,
+ p->lock.msgq.lc,
+ p->lock.status.lc};
+#endif
erts_lc_have_locks(resv, locks, 4);
if (resv[0])
@@ -1450,7 +1596,7 @@ erts_proc_lc_chk_no_proc_locks(char *file, int line)
#endif /* #ifdef ERTS_ENABLE_LOCK_CHECK */
-#ifdef ERTS_PROC_LOCK_HARD_DEBUG
+#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_PROC_LOCK_HARD_DEBUG)
void
check_queue(erts_proc_lock_t *lck)
{
@@ -1463,24 +1609,24 @@ check_queue(erts_proc_lock_t *lck)
if (lflgs & wtr) {
int n;
erts_tse_t *wtr;
- ERTS_LC_ASSERT(lck->queues && lck->queues->queue[lock_no]);
- wtr = lck->queues->queue[lock_no];
+ ERTS_LC_ASSERT(lck->queue[lock_no]);
+ wtr = lck->queue[lock_no];
n = 0;
do {
wtr = wtr->next;
n++;
- } while (wtr != lck->queues->queue[lock_no]);
+ } while (wtr != lck->queue[lock_no]);
do {
wtr = wtr->prev;
n--;
- } while (wtr != lck->queues->queue[lock_no]);
+ } while (wtr != lck->queue[lock_no]);
ERTS_LC_ASSERT(n == 0);
}
else {
- ERTS_LC_ASSERT(!lck->queues || !lck->queues->queue[lock_no]);
+ ERTS_LC_ASSERT(!lck->queue[lock_no]);
}
}
}
#endif
-#endif /* ERTS_SMP (the whole file) */
+#endif /* ERTS_SMP */
diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h
index 413c45480c..4aec19c8c3 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-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2012. 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
@@ -37,10 +37,21 @@
#include "erl_smp.h"
+#if defined(VALGRIND) || defined(ETHR_DISABLE_NATIVE_IMPLS)
+# define ERTS_PROC_LOCK_OWN_IMPL 0
+#else
+# define ERTS_PROC_LOCK_OWN_IMPL 1
+#endif
+
#define ERTS_PROC_LOCK_ATOMIC_IMPL 0
#define ERTS_PROC_LOCK_SPINLOCK_IMPL 0
#define ERTS_PROC_LOCK_MUTEX_IMPL 0
+#if !ERTS_PROC_LOCK_OWN_IMPL
+#define ERTS_PROC_LOCK_RAW_MUTEX_IMPL 1
+#else
+#define ERTS_PROC_LOCK_RAW_MUTEX_IMPL 0
+
#if defined(ETHR_HAVE_32BIT_NATIVE_ATOMIC_OPS)
# undef ERTS_PROC_LOCK_ATOMIC_IMPL
# define ERTS_PROC_LOCK_ATOMIC_IMPL 1
@@ -52,29 +63,38 @@
# define ERTS_PROC_LOCK_MUTEX_IMPL 1
#endif
+#endif
+
#define ERTS_PROC_LOCK_MAX_BIT 3
typedef erts_aint32_t ErtsProcLocks;
-typedef struct erts_proc_lock_queues_t_ erts_proc_lock_queues_t;
-
typedef struct erts_proc_lock_t_ {
+#if ERTS_PROC_LOCK_OWN_IMPL
#if ERTS_PROC_LOCK_ATOMIC_IMPL
erts_smp_atomic32_t flags;
#else
ErtsProcLocks flags;
#endif
- erts_proc_lock_queues_t *queues;
- Sint32 refc;
-#ifdef ERTS_PROC_LOCK_DEBUG
- erts_smp_atomic32_t locked[ERTS_PROC_LOCK_MAX_BIT+1];
-#endif
+ erts_tse_t *queue[ERTS_PROC_LOCK_MAX_BIT+1];
#ifdef ERTS_ENABLE_LOCK_COUNT
erts_lcnt_lock_t lcnt_main;
erts_lcnt_lock_t lcnt_link;
erts_lcnt_lock_t lcnt_msgq;
erts_lcnt_lock_t lcnt_status;
#endif
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ erts_mtx_t main;
+ erts_mtx_t link;
+ erts_mtx_t msgq;
+ erts_mtx_t status;
+#else
+# error "no implementation"
+#endif
+ erts_atomic32_t refc;
+#ifdef ERTS_PROC_LOCK_DEBUG
+ erts_smp_atomic32_t locked[ERTS_PROC_LOCK_MAX_BIT+1];
+#endif
} erts_proc_lock_t;
/* Process lock flags */
@@ -107,11 +127,9 @@ typedef struct erts_proc_lock_t_ {
/*
* Status lock:
* Protects the following fields in the process structure:
- * * status
- * * rstatus
- * * status_flags
* * pending_suspenders
* * suspendee
+ * * ...
*/
#define ERTS_PROC_LOCK_STATUS (((ErtsProcLocks) 1) << ERTS_PROC_LOCK_MAX_BIT)
@@ -143,14 +161,11 @@ typedef struct erts_proc_lock_t_ {
* Other rules regarding process locking:
*
* Exiting processes:
- * When changing status to P_EXITING on a process, you are required
- * to take all process locks (ERTS_PROC_LOCKS_ALL). Thus, by holding
- * at least one process lock (whichever one doesn't matter) you
- * are guaranteed that the process won't exit until the lock you are
- * holding has been released. Appart from all process locks also
- * the pix lock corresponding to the process has to be held.
- * At the same time as status is changed to P_EXITING, also the
- * field 'is_exiting' in the process structure is set to a value != 0.
+ * When changing state to exiting (ERTS_PSFLG_EXITING) on a process,
+ * you are required to take all process locks (ERTS_PROC_LOCKS_ALL).
+ * Thus, by holding at least one process lock (whichever one doesn't
+ * matter) you are guaranteed that the process won't exit until the
+ * lock you are holding has been released.
*
* Lock order:
* Process locks with low numeric values has to be locked before
@@ -262,12 +277,10 @@ typedef struct {
} u;
} erts_pix_lock_t;
-#define ERTS_PIX2PIXLOCKIX(PIX) \
- ((PIX) & ((1 << ERTS_PIX_LOCKS_BITS) - 1))
-#define ERTS_PIX2PIXLOCK(PIX) \
- (&erts_pix_locks[ERTS_PIX2PIXLOCKIX((PIX))])
#define ERTS_PID2PIXLOCK(PID) \
- ERTS_PIX2PIXLOCK(internal_pid_data((PID)))
+ (&erts_pix_locks[(internal_pid_data((PID)) & ((1 << ERTS_PIX_LOCKS_BITS) - 1))])
+
+#if ERTS_PROC_LOCK_OWN_IMPL
#if ERTS_PROC_LOCK_ATOMIC_IMPL
@@ -337,11 +350,13 @@ erts_proc_lock_flags_cmpxchg(erts_proc_lock_t *lck, ErtsProcLocks new,
#define ERTS_PROC_LOCK_FLGS_READ_(L) ((L)->flags)
#endif /* end no opt atomic ops */
+#endif /* ERTS_PROC_LOCK_OWN_IMPL */
extern erts_pix_lock_t erts_pix_locks[ERTS_NO_OF_PIX_LOCKS];
void erts_init_proc_lock(int cpus);
void erts_proc_lock_prepare_proc_lock_waiter(void);
+#if ERTS_PROC_LOCK_OWN_IMPL
void erts_proc_lock_failed(Process *,
erts_pix_lock_t *,
ErtsProcLocks,
@@ -349,6 +364,7 @@ void erts_proc_lock_failed(Process *,
void erts_proc_unlock_failed(Process *,
erts_pix_lock_t *,
ErtsProcLocks);
+#endif
ERTS_GLB_INLINE void erts_pix_lock(erts_pix_lock_t *);
ERTS_GLB_INLINE void erts_pix_unlock(erts_pix_lock_t *);
@@ -412,6 +428,7 @@ ERTS_GLB_INLINE int erts_lc_pix_lock_is_locked(erts_pix_lock_t *pixlck)
ERTS_GLB_INLINE ErtsProcLocks
erts_smp_proc_raw_trylock__(Process *p, ErtsProcLocks locks)
{
+#if ERTS_PROC_LOCK_OWN_IMPL
ErtsProcLocks expct_lflgs = 0;
while (1) {
@@ -431,8 +448,38 @@ erts_smp_proc_raw_trylock__(Process *p, ErtsProcLocks locks)
/* cmpxchg failed, try again (should be rare). */
expct_lflgs = lflgs;
}
-}
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ if (erts_mtx_trylock(&p->lock.main) == EBUSY)
+ goto busy_main;
+ if (locks & ERTS_PROC_LOCK_LINK)
+ if (erts_mtx_trylock(&p->lock.link) == EBUSY)
+ goto busy_link;
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ if (erts_mtx_trylock(&p->lock.msgq) == EBUSY)
+ goto busy_msgq;
+ if (locks & ERTS_PROC_LOCK_STATUS)
+ if (erts_mtx_trylock(&p->lock.status) == EBUSY)
+ goto busy_status;
+
+ return 0;
+
+busy_status:
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ erts_mtx_unlock(&p->lock.msgq);
+busy_msgq:
+ if (locks & ERTS_PROC_LOCK_LINK)
+ erts_mtx_unlock(&p->lock.link);
+busy_link:
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ erts_mtx_unlock(&p->lock.main);
+busy_main:
+
+ return EBUSY;
+#endif
+}
ERTS_GLB_INLINE void
#ifdef ERTS_ENABLE_LOCK_COUNT
@@ -446,10 +493,13 @@ erts_smp_proc_lock__(Process *p,
ErtsProcLocks locks)
#endif
{
+#if ERTS_PROC_LOCK_OWN_IMPL
+
ErtsProcLocks old_lflgs;
#if !ERTS_PROC_LOCK_ATOMIC_IMPL
erts_pix_lock(pix_lck);
#endif
+
#ifdef ERTS_ENABLE_LOCK_COUNT
erts_lcnt_proc_lock(&(p->lock), locks);
#endif
@@ -473,12 +523,14 @@ erts_smp_proc_lock__(Process *p,
erts_pix_unlock(pix_lck);
}
#endif
+
#ifdef ERTS_ENABLE_LOCK_COUNT
erts_lcnt_proc_lock_post_x(&(p->lock), locks, file, line);
#endif
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_proc_lc_lock(p, locks);
#endif
+
#ifdef ERTS_PROC_LOCK_DEBUG
erts_proc_lock_op_debug(p, locks, 1);
#endif
@@ -486,6 +538,22 @@ erts_smp_proc_lock__(Process *p,
#if ERTS_PROC_LOCK_ATOMIC_IMPL
ETHR_COMPILER_BARRIER;
#endif
+
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ erts_mtx_lock(&p->lock.main);
+ if (locks & ERTS_PROC_LOCK_LINK)
+ erts_mtx_lock(&p->lock.link);
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ erts_mtx_lock(&p->lock.msgq);
+ if (locks & ERTS_PROC_LOCK_STATUS)
+ erts_mtx_lock(&p->lock.status);
+
+#ifdef ERTS_PROC_LOCK_DEBUG
+ erts_proc_lock_op_debug(p, locks, 1);
+#endif
+
+#endif
}
ERTS_GLB_INLINE void
@@ -493,6 +561,7 @@ erts_smp_proc_unlock__(Process *p,
erts_pix_lock_t *pix_lck,
ErtsProcLocks locks)
{
+#if ERTS_PROC_LOCK_OWN_IMPL
ErtsProcLocks old_lflgs;
#if ERTS_PROC_LOCK_ATOMIC_IMPL
@@ -557,6 +626,23 @@ erts_smp_proc_unlock__(Process *p,
break;
}
+
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+
+#ifdef ERTS_PROC_LOCK_DEBUG
+ erts_proc_lock_op_debug(p, locks, 0);
+#endif
+
+ if (locks & ERTS_PROC_LOCK_STATUS)
+ erts_mtx_unlock(&p->lock.status);
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ erts_mtx_unlock(&p->lock.msgq);
+ if (locks & ERTS_PROC_LOCK_LINK)
+ erts_mtx_unlock(&p->lock.link);
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ erts_mtx_unlock(&p->lock.main);
+#endif
+
}
ERTS_GLB_INLINE int
@@ -564,6 +650,7 @@ erts_smp_proc_trylock__(Process *p,
erts_pix_lock_t *pix_lck,
ErtsProcLocks locks)
{
+#if ERTS_PROC_LOCK_OWN_IMPL
int res;
#ifdef ERTS_ENABLE_LOCK_CHECK
@@ -575,6 +662,7 @@ erts_smp_proc_trylock__(Process *p,
else
#endif
{
+
#if !ERTS_PROC_LOCK_ATOMIC_IMPL
erts_pix_lock(pix_lck);
#endif
@@ -613,8 +701,18 @@ erts_smp_proc_trylock__(Process *p,
#if ERTS_PROC_LOCK_ATOMIC_IMPL
ETHR_COMPILER_BARRIER;
#endif
-
return res;
+
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ if (erts_smp_proc_raw_trylock__(p, locks) != 0)
+ return EBUSY;
+ else {
+#ifdef ERTS_PROC_LOCK_DEBUG
+ erts_proc_lock_op_debug(p, locks, 1);
+#endif
+ return 0;
+ }
+#endif
}
#ifdef ERTS_PROC_LOCK_DEBUG
@@ -713,44 +811,36 @@ erts_smp_proc_trylock(Process *p, ErtsProcLocks locks)
#endif
}
-
ERTS_GLB_INLINE void erts_smp_proc_inc_refc(Process *p)
{
#ifdef ERTS_SMP
- erts_pix_lock_t *pixlck = ERTS_PID2PIXLOCK(p->id);
- erts_pix_lock(pixlck);
- ERTS_LC_ASSERT(p->lock.refc > 0);
- p->lock.refc++;
- erts_pix_unlock(pixlck);
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_aint32_t refc = erts_atomic32_inc_read_nob(&p->lock.refc);
+ ERTS_SMP_LC_ASSERT(refc > 1);
+#else
+ erts_atomic32_inc_nob(&p->lock.refc);
+#endif
#endif
}
ERTS_GLB_INLINE void erts_smp_proc_dec_refc(Process *p)
{
#ifdef ERTS_SMP
- Process *fp;
- erts_pix_lock_t *pixlck = ERTS_PID2PIXLOCK(p->id);
- erts_pix_lock(pixlck);
- ERTS_LC_ASSERT(p->lock.refc > 0);
- fp = --p->lock.refc == 0 ? p : NULL;
- erts_pix_unlock(pixlck);
- if (fp)
- erts_free_proc(fp);
+ erts_aint32_t refc = erts_atomic32_dec_read_nob(&p->lock.refc);
+ ERTS_SMP_LC_ASSERT(refc >= 0);
+ if (refc == 0)
+ erts_free_proc(p);
#endif
}
-ERTS_GLB_INLINE void erts_smp_proc_add_refc(Process *p, Sint32 refc)
+ERTS_GLB_INLINE void erts_smp_proc_add_refc(Process *p, Sint32 add_refc)
{
#ifdef ERTS_SMP
- Process *fp;
- erts_pix_lock_t *pixlck = ERTS_PID2PIXLOCK(p->id);
- erts_pix_lock(pixlck);
- ERTS_LC_ASSERT(p->lock.refc > 0);
- p->lock.refc += refc;
- fp = p->lock.refc == 0 ? p : NULL;
- erts_pix_unlock(pixlck);
- if (fp)
- erts_free_proc(fp);
+ erts_aint32_t refc = erts_atomic32_add_read_nob(&p->lock.refc,
+ (erts_aint32_t) add_refc);
+ ERTS_SMP_LC_ASSERT(refc >= 0);
+ if (refc == 0)
+ erts_free_proc(p);
#endif
}
@@ -758,6 +848,7 @@ ERTS_GLB_INLINE void erts_smp_proc_add_refc(Process *p, Sint32 refc)
#ifdef ERTS_SMP
void erts_proc_lock_init(Process *);
+void erts_proc_lock_fin(Process *);
void erts_proc_safelock(Process *a_proc,
ErtsProcLocks a_have_locks,
ErtsProcLocks a_need_locks,
@@ -790,211 +881,75 @@ extern const Process erts_proc_lock_busy;
#define erts_pid2proc(PROC, HL, PID, NL) \
erts_pid2proc_opt((PROC), (HL), (PID), (NL), 0)
-ERTS_GLB_INLINE Process *
-erts_pid2proc_opt(Process *, ErtsProcLocks, Eterm, ErtsProcLocks, int);
-#ifdef ERTS_SMP
-void
-erts_pid2proc_safelock(Process *c_p,
- ErtsProcLocks c_p_have_locks,
- Process **proc,
- ErtsProcLocks need_locks,
- erts_pix_lock_t *pix_lock,
- int flags);
-ERTS_GLB_INLINE Process *erts_pid2proc_unlocked_opt(Eterm pid, int flags);
-#define erts_pid2proc_unlocked(PID) erts_pid2proc_unlocked_opt((PID), 0)
-#else
-#define erts_pid2proc_unlocked_opt(PID, FLGS) \
- erts_pid2proc_opt(NULL, 0, (PID), 0, FLGS)
-#define erts_pid2proc_unlocked(PID) erts_pid2proc_opt(NULL, 0, (PID), 0, 0)
+ERTS_GLB_INLINE Process *erts_pix2proc(int ix);
+ERTS_GLB_INLINE Process *erts_proc_lookup_raw(Eterm pid);
+ERTS_GLB_INLINE Process *erts_proc_lookup(Eterm pid);
+
+#ifndef ERTS_SMP
+ERTS_GLB_INLINE
#endif
+Process *erts_pid2proc_opt(Process *, ErtsProcLocks, Eterm, ErtsProcLocks, int);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-ERTS_GLB_INLINE Process *
-#ifdef ERTS_SMP
-erts_pid2proc_unlocked_opt(Eterm pid, int flags)
-#else
-erts_pid2proc_opt(Process *c_p_unused,
- ErtsProcLocks c_p_have_locks_unused,
- Eterm pid,
- ErtsProcLocks pid_need_locks_unused,
- int flags)
-#endif
+ERTS_GLB_INLINE Process *erts_pix2proc(int ix)
+{
+ Process *proc;
+ ASSERT(0 <= ix && ix < erts_proc.max);
+ proc = (Process *) erts_smp_atomic_read_nob(&erts_proc.tab[ix]);
+ return proc == ERTS_PROC_LOCK_BUSY ? NULL : proc;
+}
+
+ERTS_GLB_INLINE Process *erts_proc_lookup_raw(Eterm pid)
{
- Uint pix;
Process *proc;
+ int pix;
+
+ /*
+ * In SMP case: Only scheduler threads are allowed
+ * to use this function. Other threads need to
+ * atomicaly increment refc at lookup, i.e., use
+ * erts_pid2proc_opt() with ERTS_P2P_FLG_SMP_INC_REFC.
+ */
+ ERTS_SMP_LC_ASSERT(erts_get_scheduler_id());
if (is_not_internal_pid(pid))
return NULL;
pix = internal_pid_index(pid);
- if(pix >= erts_max_processes)
+
+ proc = (Process *) erts_smp_atomic_read_ddrb(&erts_proc.tab[pix]);
+
+ if (proc && proc->id != pid)
return NULL;
- proc = process_tab[pix];
- if (proc) {
- if (proc->id != pid
- || (!(flags & ERTS_P2P_FLG_ALLOW_OTHER_X)
- && proc->status == P_EXITING))
- proc = NULL;
- }
+
return proc;
}
-#ifdef ERTS_SMP
+ERTS_GLB_INLINE Process *erts_proc_lookup(Eterm pid)
+{
+ Process *proc = erts_proc_lookup_raw(pid);
+ if (proc && ERTS_PROC_IS_EXITING(proc))
+ return NULL;
+ return proc;
+}
+#ifndef ERTS_SMP
ERTS_GLB_INLINE Process *
-erts_pid2proc_opt(Process *c_p,
- ErtsProcLocks c_p_have_locks,
+erts_pid2proc_opt(Process *c_p_unused,
+ ErtsProcLocks c_p_have_locks_unused,
Eterm pid,
- ErtsProcLocks pid_need_locks,
+ ErtsProcLocks pid_need_locks_unused,
int flags)
{
- erts_pix_lock_t *pix_lock;
- ErtsProcLocks need_locks;
- Uint pix;
- Process *proc;
-#ifdef ERTS_ENABLE_LOCK_COUNT
- ErtsProcLocks lcnt_locks;
-#endif
-
-#ifdef ERTS_ENABLE_LOCK_CHECK
- if (c_p) {
- ErtsProcLocks might_unlock = c_p_have_locks & pid_need_locks;
- if (might_unlock)
- erts_proc_lc_might_unlock(c_p, might_unlock);
- }
-#endif
- if (is_not_internal_pid(pid)) {
- proc = NULL;
- goto done;
- }
- pix = internal_pid_index(pid);
- if(pix >= erts_max_processes) {
- proc = NULL;
- goto done;
- }
-
- ERTS_LC_ASSERT((pid_need_locks & ERTS_PROC_LOCKS_ALL) == pid_need_locks);
- need_locks = pid_need_locks;
-
- pix_lock = ERTS_PIX2PIXLOCK(pix);
-
- if (c_p && c_p->id == pid) {
- ASSERT(c_p->id != ERTS_INVALID_PID);
- ASSERT(c_p == process_tab[pix]);
- if (!(flags & ERTS_P2P_FLG_ALLOW_OTHER_X) && c_p->is_exiting) {
- proc = NULL;
- goto done;
- }
- need_locks &= ~c_p_have_locks;
- if (!need_locks) {
- proc = c_p;
- erts_pix_lock(pix_lock);
- if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
- proc->lock.refc++;
- erts_pix_unlock(pix_lock);
- goto done;
- }
- }
-
- erts_pix_lock(pix_lock);
-
- proc = process_tab[pix];
- if (proc) {
- if (proc->id != pid || (!(flags & ERTS_P2P_FLG_ALLOW_OTHER_X)
- && ERTS_PROC_IS_EXITING(proc))) {
- proc = NULL;
- }
- else if (!need_locks) {
- if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
- proc->lock.refc++;
- }
- else {
- int busy;
-
-#ifdef ERTS_ENABLE_LOCK_COUNT
- lcnt_locks = need_locks;
- if (!(flags & ERTS_P2P_FLG_TRY_LOCK)) {
- erts_lcnt_proc_lock(&proc->lock, need_locks);
- }
-#endif
-
-#ifdef ERTS_ENABLE_LOCK_CHECK
- /* Make sure erts_pid2proc_safelock() is enough to handle
- a potential lock order violation situation... */
- busy = erts_proc_lc_trylock_force_busy(proc, need_locks);
- if (!busy)
-#endif
- {
- /* Try a quick trylock to grab all the locks we need. */
- busy = (int) erts_smp_proc_raw_trylock__(proc, need_locks);
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_proc_lc_trylock(proc, need_locks, !busy);
-#endif
-#ifdef ERTS_PROC_LOCK_DEBUG
- if (!busy)
- erts_proc_lock_op_debug(proc, need_locks, 1);
-#endif
- }
-
-#ifdef ERTS_ENABLE_LOCK_COUNT
- if (flags & ERTS_P2P_FLG_TRY_LOCK) {
- if (busy) {
- erts_lcnt_proc_trylock(&proc->lock, need_locks, EBUSY);
- } else {
- erts_lcnt_proc_trylock(&proc->lock, need_locks, 0);
- }
- }
-#endif
- if (!busy) {
- if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
- proc->lock.refc++;
-#ifdef ERTS_ENABLE_LOCK_COUNT
- /* all is great */
- if (!(flags & ERTS_P2P_FLG_TRY_LOCK)) {
- erts_lcnt_proc_lock_post_x(&proc->lock, lcnt_locks, __FILE__, __LINE__);
- }
-#endif
- }
- else {
- if (flags & ERTS_P2P_FLG_TRY_LOCK)
- proc = ERTS_PROC_LOCK_BUSY;
- else {
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_proc_lock_unaquire(&proc->lock, lcnt_locks);
-#endif
- erts_pid2proc_safelock(c_p,
- c_p_have_locks,
- &proc,
- pid_need_locks,
- pix_lock,
- flags);
- if (proc && (flags & ERTS_P2P_FLG_SMP_INC_REFC))
- proc->lock.refc++;
- }
- }
- }
- }
-
- erts_pix_unlock(pix_lock);
-#ifdef ERTS_PROC_LOCK_DEBUG
- ERTS_LC_ASSERT(!proc
- || proc == ERTS_PROC_LOCK_BUSY
- || (pid_need_locks ==
- (ERTS_PROC_LOCK_FLGS_READ_(&proc->lock)
- & pid_need_locks)));
-#endif
-
-
- done:
-
-#if ERTS_PROC_LOCK_ATOMIC_IMPL
- ETHR_COMPILER_BARRIER;
-#endif
-
- return proc;
+ Process *proc = erts_proc_lookup_raw(pid);
+ return ((!(flags & ERTS_P2P_FLG_ALLOW_OTHER_X)
+ && proc
+ && ERTS_PROC_IS_EXITING(proc))
+ ? NULL
+ : proc);
}
-#endif /* ERTS_SMP */
+#endif /* !ERTS_SMP */
#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.c b/erts/emulator/beam/erl_sched_spec_pre_alloc.c
index 37b186abd9..a490aec734 100644
--- a/erts/emulator/beam/erl_sched_spec_pre_alloc.c
+++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.c
@@ -116,54 +116,84 @@ erts_sspa_create(size_t blk_sz, int pa_size)
return data;
}
-static ERTS_INLINE erts_aint_t
+static ERTS_INLINE void
enqueue_remote_managed_thread(erts_sspa_chunk_header_t *chdr,
erts_sspa_blk_t *this,
- int want_last)
+ int cinit)
{
- erts_aint_t ilast, itmp;
+ erts_aint_t itmp;
+ erts_sspa_blk_t *enq;
erts_atomic_init_nob(&this->next_atmc, ERTS_AINT_NULL);
-
/* Enqueue at end of list... */
- ilast = erts_atomic_read_nob(&chdr->tail.data.last);
- while (1) {
- erts_sspa_blk_t *last = (erts_sspa_blk_t *) ilast;
- itmp = erts_atomic_cmpxchg_mb(&last->next_atmc,
- (erts_aint_t) this,
- ERTS_AINT_NULL);
- if (itmp == ERTS_AINT_NULL)
- break;
- ilast = itmp;
+ enq = (erts_sspa_blk_t *) erts_atomic_read_nob(&chdr->tail.data.last);
+ itmp = erts_atomic_cmpxchg_relb(&enq->next_atmc,
+ (erts_aint_t) this,
+ ERTS_AINT_NULL);
+ if (itmp == ERTS_AINT_NULL) {
+ /* We are required to move last pointer */
+#ifdef DEBUG
+ ASSERT(ERTS_AINT_NULL == erts_atomic_read_nob(&this->next_atmc));
+ ASSERT(((erts_aint_t) enq)
+ == erts_atomic_xchg_relb(&chdr->tail.data.last,
+ (erts_aint_t) this));
+#else
+ erts_atomic_set_relb(&chdr->tail.data.last, (erts_aint_t) this);
+#endif
}
+ else {
+ /*
+ * We *need* to insert element somewhere in between the
+ * last element we read earlier and the actual last element.
+ */
+ int i = cinit;
- /* Move last pointer forward... */
- while (1) {
- erts_aint_t itmp;
- if (want_last) {
- if (erts_atomic_read_rb(&this->next_atmc) != ERTS_AINT_NULL) {
- /* Someone else will move it forward */
- return erts_atomic_read_nob(&chdr->tail.data.last);
+ while (1) {
+ erts_aint_t itmp2;
+ erts_atomic_set_nob(&this->next_atmc, itmp);
+ itmp2 = erts_atomic_cmpxchg_relb(&enq->next_atmc,
+ (erts_aint_t) this,
+ itmp);
+ if (itmp == itmp2)
+ break; /* inserted this */
+ if ((i & 1) == 0)
+ itmp = itmp2;
+ else {
+ enq = (erts_sspa_blk_t *) itmp;
+ itmp = erts_atomic_read_acqb(&enq->next_atmc);
+ ASSERT(itmp != ERTS_AINT_NULL);
}
+ i++;
}
- else {
- if (erts_atomic_read_nob(&this->next_atmc) != ERTS_AINT_NULL) {
- /* Someone else will move it forward */
- return ERTS_AINT_NULL;
- }
+ }
+}
+
+static ERTS_INLINE erts_aint_t
+check_insert_marker(erts_sspa_chunk_header_t *chdr, erts_aint_t ilast)
+{
+ if (!chdr->head.used_marker
+ && chdr->head.unref_end == (erts_sspa_blk_t *) ilast) {
+ erts_aint_t itmp;
+ erts_sspa_blk_t *last = (erts_sspa_blk_t *) ilast;
+
+ erts_atomic_init_nob(&chdr->tail.data.marker.next_atmc, ERTS_AINT_NULL);
+ itmp = erts_atomic_cmpxchg_relb(&last->next_atmc,
+ (erts_aint_t) &chdr->tail.data.marker,
+ ERTS_AINT_NULL);
+ if (itmp == ERTS_AINT_NULL) {
+ ilast = (erts_aint_t) &chdr->tail.data.marker;
+ chdr->head.used_marker = !0;
+ erts_atomic_set_relb(&chdr->tail.data.last, ilast);
}
- itmp = erts_atomic_cmpxchg_mb(&chdr->tail.data.last,
- (erts_aint_t) this,
- ilast);
- if (ilast == itmp)
- return want_last ? (erts_aint_t) this : ERTS_AINT_NULL;
- ilast = itmp;
}
+ return ilast;
}
void
-erts_sspa_remote_free(erts_sspa_chunk_header_t *chdr, erts_sspa_blk_t *blk)
+erts_sspa_remote_free(erts_sspa_chunk_header_t *chdr,
+ erts_sspa_blk_t *blk,
+ int cinit)
{
int um_refc_ix = 0;
int managed_thread = erts_thr_progress_is_managed_thread();
@@ -180,7 +210,7 @@ erts_sspa_remote_free(erts_sspa_chunk_header_t *chdr, erts_sspa_blk_t *blk)
}
}
- (void) enqueue_remote_managed_thread(chdr, blk, 0);
+ enqueue_remote_managed_thread(chdr, blk, cinit);
if (!managed_thread)
erts_atomic_dec_relb(&chdr->tail.data.um_refc[um_refc_ix]);
@@ -208,24 +238,17 @@ fetch_remote(erts_sspa_chunk_header_t *chdr, int max)
int um_refc_ix;
chdr->head.next.thr_progress_reached = 1;
um_refc_ix = chdr->head.next.um_refc_ix;
- if (erts_atomic_read_acqb(&chdr->tail.data.um_refc[um_refc_ix]) == 0) {
+ if (erts_atomic_read_nob(&chdr->tail.data.um_refc[um_refc_ix]) == 0) {
+
+ ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore);
/* Move unreferenced end pointer forward... */
chdr->head.unref_end = chdr->head.next.unref_end;
- if (!chdr->head.used_marker
- && chdr->head.unref_end == (erts_sspa_blk_t *) ilast) {
- /* Need to equeue marker */
- chdr->head.used_marker = 1;
- ilast = enqueue_remote_managed_thread(chdr,
- &chdr->tail.data.marker,
- 1);
- }
+ ilast = check_insert_marker(chdr, ilast);
- if (chdr->head.unref_end == (erts_sspa_blk_t *) ilast)
- ERTS_THR_MEMORY_BARRIER;
- else {
+ if (chdr->head.unref_end != (erts_sspa_blk_t *) ilast) {
chdr->head.next.unref_end = (erts_sspa_blk_t *) ilast;
chdr->head.next.thr_progress = erts_thr_progress_later(NULL);
erts_atomic32_set_relb(&chdr->tail.data.um_refc_ix,
diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.h b/erts/emulator/beam/erl_sched_spec_pre_alloc.h
index d36066c399..9144c73acd 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. All Rights Reserved.
+ * Copyright Ericsson AB 2011-2012. 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
@@ -142,7 +142,8 @@ check_local_list(erts_sspa_chunk_header_t *chdr)
erts_sspa_data_t *erts_sspa_create(size_t blk_sz,
int pa_size);
void erts_sspa_remote_free(erts_sspa_chunk_header_t *chdr,
- erts_sspa_blk_t *blk);
+ erts_sspa_blk_t *blk,
+ int cinit);
erts_sspa_blk_t *erts_sspa_process_remote_frees(erts_sspa_chunk_header_t *chdr,
erts_sspa_blk_t *old_res);
@@ -216,7 +217,7 @@ erts_sspa_free(erts_sspa_data_t *data, int cix, char *cblk)
chdr = &chnk->aligned.header;
if (chnk_cix != cix) {
/* Remote chunk */
- erts_sspa_remote_free(chdr, blk);
+ erts_sspa_remote_free(chdr, blk, chnk_cix - cix);
}
else {
/* Local chunk */
diff --git a/erts/emulator/beam/erl_term.c b/erts/emulator/beam/erl_term.c
index f77e8b798f..bf7774f882 100644
--- a/erts/emulator/beam/erl_term.c
+++ b/erts/emulator/beam/erl_term.c
@@ -105,7 +105,7 @@ unsigned tag_val_def(Wterm x)
break;
}
}
- sprintf(msg, "tag_val_def: %#lx", (unsigned long) x);
+ erts_snprintf(msg, sizeof(msg), "tag_val_def: %#lx", (unsigned long) x);
et_abort(msg, file, line);
#undef file
#undef line
diff --git a/erts/emulator/beam/erl_thr_progress.h b/erts/emulator/beam/erl_thr_progress.h
index 89486b065b..e72321cf48 100644
--- a/erts/emulator/beam/erl_thr_progress.h
+++ b/erts/emulator/beam/erl_thr_progress.h
@@ -78,6 +78,16 @@ void erts_thr_progress_fatal_error_block(SWord timeout,
#endif /* ERTS_SMP */
+typedef struct ErtsThrPrgrLaterOp_ ErtsThrPrgrLaterOp;
+struct ErtsThrPrgrLaterOp_ {
+#ifdef ERTS_SMP
+ ErtsThrPrgrVal later;
+#endif
+ void (*func)(void *);
+ void *data;
+ ErtsThrPrgrLaterOp *next;
+};
+
#endif
#if !defined(ERL_THR_PROGRESS_H__) && !defined(ERL_THR_PROGRESS_TSD_TYPE_ONLY)
diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h
index ee47c98009..17628286bc 100644
--- a/erts/emulator/beam/erl_threads.h
+++ b/erts/emulator/beam/erl_threads.h
@@ -258,10 +258,6 @@
#include "sys.h"
-typedef struct { SWord sint[2]; } erts_no_dw_atomic_t;
-typedef SWord erts_no_atomic_t;
-typedef Sint32 erts_no_atomic32_t;
-
#ifdef USE_THREADS
#define ETHR_TRY_INLINE_FUNCS
@@ -411,12 +407,15 @@ typedef struct {
typedef int erts_rwmtx_t;
typedef int erts_tsd_key_t;
typedef int erts_tse_t;
-#define erts_dw_aint_t erts_no_dw_atomic_t
-#define erts_dw_atomic_t erts_no_dw_atomic_t
-#define erts_aint_t SWord
-#define erts_atomic_t erts_no_atomic_t
-#define erts_aint32_t Sint32
-#define erts_atomic32_t erts_no_atomic32_t
+
+typedef struct { SWord sint[2]; } erts_dw_aint_t;
+typedef SWord erts_aint_t;
+typedef Sint32 erts_aint32_t;
+
+#define erts_dw_atomic_t erts_dw_aint_t
+#define erts_atomic_t erts_aint_t
+#define erts_atomic32_t erts_aint32_t
+
#if __GNUC__ > 2
typedef struct { } erts_spinlock_t;
typedef struct { } erts_rwlock_t;
@@ -425,6 +424,14 @@ typedef struct { int gcc_is_buggy; } erts_spinlock_t;
typedef struct { int gcc_is_buggy; } erts_rwlock_t;
#endif
+#ifdef WORDS_BIGENDIAN
+#define ERTS_DW_AINT_LOW_WORD 1
+#define ERTS_DW_AINT_HIGH_WORD 0
+#else
+#define ERTS_DW_AINT_LOW_WORD 0
+#define ERTS_DW_AINT_HIGH_WORD 1
+#endif
+
#define ERTS_MTX_INITER 0
#define ERTS_CND_INITER 0
#define ERTS_THR_INIT_DATA_DEF_INITER 0
@@ -433,6 +440,10 @@ typedef struct { int gcc_is_buggy; } erts_rwlock_t;
#endif /* #ifdef USE_THREADS */
+#define erts_no_dw_atomic_t erts_dw_aint_t
+#define erts_no_atomic_t erts_aint_t
+#define erts_no_atomic32_t erts_aint32_t
+
#define ERTS_AINT_NULL ((erts_aint_t) NULL)
#define ERTS_AINT_T_MAX (~(((erts_aint_t) 1) << (sizeof(erts_aint_t)*8-1)))
diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h
index 6c6e193818..4bbdcaa3e3 100644
--- a/erts/emulator/beam/erl_time.h
+++ b/erts/emulator/beam/erl_time.h
@@ -118,9 +118,11 @@ ERTS_GLB_INLINE void erts_do_time_add(erts_short_time_t elapsed)
void erts_get_now_cpu(Uint* megasec, Uint* sec, Uint* microsec);
#endif
+typedef UWord erts_approx_time_t;
+erts_approx_time_t erts_get_approx_time(void);
+
void erts_get_timeval(SysTimeval *tv);
erts_time_t erts_get_time(void);
-void erts_get_emu_time(SysTimeval *);
ERTS_GLB_INLINE int erts_cmp_timeval(SysTimeval *t1p, SysTimeval *t2p);
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c
index 1d0735aa99..3272a5326d 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-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2012. 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
@@ -91,6 +91,41 @@ static SysTimeval then; /* Used in get_now */
static SysTimeval last_emu_time; /* Used in erts_get_emu_time() */
SysTimeval erts_first_emu_time; /* Used in erts_get_emu_time() */
+union {
+ erts_smp_atomic_t time;
+ char align[ERTS_CACHE_LINE_SIZE];
+} approx erts_align_attribute(ERTS_CACHE_LINE_SIZE);
+
+static void
+init_approx_time(void)
+{
+ erts_smp_atomic_init_nob(&approx.time, 0);
+}
+
+static ERTS_INLINE erts_approx_time_t
+get_approx_time(void)
+{
+ return (erts_approx_time_t) erts_smp_atomic_read_nob(&approx.time);
+}
+
+static ERTS_INLINE void
+update_approx_time(SysTimeval *tv)
+{
+ erts_approx_time_t new_secs = (erts_approx_time_t) tv->tv_sec;
+ erts_approx_time_t old_secs = get_approx_time();
+ if (old_secs != new_secs)
+ erts_smp_atomic_set_nob(&approx.time, new_secs);
+}
+
+/*
+ * erts_get_approx_time() returns an *approximate* time
+ * in seconds. NOTE that this time may jump backwards!!!
+ */
+erts_approx_time_t
+erts_get_approx_time(void)
+{
+ return get_approx_time();
+}
#ifdef HAVE_GETHRTIME
@@ -351,7 +386,7 @@ static int clock_resolution;
/*
** The clock resolution should really be the resolution of the
** time function in use, which on most platforms
-** is 1. On VxWorks the resolution shold be
+** is 1. On VxWorks the resolution should be
** the number of ticks per second (or 1, which would work nicely to).
**
** Setting lower resolutions is mostly interesting when timers are used
@@ -398,6 +433,8 @@ erts_init_time_sup(void)
{
erts_smp_mtx_init(&erts_timeofday_mtx, "timeofday");
+ init_approx_time();
+
last_emu_time.tv_sec = 0;
last_emu_time.tv_usec = 0;
@@ -417,7 +454,7 @@ erts_init_time_sup(void)
gtv = inittv;
then.tv_sec = then.tv_usec = 0;
- erts_get_emu_time(&erts_first_emu_time);
+ erts_deliver_time();
return CLOCK_RESOLUTION;
}
@@ -717,6 +754,11 @@ int univ_to_seconds(Sint year, Sint month, Sint day, Sint hour, Sint minute, Sin
return 1;
}
+#if defined(HAVE_TIME2POSIX) && defined(HAVE_DECL_TIME2POSIX) && \
+ !HAVE_DECL_TIME2POSIX
+extern time_t time2posix(time_t);
+#endif
+
int
local_to_univ(Sint *year, Sint *month, Sint *day,
Sint *hour, Sint *minute, Sint *second, int isdst)
@@ -757,7 +799,7 @@ local_to_univ(Sint *year, Sint *month, Sint *day,
refuses to give us a DST time, we simulate the Linux/Solaris
behaviour of giving the same data as if is_dst was not set. */
t.tm_isdst = 0;
- if (erl_mktime(&the_clock, &t)) {
+ if (erl_mktime(&the_clock, &t) < 0) {
/* Failed anyway, something else is bad - will be a badarg */
return 0;
}
@@ -766,6 +808,11 @@ local_to_univ(Sint *year, Sint *month, Sint *day,
return 0;
}
}
+
+#ifdef HAVE_TIME2POSIX
+ the_clock = time2posix(the_clock);
+#endif
+
#ifdef HAVE_GMTIME_R
tm = gmtime_r(&the_clock, &tmbuf);
#else
@@ -873,6 +920,8 @@ get_now(Uint* megasec, Uint* sec, Uint* microsec)
*megasec = (Uint) (now.tv_sec / 1000000);
*sec = (Uint) (now.tv_sec % 1000000);
*microsec = (Uint) (now.tv_usec);
+
+ update_approx_time(&now);
}
void
@@ -885,6 +934,8 @@ get_sys_now(Uint* megasec, Uint* sec, Uint* microsec)
*megasec = (Uint) (now.tv_sec / 1000000);
*sec = (Uint) (now.tv_sec % 1000000);
*microsec = (Uint) (now.tv_usec);
+
+ update_approx_time(&now);
}
@@ -901,6 +952,8 @@ void erts_deliver_time(void) {
do_erts_deliver_time(&now);
erts_smp_mtx_unlock(&erts_timeofday_mtx);
+
+ update_approx_time(&now);
}
/* get *real* time (not ticks) remaining until next timeout - if there
@@ -949,6 +1002,7 @@ void erts_get_timeval(SysTimeval *tv)
erts_smp_mtx_lock(&erts_timeofday_mtx);
get_tolerant_timeofday(tv);
erts_smp_mtx_unlock(&erts_timeofday_mtx);
+ update_approx_time(tv);
}
erts_time_t
@@ -961,7 +1015,9 @@ erts_get_time(void)
get_tolerant_timeofday(&sys_tv);
erts_smp_mtx_unlock(&erts_timeofday_mtx);
-
+
+ update_approx_time(&sys_tv);
+
return sys_tv.tv_sec;
}
@@ -977,38 +1033,3 @@ void erts_get_now_cpu(Uint* megasec, Uint* sec, Uint* microsec) {
*sec = (Uint)(tp.tv_sec % 1000000);
}
#endif
-
-
-/*
- * erts_get_emu_time() is similar to get_now(). You will
- * always get different times from erts_get_emu_time(), but they
- * may equal a time from get_now().
- *
- * erts_get_emu_time() is only used internally in the emulator in
- * order to order emulator internal events.
- */
-
-void
-erts_get_emu_time(SysTimeval *this_emu_time_p)
-{
- erts_smp_mtx_lock(&erts_timeofday_mtx);
-
- get_tolerant_timeofday(this_emu_time_p);
-
- /* Make sure time is later than last */
- if (last_emu_time.tv_sec > this_emu_time_p->tv_sec ||
- (last_emu_time.tv_sec == this_emu_time_p->tv_sec
- && last_emu_time.tv_usec >= this_emu_time_p->tv_usec)) {
- *this_emu_time_p = last_emu_time;
- this_emu_time_p->tv_usec++;
- }
- /* Check for carry from above + general reasonability */
- if (this_emu_time_p->tv_usec >= 1000000) {
- this_emu_time_p->tv_usec = 0;
- this_emu_time_p->tv_sec++;
- }
-
- last_emu_time = *this_emu_time_p;
-
- erts_smp_mtx_unlock(&erts_timeofday_mtx);
-}
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index 009ca1eb52..d04a91f18c 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -64,7 +64,7 @@ int erts_cpu_timestamp;
#endif
static erts_smp_mtx_t smq_mtx;
-static erts_smp_mtx_t sys_trace_mtx;
+static erts_smp_rwmtx_t sys_trace_rwmtx;
enum ErtsSysMsgType {
SYS_MSG_TYPE_UNDEFINED,
@@ -91,7 +91,12 @@ static void init_sys_msg_dispatcher(void);
#endif
void erts_init_trace(void) {
- erts_smp_mtx_init(&sys_trace_mtx, "sys_tracers");
+ erts_smp_rwmtx_opt_t rwmtx_opts = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER;
+ rwmtx_opts.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ;
+ rwmtx_opts.lived = ERTS_SMP_RWMTX_LONG_LIVED;
+
+ erts_smp_rwmtx_init_opt(&sys_trace_rwmtx, &rwmtx_opts, "sys_tracers");
+
#ifdef HAVE_ERTS_NOW_CPU
erts_cpu_timestamp = 0;
#endif
@@ -151,8 +156,8 @@ do { (RES) = (TPID); } while(0)
#define ERTS_TRACER_REF_TYPE Process *
#define ERTS_GET_TRACER_REF(RES, TPID, TRACEE_FLGS) \
do { \
- (RES) = process_tab[internal_pid_index((TPID))]; \
- if (INVALID_PID((RES), (TPID)) || !((RES)->trace_flags & F_TRACER)) { \
+ (RES) = erts_proc_lookup((TPID)); \
+ if (!(RES) || !((RES)->trace_flags & F_TRACER)) { \
(TPID) = NIL; \
(TRACEE_FLGS) &= ~TRACEE_FLAGS; \
return; \
@@ -169,10 +174,10 @@ erts_system_profile_setup_active_schedulers(void)
active_sched = erts_active_schedulers();
}
-void
-erts_trace_check_exiting(Eterm exiting)
+static void
+exiting_reset(Eterm exiting)
{
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
if (exiting == default_tracer) {
default_tracer = NIL;
default_trace_flags &= TRACEE_FLAGS;
@@ -202,29 +207,49 @@ erts_trace_check_exiting(Eterm exiting)
erts_system_profile_clear(NULL);
#endif
}
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx);
+}
+
+void
+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)
+ reset = 1;
+ else if (exiting == system_profile)
+ reset = 1;
+ erts_smp_rwmtx_runlock(&sys_trace_rwmtx);
+ if (reset)
+ 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)
{
- Eterm old = THE_NON_VALUE;
+ Eterm old;
- if (new != am_false) {
- if (!erts_pid2proc(c_p, c_p_locks, new, 0)
- && !erts_is_valid_tracer_port(new)) {
- return old;
- }
- }
+ if (new != am_false && !is_valid_tracer(new))
+ return THE_NON_VALUE;
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
old = system_seq_tracer;
system_seq_tracer = new;
#ifdef DEBUG_PRINTOUTS
erts_fprintf(stderr, "set seq tracer new=%T old=%T\n", new, old);
#endif
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx);
return old;
}
@@ -232,12 +257,12 @@ Eterm
erts_get_system_seq_tracer(void)
{
Eterm st;
- erts_smp_mtx_lock(&sys_trace_mtx);
+ 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_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_runlock(&sys_trace_rwmtx);
return st;
}
@@ -250,7 +275,7 @@ get_default_tracing(Uint *flagsp, Eterm *tracerp)
if (is_nil(default_tracer)) {
default_trace_flags &= ~TRACEE_FLAGS;
} else if (is_internal_pid(default_tracer)) {
- if (!erts_pid2proc(NULL, 0, default_tracer, 0)) {
+ if (!erts_proc_lookup(default_tracer)) {
reset_tracer:
default_trace_flags &= ~TRACEE_FLAGS;
default_tracer = NIL;
@@ -270,7 +295,7 @@ get_default_tracing(Uint *flagsp, Eterm *tracerp)
void
erts_change_default_tracing(int setflags, Uint *flagsp, Eterm *tracerp)
{
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
if (flagsp) {
if (setflags)
default_trace_flags |= *flagsp;
@@ -280,48 +305,48 @@ erts_change_default_tracing(int setflags, Uint *flagsp, Eterm *tracerp)
if (tracerp)
default_tracer = *tracerp;
get_default_tracing(flagsp, tracerp);
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx);
}
void
erts_get_default_tracing(Uint *flagsp, Eterm *tracerp)
{
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rlock(&sys_trace_rwmtx);
get_default_tracing(flagsp, tracerp);
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_runlock(&sys_trace_rwmtx);
}
void
erts_set_system_monitor(Eterm monitor)
{
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
system_monitor = monitor;
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx);
}
Eterm
erts_get_system_monitor(void)
{
Eterm monitor;
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rlock(&sys_trace_rwmtx);
monitor = system_monitor;
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_runlock(&sys_trace_rwmtx);
return monitor;
}
/* Performance monitoring */
void erts_set_system_profile(Eterm profile) {
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
system_profile = profile;
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx);
}
Eterm
erts_get_system_profile(void) {
Eterm profile;
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rlock(&sys_trace_rwmtx);
profile = system_profile;
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_runlock(&sys_trace_rwmtx);
return profile;
}
@@ -384,13 +409,9 @@ WRITE_SYS_MSG_TO_PORT(Eterm unused_to,
}
#ifndef ERTS_SMP
- if (!INVALID_TRACER_PORT(trace_port, trace_port->id)) {
+ if (!INVALID_TRACER_PORT(trace_port, trace_port->id))
#endif
erts_raw_port_command(trace_port, buffer, ptr-buffer);
-#ifndef ERTS_SMP
- erts_port_release(trace_port);
- }
-#endif
erts_free(ERTS_ALC_T_TMP, (void *) buffer);
}
@@ -465,13 +486,13 @@ send_to_port(Process *c_p, Eterm message,
trace_port = NULL;
#else
- if (is_not_internal_port(*tracer_pid))
- goto invalid_tracer_port;
- trace_port = &erts_port[internal_port_index(*tracer_pid)];
+ trace_port = erts_id2port_sflgs(*tracer_pid,
+ NULL,
+ 0,
+ ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
- if (INVALID_TRACER_PORT(trace_port, *tracer_pid)) {
- invalid_tracer_port:
+ if (!trace_port) {
*tracee_flags &= ~TRACEE_FLAGS;
*tracer_pid = NIL;
return;
@@ -491,6 +512,7 @@ send_to_port(Process *c_p, Eterm message,
SYS_MSG_TYPE_TRACE,
message);
#ifndef ERTS_SMP
+ erts_port_release(trace_port);
return;
}
@@ -537,6 +559,9 @@ send_to_port(Process *c_p, Eterm message,
*/
do_send_schedfix_to_port(trace_port, c_p->id, ts);
}
+
+ erts_port_release(trace_port);
+
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
#undef LOCAL_HEAP_SIZE
#endif
@@ -566,23 +591,28 @@ profile_send(Eterm from, Eterm message) {
Port *profiler_port = NULL;
/* not smp */
-
-
- profiler_port = &erts_port[internal_port_index(profiler)];
-
- do_send_to_port(profiler,
- profiler_port,
- NIL, /* or current process->id */
- SYS_MSG_TYPE_SYSPROF,
- message);
+
+ profiler_port = erts_id2port_sflgs(profiler,
+ NULL,
+ 0,
+ ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
+ if (profiler_port) {
+ do_send_to_port(profiler,
+ profiler_port,
+ NIL, /* or current process->id */
+ SYS_MSG_TYPE_SYSPROF,
+ message);
+ erts_port_release(profiler_port);
+ }
} else {
ASSERT(is_internal_pid(profiler)
&& internal_pid_index(profiler) < erts_max_processes);
- profile_p = process_tab[internal_pid_index(profiler)];
+ profile_p = erts_proc_lookup(profiler);
- if (INVALID_PID(profile_p, profiler)) return;
+ if (!profile_p)
+ return;
sz = size_object(message);
hp = erts_alloc_message_heap(sz, &bp, &off_heap, profile_p, 0);
@@ -626,13 +656,11 @@ seq_trace_send_to_port(Process *c_p,
trace_port = NULL;
#else
- if (is_not_internal_port(seq_tracer))
- goto invalid_tracer_port;
-
- trace_port = &erts_port[internal_port_index(seq_tracer)];
-
- if (INVALID_TRACER_PORT(trace_port, seq_tracer)) {
- invalid_tracer_port:
+ 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);
@@ -650,6 +678,7 @@ seq_trace_send_to_port(Process *c_p,
message);
#ifndef ERTS_SMP
+ erts_port_release(trace_port);
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
return;
}
@@ -691,6 +720,9 @@ seq_trace_send_to_port(Process *c_p,
*/
do_send_schedfix_to_port(trace_port, c_p->id, ts);
}
+
+ erts_port_release(trace_port);
+
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
#undef LOCAL_HEAP_SIZE
#endif
@@ -789,13 +821,8 @@ trace_sched_aux(Process *p, Eterm what, int never_fake_sched)
ERTS_GET_TRACER_REF(tracer_ref, p->tracer_proc, p->trace_flags);
}
- if (ERTS_PROC_IS_EXITING(p)
-#ifndef ERTS_SMP
- || p->status == P_FREE
-#endif
- ) {
+ if (ERTS_PROC_IS_EXITING(p))
curr_func = 0;
- }
else {
if (!p->current)
p->current = find_function_from_pc(p->i);
@@ -874,7 +901,7 @@ trace_send(Process *p, Eterm to, Eterm msg)
operation = am_send;
if (is_internal_pid(to)) {
- if (!erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, to, 0))
+ if (!erts_proc_lookup(to))
goto send_to_non_existing_process;
}
else if(is_external_pid(to)
@@ -1116,8 +1143,8 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type,
#ifndef ERTS_SMP
- tracer = process_tab[internal_pid_index(seq_tracer)];
- if (INVALID_PID(tracer, tracer->id)) {
+ tracer = erts_proc_lookup(seq_tracer);
+ if (!tracer) {
system_seq_tracer = am_false;
return; /* no need to send anything */
}
@@ -2107,187 +2134,6 @@ void save_calls(Process *p, Export *e)
}
}
-/*
- * Entry point called by the trace wrap functions in erl_bif_wrap.c
- *
- * The trace wrap functions are themselves called through the export
- * entries instead of the original BIF functions.
- */
-Eterm
-erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
-{
- Eterm result;
- int meta = !!(erts_bif_trace_flags[bif_index] & BIF_TRACE_AS_META);
-
- ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
-
- if (!ARE_TRACE_FLAGS_ON(p, F_TRACE_CALLS) && (! meta)) {
- /* Warning! This is an Optimization.
- *
- * If neither meta trace is active nor process trace flags then
- * no tracing will occur. Doing the whole else branch will
- * also do nothing, only slower.
- */
- Eterm (*func)(Process*, Eterm*, BeamInstr*) = bif_table[bif_index].f;
- result = func(p, args, I);
- } else {
- Eterm (*func)(Process*, Eterm*, BeamInstr*);
- Export* ep = bif_export[bif_index];
- Uint32 flags = 0, flags_meta = 0;
- int global = !!(erts_bif_trace_flags[bif_index] & BIF_TRACE_AS_GLOBAL);
- int local = !!(erts_bif_trace_flags[bif_index] & BIF_TRACE_AS_LOCAL);
- int time = !!(erts_bif_trace_flags[bif_index] & BIF_TRACE_AS_CALL_TIME);
- Eterm meta_tracer_pid = NIL;
- int applying = (I == &(ep->code[3])); /* Yup, the apply code for a bif
- * is actually in the
- * export entry */
- BeamInstr *cp = p->cp;
-
- /*
- * Make continuation pointer OK, it is not during direct BIF calls,
- * but it is correct during apply of bif.
- */
- if (!applying) {
- p->cp = I;
- }
- if (global || local) {
- flags = erts_call_trace(p, ep->code, ep->match_prog_set, args,
- local, &p->tracer_proc);
- }
- if (meta) {
- flags_meta = erts_bif_mtrace(p, ep->code+3, args, local,
- &meta_tracer_pid);
- }
- if (time) {
- BpDataTime *bdt = NULL;
- BeamInstr *pc = (BeamInstr *)ep->code+3;
-
- bdt = (BpDataTime *) erts_get_time_break(p, pc);
- ASSERT(bdt);
-
- if (!bdt->pause) {
- erts_trace_time_break(p, pc, bdt, ERTS_BP_CALL_TIME_CALL);
- }
- }
- /* Restore original continuation pointer (if changed). */
- p->cp = cp;
-
- func = bif_table[bif_index].f;
-
- result = func(p, args, I);
-
- if (applying && (flags & MATCH_SET_RETURN_TO_TRACE)) {
- BeamInstr i_return_trace = beam_return_trace[0];
- BeamInstr i_return_to_trace = beam_return_to_trace[0];
- BeamInstr i_return_time_trace = beam_return_time_trace[0];
- Eterm *cpp;
- /* Maybe advance cp to skip trace stack frames */
- for (cpp = p->stop; ; cp = cp_val(*cpp++)) {
- if (*cp == i_return_trace) {
- /* Skip stack frame variables */
- while (is_not_CP(*cpp)) cpp++;
- cpp += 2; /* Skip return_trace parameters */
- } else if (*cp == i_return_time_trace) {
- /* Skip stack frame variables */
- while (is_not_CP(*cpp)) cpp++;
- cpp += 1; /* Skip return_time_trace parameters */
- } else if (*cp == i_return_to_trace) {
- /* A return_to trace message is going to be generated
- * by normal means, so we do not have to.
- */
- cp = NULL;
- break;
- } else break;
- }
- }
-
- /* Try to get these in the order
- * they usually appear in normal code... */
- if (is_non_value(result)) {
- Uint reason = p->freason;
- if (reason != TRAP) {
- Eterm class;
- Eterm value = p->fvalue;
- DeclareTmpHeapNoproc(nocatch,3);
- UseTmpHeapNoproc(3);
- /* Expand error value like in handle_error() */
- if (reason & EXF_ARGLIST) {
- Eterm *tp;
- ASSERT(is_tuple(value));
- tp = tuple_val(value);
- value = tp[1];
- }
- if ((reason & EXF_THROWN) && (p->catches <= 0)) {
- value = TUPLE2(nocatch, am_nocatch, value);
- reason = EXC_ERROR;
- }
- /* Note: expand_error_value() could theoretically
- * allocate on the heap, but not for any error
- * returned by a BIF, and it would do no harm,
- * just be annoying.
- */
- value = expand_error_value(p, reason, value);
- class = exception_tag[GET_EXC_CLASS(reason)];
-
- if (flags_meta & MATCH_SET_EXCEPTION_TRACE) {
- erts_trace_exception(p, ep->code, class, value,
- &meta_tracer_pid);
- }
- if (flags & MATCH_SET_EXCEPTION_TRACE) {
- erts_trace_exception(p, ep->code, class, value,
- &p->tracer_proc);
- }
- if ((flags & MATCH_SET_RETURN_TO_TRACE) && p->catches > 0) {
- /* can only happen if(local)*/
- Eterm *ptr = p->stop;
- ASSERT(is_CP(*ptr));
- ASSERT(ptr <= STACK_START(p));
- /* Search the nearest stack frame for a catch */
- while (++ptr < STACK_START(p)) {
- if (is_CP(*ptr)) break;
- if (is_catch(*ptr)) {
- if (applying) {
- /* Apply of BIF, cp is in calling function */
- if (cp) erts_trace_return_to(p, cp);
- } else {
- /* Direct bif call, I points into
- * calling function */
- erts_trace_return_to(p, I);
- }
- }
- }
- }
- UnUseTmpHeapNoproc(3);
- if ((flags_meta|flags) & MATCH_SET_EXCEPTION_TRACE) {
- erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- p->trace_flags |= F_EXCEPTION_TRACE;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- }
- } else {
- if (flags_meta & MATCH_SET_RX_TRACE) {
- erts_trace_return(p, ep->code, result, &meta_tracer_pid);
- }
- /* MATCH_SET_RETURN_TO_TRACE cannot occur if(meta) */
- if (flags & MATCH_SET_RX_TRACE) {
- erts_trace_return(p, ep->code, result, &p->tracer_proc);
- }
- if (flags & MATCH_SET_RETURN_TO_TRACE) {
- /* can only happen if(local)*/
- if (applying) {
- /* Apply of BIF, cp is in calling function */
- if (cp) erts_trace_return_to(p, cp);
- } else {
- /* Direct bif call, I points into calling function */
- erts_trace_return_to(p, I);
- }
- }
- }
- }
- ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
- return result;
-}
-
/* Sends trace message:
* {trace_ts, Pid, What, Msg, Timestamp}
* or {trace, Pid, What, Msg}
@@ -2451,10 +2297,9 @@ monitor_long_gc(Process *p, Uint time) {
#ifndef ERTS_SMP
ASSERT(is_internal_pid(system_monitor)
&& internal_pid_index(system_monitor) < erts_max_processes);
- monitor_p = process_tab[internal_pid_index(system_monitor)];
- if (INVALID_PID(monitor_p, system_monitor) || p == monitor_p) {
+ monitor_p = erts_proc_lookup(system_monitor);
+ if (!monitor_p || p == monitor_p)
return;
- }
#endif
hsz = 0;
@@ -2527,8 +2372,8 @@ monitor_large_heap(Process *p) {
#ifndef ERTS_SMP
ASSERT(is_internal_pid(system_monitor)
&& internal_pid_index(system_monitor) < erts_max_processes);
- monitor_p = process_tab[internal_pid_index(system_monitor)];
- if (INVALID_PID(monitor_p, system_monitor) || p == monitor_p) {
+ monitor_p = erts_proc_lookup(system_monitor);
+ if (monitor_p || p == monitor_p) {
return;
}
#endif
@@ -2582,10 +2427,9 @@ monitor_generic(Process *p, Eterm type, Eterm spec) {
#ifndef ERTS_SMP
ASSERT(is_internal_pid(system_monitor)
&& internal_pid_index(system_monitor) < erts_max_processes);
- monitor_p = process_tab[internal_pid_index(system_monitor)];
- if (INVALID_PID(monitor_p, system_monitor) || p == monitor_p) {
+ monitor_p = erts_proc_lookup(system_monitor);
+ if (!monitor_p || p == monitor_p)
return;
- }
#endif
hp = ERTS_ALLOC_SYSMSG_HEAP(5, &bp, &off_heap, monitor_p);
@@ -3149,10 +2993,10 @@ sys_msg_disp_failure(ErtsSysMsgQ *smqp, Eterm receiver)
break;
case SYS_MSG_TYPE_SEQTRACE:
/* Reset seq_tracer if it hasn't changed */
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
if (system_seq_tracer == receiver)
system_seq_tracer = am_false;
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx);
break;
case SYS_MSG_TYPE_SYSMON:
if (receiver == NIL
@@ -3407,12 +3251,12 @@ sys_msg_dispatcher_func(void *unused)
goto queue_proc_msg;
}
else if (is_internal_port(receiver)) {
- port = erts_id2port(receiver, NULL, 0);
- if (INVALID_TRACER_PORT(port, receiver)) {
- if (port)
- erts_port_release(port);
+ port = erts_id2port_sflgs(receiver,
+ NULL,
+ 0,
+ ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
+ if (!port)
goto failure;
- }
else {
write_sys_msg_to_port(receiver,
port,
diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c
index 6d5eae73b0..51559aea1c 100644
--- a/erts/emulator/beam/erl_unicode.c
+++ b/erts/emulator/beam/erl_unicode.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2012. 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
@@ -77,66 +77,25 @@ void erts_init_unicode(void)
{
max_loop_limit = CONTEXT_REDS * LOOP_FACTOR;
/* Non visual BIFs to trap to. */
- memset(&characters_to_utf8_trap_exp, 0, sizeof(Export));
- characters_to_utf8_trap_exp.address =
- &characters_to_utf8_trap_exp.code[3];
- characters_to_utf8_trap_exp.code[0] = am_erlang;
- characters_to_utf8_trap_exp.code[1] =
- am_atom_put("characters_to_utf8_trap",23);
- characters_to_utf8_trap_exp.code[2] = 3;
- characters_to_utf8_trap_exp.code[3] =
- (BeamInstr) em_apply_bif;
- characters_to_utf8_trap_exp.code[4] =
- (BeamInstr) &characters_to_utf8_trap;
-
- memset(&characters_to_list_trap_1_exp, 0, sizeof(Export));
- characters_to_list_trap_1_exp.address =
- &characters_to_list_trap_1_exp.code[3];
- characters_to_list_trap_1_exp.code[0] = am_erlang;
- characters_to_list_trap_1_exp.code[1] =
- am_atom_put("characters_to_list_trap_1",25);
- characters_to_list_trap_1_exp.code[2] = 3;
- characters_to_list_trap_1_exp.code[3] =
- (BeamInstr) em_apply_bif;
- characters_to_list_trap_1_exp.code[4] =
- (BeamInstr) &characters_to_list_trap_1;
-
- memset(&characters_to_list_trap_2_exp, 0, sizeof(Export));
- characters_to_list_trap_2_exp.address =
- &characters_to_list_trap_2_exp.code[3];
- characters_to_list_trap_2_exp.code[0] = am_erlang;
- characters_to_list_trap_2_exp.code[1] =
- am_atom_put("characters_to_list_trap_2",25);
- characters_to_list_trap_2_exp.code[2] = 3;
- characters_to_list_trap_2_exp.code[3] =
- (BeamInstr) em_apply_bif;
- characters_to_list_trap_2_exp.code[4] =
- (BeamInstr) &characters_to_list_trap_2;
-
-
- memset(&characters_to_list_trap_3_exp, 0, sizeof(Export));
- characters_to_list_trap_3_exp.address =
- &characters_to_list_trap_3_exp.code[3];
- characters_to_list_trap_3_exp.code[0] = am_erlang;
- characters_to_list_trap_3_exp.code[1] =
- am_atom_put("characters_to_list_trap_3",25);
- characters_to_list_trap_3_exp.code[2] = 3;
- characters_to_list_trap_3_exp.code[3] =
- (BeamInstr) em_apply_bif;
- characters_to_list_trap_3_exp.code[4] =
- (BeamInstr) &characters_to_list_trap_3;
-
- memset(&characters_to_list_trap_4_exp, 0, sizeof(Export));
- characters_to_list_trap_4_exp.address =
- &characters_to_list_trap_4_exp.code[3];
- characters_to_list_trap_4_exp.code[0] = am_erlang;
- characters_to_list_trap_4_exp.code[1] =
- am_atom_put("characters_to_list_trap_4",25);
- characters_to_list_trap_4_exp.code[2] = 1;
- characters_to_list_trap_4_exp.code[3] =
- (BeamInstr) em_apply_bif;
- characters_to_list_trap_4_exp.code[4] =
- (BeamInstr) &characters_to_list_trap_4;
+ erts_init_trap_export(&characters_to_utf8_trap_exp,
+ am_erlang, am_atom_put("characters_to_utf8_trap",23), 3,
+ &characters_to_utf8_trap);
+
+ erts_init_trap_export(&characters_to_list_trap_1_exp,
+ am_erlang, am_atom_put("characters_to_list_trap_1",25), 3,
+ &characters_to_list_trap_1);
+
+ erts_init_trap_export(&characters_to_list_trap_2_exp,
+ am_erlang, am_atom_put("characters_to_list_trap_2",25), 3,
+ &characters_to_list_trap_2);
+
+ erts_init_trap_export(&characters_to_list_trap_3_exp,
+ am_erlang, am_atom_put("characters_to_list_trap_3",25), 3,
+ &characters_to_list_trap_3);
+
+ erts_init_trap_export(&characters_to_list_trap_4_exp,
+ am_erlang, am_atom_put("characters_to_list_trap_4",25), 1,
+ &characters_to_list_trap_4);
c_to_b_int_trap_exportp = erts_export_put(am_unicode,am_characters_to_binary_int,2);
c_to_l_int_trap_exportp = erts_export_put(am_unicode,am_characters_to_list_int,2);
@@ -2027,12 +1986,14 @@ BIF_RETTYPE binary_to_existing_atom_2(BIF_ALIST_2)
* string routines, that will certainly fail on some OS.
*/
-char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int allow_empty)
+char *erts_convert_filename_to_native(Eterm name, char *statbuf, size_t statbuf_size, ErtsAlcType_t alloc_type, int allow_empty, int allow_atom, Sint *used)
{
int encoding = erts_get_native_filename_encoding();
char* name_buf = NULL;
- if (is_atom(name) || is_list(name) || (allow_empty && is_nil(name))) {
+ if ((allow_atom && is_atom(name)) ||
+ is_list(name) ||
+ (allow_empty && is_nil(name))) {
Sint need;
if ((need = erts_native_filename_need(name,encoding)) < 0) {
return NULL;
@@ -2042,7 +2003,13 @@ char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int
} else {
++need;
}
- name_buf = (char *) erts_alloc(alloc_type, need);
+ if (used)
+ *used = (Sint) need;
+ if (need > statbuf_size) {
+ name_buf = (char *) erts_alloc(alloc_type, need);
+ } else {
+ name_buf = statbuf;
+ }
erts_native_filename_put(name,encoding,(byte *)name_buf);
name_buf[need-1] = 0;
if (encoding == ERL_FILENAME_WIN_WCHAR) {
@@ -2058,14 +2025,26 @@ char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int
bytes = erts_get_aligned_binary_bytes(name, &temp_alloc);
if (encoding != ERL_FILENAME_WIN_WCHAR) {
/*Add 0 termination only*/
- name_buf = (char *) erts_alloc(alloc_type, size+1);
+ if (used)
+ *used = (Sint) size+1;
+ if (size+1 > statbuf_size) {
+ name_buf = (char *) erts_alloc(alloc_type, size+1);
+ } else {
+ name_buf = statbuf;
+ }
memcpy(name_buf,bytes,size);
name_buf[size]=0;
} else if (erts_analyze_utf8(bytes,size,&err_pos,&num_chars,NULL) != ERTS_UTF8_OK ||
erts_get_user_requested_filename_encoding() == ERL_FILENAME_LATIN1) {
byte *p;
/* What to do now? Maybe latin1, so just take byte for byte instead */
- name_buf = (char *) erts_alloc(alloc_type, (size+1)*2);
+ if (used)
+ *used = (Sint) (size+1)*2;
+ if ((size+1)*2 > statbuf_size) {
+ name_buf = (char *) erts_alloc(alloc_type, (size+1)*2);
+ } else {
+ name_buf = statbuf;
+ }
p = (byte *) name_buf;
while (size--) {
*p++ = *bytes++;
@@ -2074,7 +2053,13 @@ char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int
*p++ = 0;
*p++ = 0;
} else { /* WIN_WCHAR and valid UTF8 */
- name_buf = (char *) erts_alloc(alloc_type, (num_chars+1)*2);
+ if (used)
+ *used = (Sint) (num_chars+1)*2;
+ if ((num_chars+1)*2 > statbuf_size) {
+ name_buf = (char *) erts_alloc(alloc_type, (num_chars+1)*2);
+ } else {
+ name_buf = statbuf;
+ }
erts_copy_utf8_to_utf16_little((byte *) name_buf, bytes, num_chars);
name_buf[num_chars*2] = 0;
name_buf[num_chars*2+1] = 0;
@@ -2086,6 +2071,71 @@ char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int
return name_buf;
}
+static int filename_len_16bit(byte *str)
+{
+ byte *p = str;
+ while(*p != '\0' || p[1] != '\0') {
+ p += 2;
+ }
+ return (p - str);
+}
+Eterm erts_convert_native_to_filename(Process *p, byte *bytes)
+{
+ Uint size,num_chars;
+ Eterm *hp;
+ byte *err_pos;
+ Uint num_built; /* characters */
+ Uint num_eaten; /* bytes */
+ Eterm ret;
+ int mac = 0;
+
+ switch (erts_get_native_filename_encoding()) {
+ case ERL_FILENAME_LATIN1:
+ goto noconvert;
+ case ERL_FILENAME_UTF8_MAC:
+ mac = 1;
+ case ERL_FILENAME_UTF8:
+ size = strlen((char *) bytes);
+ if (erts_analyze_utf8(bytes,size,&err_pos,&num_chars,NULL) != ERTS_UTF8_OK) {
+ goto noconvert;
+ }
+ num_built = 0;
+ num_eaten = 0;
+ if (mac) {
+ ret = do_utf8_to_list_normalize(p, num_chars, bytes, size);
+ } else {
+ ret = do_utf8_to_list(p, num_chars, bytes, size, num_chars, &num_built, &num_eaten, NIL);
+ }
+ return ret;
+ case ERL_FILENAME_WIN_WCHAR:
+ size=filename_len_16bit(bytes);
+ if ((size % 2) != 0) { /* Panic fixup to avoid crashing the emulator */
+ size--;
+ hp = HAlloc(p, size+2);
+ ret = CONS(hp,make_small((Uint) bytes[size]),NIL);
+ hp += 2;
+ } else {
+ hp = HAlloc(p, size);
+ ret = NIL;
+ }
+ bytes += size-1;
+ while (size > 0) {
+ Uint x = ((Uint) *bytes--) << 8;
+ x |= ((Uint) *bytes--);
+ size -= 2;
+ ret = CONS(hp,make_small(x),ret);
+ hp += 2;
+ }
+ return ret;
+ default:
+ goto noconvert;
+ }
+ noconvert:
+ size = strlen((char *) bytes);
+ hp = HAlloc(p, 2 * size);
+ return erts_bin_bytes_to_list(NIL, hp, bytes, size, 0);
+}
+
Sint erts_native_filename_need(Eterm ioterm, int encoding)
{
@@ -2619,3 +2669,4 @@ BIF_RETTYPE file_native_name_encoding_0(BIF_ALIST_0)
BIF_RET(am_undefined);
}
}
+
diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h
index a0e12e57f2..c962955de9 100644
--- a/erts/emulator/beam/erl_vm.h
+++ b/erts/emulator/beam/erl_vm.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2012. 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/beam/erlang_dtrace.d b/erts/emulator/beam/erlang_dtrace.d
index c1024dafc4..e3ebbb84f4 100644
--- a/erts/emulator/beam/erlang_dtrace.d
+++ b/erts/emulator/beam/erlang_dtrace.d
@@ -639,9 +639,9 @@ provider erlang {
* Entry into the efile_drv.c file I/O driver
*
* For a list of command numbers used by this driver, see the section
- * "Guide to probe arguments" in ../../../README.md. That section
- * also contains explanation of the various integer and string
- * arguments that may be present when any particular probe fires.
+ * "Guide to efile_drv.c probe arguments" in ../../../HOWTO/DTRACE.md.
+ * That section also contains explanation of the various integer and
+ * string arguments that may be present when any particular probe fires.
*
* NOTE: Not all Linux platforms (using SystemTap) can support
* arguments beyond arg9.
@@ -717,6 +717,2916 @@ provider erlang {
* inside the same I/O worker pool thread.
* For R14B03's source, see erl_async.c lines 302-317.
*/
+
+/*
+ * The set of probes for use by Erlang code ... moved to here from
+ * lib/runtime_tools/c_src/dtrace_user.d until a more portable solution
+ * is found. This move pollutes the Erlang VM with functions that are
+ * only used by the NIF shared library code in
+ * lib/runtime_tools/c_src/dyntrace.c. The reason this is necessary is
+ * in order to work around an issue on several platforms, including
+ * SystemTap 1.3 and Solaris. The Solaris issue is discussed in the
+ * `dtrace-discuss` mailing list thread on 01 Dec 2011 17:58:15.
+ */
+ /**
+ * If you use only a single probe, but you also embed that probe
+ * in many different places in your code, if that probe fires 100K
+ * or more times per second, then it *will* hurt when you have to
+ * enable that probe.
+ *
+ * However, if you have any different probes, then you can ensure
+ * that any probe on a hot code path will use separate probe(s)
+ * than everyone else ... and you can then enable many non-hot
+ * probes in production without worry about creating too much
+ * measurement overhead.
+ *
+ * In an ideal world, we would use the libusdt library to be able
+ * to create arbitrary DTrace probes with more convenient and
+ * meaningful names than "user_trace-n7". But libusdt doesn't
+ * (yet) support all of the platforms that DTrace does, and there
+ * is no known (yet) equivalent for SystemTap.
+ */
+ probe user_trace__n0(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n1(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n2(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n3(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n4(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n5(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n6(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n7(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n8(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n9(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n10(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n11(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n12(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n13(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n14(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n15(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n16(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n17(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n18(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n19(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n20(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n21(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n22(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n23(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n24(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n25(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n26(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n27(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n28(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n29(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n30(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n31(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n32(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n33(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n34(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n35(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n36(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n37(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n38(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n39(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n40(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n41(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n42(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n43(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n44(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n45(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n46(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n47(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n48(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n49(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n50(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n51(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n52(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n53(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n54(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n55(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n56(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n57(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n58(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n59(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n60(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n61(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n62(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n63(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n64(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n65(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n66(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n67(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n68(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n69(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n70(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n71(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n72(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n73(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n74(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n75(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n76(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n77(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n78(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n79(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n80(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n81(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n82(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n83(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n84(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n85(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n86(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n87(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n88(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n89(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n90(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n91(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n92(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n93(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n94(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n95(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n96(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n97(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n98(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n99(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n100(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n101(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n102(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n103(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n104(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n105(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n106(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n107(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n108(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n109(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n110(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n111(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n112(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n113(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n114(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n115(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n116(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n117(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n118(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n119(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n120(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n121(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n122(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n123(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n124(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n125(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n126(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n127(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n128(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n129(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n130(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n131(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n132(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n133(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n134(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n135(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n136(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n137(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n138(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n139(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n140(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n141(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n142(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n143(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n144(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n145(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n146(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n147(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n148(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n149(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n150(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n151(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n152(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n153(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n154(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n155(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n156(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n157(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n158(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n159(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n160(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n161(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n162(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n163(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n164(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n165(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n166(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n167(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n168(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n169(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n170(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n171(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n172(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n173(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n174(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n175(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n176(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n177(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n178(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n179(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n180(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n181(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n182(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n183(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n184(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n185(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n186(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n187(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n188(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n189(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n190(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n191(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n192(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n193(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n194(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n195(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n196(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n197(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n198(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n199(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n200(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n201(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n202(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n203(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n204(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n205(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n206(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n207(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n208(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n209(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n210(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n211(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n212(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n213(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n214(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n215(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n216(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n217(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n218(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n219(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n220(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n221(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n222(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n223(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n224(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n225(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n226(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n227(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n228(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n229(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n230(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n231(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n232(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n233(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n234(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n235(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n236(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n237(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n238(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n239(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n240(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n241(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n242(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n243(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n244(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n245(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n246(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n247(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n248(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n249(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n250(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n251(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n252(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n253(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n254(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n255(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n256(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n257(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n258(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n259(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n260(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n261(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n262(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n263(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n264(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n265(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n266(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n267(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n268(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n269(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n270(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n271(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n272(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n273(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n274(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n275(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n276(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n277(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n278(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n279(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n280(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n281(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n282(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n283(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n284(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n285(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n286(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n287(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n288(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n289(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n290(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n291(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n292(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n293(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n294(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n295(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n296(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n297(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n298(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n299(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n300(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n301(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n302(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n303(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n304(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n305(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n306(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n307(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n308(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n309(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n310(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n311(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n312(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n313(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n314(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n315(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n316(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n317(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n318(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n319(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n320(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n321(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n322(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n323(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n324(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n325(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n326(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n327(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n328(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n329(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n330(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n331(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n332(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n333(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n334(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n335(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n336(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n337(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n338(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n339(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n340(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n341(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n342(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n343(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n344(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n345(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n346(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n347(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n348(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n349(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n350(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n351(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n352(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n353(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n354(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n355(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n356(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n357(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n358(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n359(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n360(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n361(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n362(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n363(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n364(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n365(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n366(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n367(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n368(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n369(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n370(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n371(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n372(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n373(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n374(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n375(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n376(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n377(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n378(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n379(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n380(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n381(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n382(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n383(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n384(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n385(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n386(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n387(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n388(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n389(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n390(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n391(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n392(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n393(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n394(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n395(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n396(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n397(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n398(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n399(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n400(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n401(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n402(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n403(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n404(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n405(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n406(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n407(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n408(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n409(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n410(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n411(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n412(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n413(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n414(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n415(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n416(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n417(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n418(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n419(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n420(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n421(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n422(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n423(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n424(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n425(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n426(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n427(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n428(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n429(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n430(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n431(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n432(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n433(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n434(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n435(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n436(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n437(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n438(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n439(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n440(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n441(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n442(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n443(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n444(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n445(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n446(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n447(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n448(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n449(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n450(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n451(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n452(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n453(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n454(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n455(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n456(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n457(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n458(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n459(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n460(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n461(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n462(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n463(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n464(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n465(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n466(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n467(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n468(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n469(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n470(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n471(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n472(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n473(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n474(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n475(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n476(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n477(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n478(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n479(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n480(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n481(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n482(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n483(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n484(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n485(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n486(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n487(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n488(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n489(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n490(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n491(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n492(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n493(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n494(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n495(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n496(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n497(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n498(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n499(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n500(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n501(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n502(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n503(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n504(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n505(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n506(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n507(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n508(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n509(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n510(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n511(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n512(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n513(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n514(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n515(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n516(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n517(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n518(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n519(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n520(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n521(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n522(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n523(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n524(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n525(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n526(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n527(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n528(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n529(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n530(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n531(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n532(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n533(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n534(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n535(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n536(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n537(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n538(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n539(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n540(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n541(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n542(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n543(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n544(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n545(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n546(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n547(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n548(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n549(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n550(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n551(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n552(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n553(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n554(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n555(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n556(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n557(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n558(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n559(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n560(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n561(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n562(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n563(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n564(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n565(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n566(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n567(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n568(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n569(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n570(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n571(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n572(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n573(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n574(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n575(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n576(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n577(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n578(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n579(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n580(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n581(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n582(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n583(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n584(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n585(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n586(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n587(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n588(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n589(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n590(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n591(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n592(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n593(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n594(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n595(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n596(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n597(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n598(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n599(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n600(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n601(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n602(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n603(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n604(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n605(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n606(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n607(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n608(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n609(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n610(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n611(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n612(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n613(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n614(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n615(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n616(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n617(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n618(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n619(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n620(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n621(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n622(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n623(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n624(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n625(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n626(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n627(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n628(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n629(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n630(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n631(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n632(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n633(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n634(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n635(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n636(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n637(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n638(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n639(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n640(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n641(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n642(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n643(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n644(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n645(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n646(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n647(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n648(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n649(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n650(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n651(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n652(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n653(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n654(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n655(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n656(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n657(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n658(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n659(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n660(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n661(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n662(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n663(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n664(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n665(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n666(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n667(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n668(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n669(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n670(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n671(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n672(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n673(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n674(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n675(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n676(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n677(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n678(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n679(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n680(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n681(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n682(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n683(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n684(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n685(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n686(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n687(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n688(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n689(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n690(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n691(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n692(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n693(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n694(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n695(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n696(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n697(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n698(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n699(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n700(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n701(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n702(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n703(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n704(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n705(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n706(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n707(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n708(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n709(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n710(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n711(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n712(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n713(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n714(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n715(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n716(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n717(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n718(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n719(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n720(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n721(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n722(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n723(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n724(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n725(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n726(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n727(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n728(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n729(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n730(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n731(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n732(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n733(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n734(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n735(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n736(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n737(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n738(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n739(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n740(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n741(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n742(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n743(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n744(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n745(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n746(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n747(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n748(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n749(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n750(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n751(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n752(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n753(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n754(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n755(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n756(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n757(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n758(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n759(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n760(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n761(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n762(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n763(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n764(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n765(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n766(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n767(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n768(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n769(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n770(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n771(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n772(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n773(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n774(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n775(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n776(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n777(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n778(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n779(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n780(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n781(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n782(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n783(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n784(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n785(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n786(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n787(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n788(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n789(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n790(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n791(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n792(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n793(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n794(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n795(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n796(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n797(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n798(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n799(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n800(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n801(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n802(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n803(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n804(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n805(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n806(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n807(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n808(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n809(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n810(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n811(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n812(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n813(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n814(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n815(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n816(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n817(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n818(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n819(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n820(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n821(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n822(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n823(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n824(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n825(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n826(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n827(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n828(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n829(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n830(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n831(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n832(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n833(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n834(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n835(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n836(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n837(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n838(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n839(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n840(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n841(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n842(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n843(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n844(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n845(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n846(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n847(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n848(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n849(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n850(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n851(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n852(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n853(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n854(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n855(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n856(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n857(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n858(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n859(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n860(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n861(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n862(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n863(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n864(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n865(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n866(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n867(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n868(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n869(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n870(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n871(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n872(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n873(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n874(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n875(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n876(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n877(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n878(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n879(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n880(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n881(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n882(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n883(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n884(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n885(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n886(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n887(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n888(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n889(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n890(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n891(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n892(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n893(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n894(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n895(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n896(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n897(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n898(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n899(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n900(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n901(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n902(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n903(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n904(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n905(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n906(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n907(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n908(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n909(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n910(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n911(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n912(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n913(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n914(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n915(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n916(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n917(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n918(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n919(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n920(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n921(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n922(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n923(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n924(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n925(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n926(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n927(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n928(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n929(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n930(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n931(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n932(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n933(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n934(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n935(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n936(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n937(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n938(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n939(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n940(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n941(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n942(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n943(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n944(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n945(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n946(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n947(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n948(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n949(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ probe user_trace__n950(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+ /**
+ * Send a single string to a probe.
+ * This probe is deprecated.
+ *
+ * @param NUL-terminated string
+ */
+ probe user_trace__s1(char* message);
+
+ /**
+ * Multi-purpose probe: up to 4 NUL-terminated strings and 4
+ * 64-bit integer arguments.
+ * This probe is deprecated.
+ *
+ * @param proc, the PID (string form) of the sending process
+ * @param user_tag, the user tag of the sender
+ * @param i1, integer
+ * @param i2, integer
+ * @param i3, integer
+ * @param i4, integer
+ * @param s1, string/iolist. D's arg6 is NULL if not given by Erlang
+ * @param s2, string/iolist. D's arg7 is NULL if not given by Erlang
+ * @param s3, string/iolist. D's arg8 is NULL if not given by Erlang
+ * @param s4, string/iolist. D's arg9 is NULL if not given by Erlang
+ */
+ probe user_trace__i4s4(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+
};
#pragma D attributes Evolving/Evolving/Common provider erlang provider
diff --git a/erts/emulator/beam/export.c b/erts/emulator/beam/export.c
index fb0ee99119..6b5121f917 100644
--- a/erts/emulator/beam/export.c
+++ b/erts/emulator/beam/export.c
@@ -32,98 +32,162 @@
#define EXPORT_HASH(m,f,a) ((m)*(f)+(a))
-static IndexTable export_table; /* Not locked. */
-static Hash secondary_export_table; /* Locked. */
+#ifdef DEBUG
+# define IF_DEBUG(x) x
+#else
+# define IF_DEBUG(x)
+#endif
-#include "erl_smp.h"
+static IndexTable export_tables[ERTS_NUM_CODE_IX]; /* Active not locked */
-static erts_smp_rwmtx_t export_table_lock; /* Locks the secondary export table. */
+static erts_smp_atomic_t total_entries_bytes;
-#define export_read_lock() erts_smp_rwmtx_rlock(&export_table_lock)
-#define export_read_unlock() erts_smp_rwmtx_runlock(&export_table_lock)
-#define export_write_lock() erts_smp_rwmtx_rwlock(&export_table_lock)
-#define export_write_unlock() erts_smp_rwmtx_rwunlock(&export_table_lock)
+#include "erl_smp.h"
+
+/* This lock protects the staging export table from concurrent access
+ * AND it protects the staging table from becoming active.
+ */
+erts_smp_mtx_t export_staging_lock;
extern BeamInstr* em_call_error_handler;
extern BeamInstr* em_call_traced_function;
+struct export_entry
+{
+ IndexSlot slot; /* MUST BE LOCATED AT TOP OF STRUCT!!! */
+ Export* ep;
+};
+
+/* Helper struct that brings things together in one allocation
+*/
+struct export_blob
+{
+ Export exp;
+ struct export_entry entryv[ERTS_NUM_CODE_IX];
+ /* Note that entryv is not indexed by "code_ix".
+ */
+};
+
+/* Helper struct only used as template
+*/
+struct export_templ
+{
+ struct export_entry entry;
+ Export exp;
+};
+
+static struct export_blob* entry_to_blob(struct export_entry* ee)
+{
+ return (struct export_blob*)
+ ((char*)ee->ep - offsetof(struct export_blob,exp));
+}
+
void
export_info(int to, void *to_arg)
{
#ifdef ERTS_SMP
int lock = !ERTS_IS_CRASH_DUMPING;
if (lock)
- export_read_lock();
+ export_staging_lock();
#endif
- index_info(to, to_arg, &export_table);
- hash_info(to, to_arg, &secondary_export_table);
+ index_info(to, to_arg, &export_tables[erts_active_code_ix()]);
+ hash_info(to, to_arg, &export_tables[erts_staging_code_ix()].htable);
#ifdef ERTS_SMP
if (lock)
- export_read_unlock();
+ export_staging_unlock();
#endif
}
static HashValue
-export_hash(Export* x)
+export_hash(struct export_entry* ee)
{
+ Export* x = ee->ep;
return EXPORT_HASH(x->code[0], x->code[1], x->code[2]);
}
static int
-export_cmp(Export* tmpl, Export* obj)
+export_cmp(struct export_entry* tmpl_e, struct export_entry* obj_e)
{
+ Export* tmpl = tmpl_e->ep;
+ Export* obj = obj_e->ep;
return !(tmpl->code[0] == obj->code[0] &&
tmpl->code[1] == obj->code[1] &&
tmpl->code[2] == obj->code[2]);
}
-static Export*
-export_alloc(Export* tmpl)
+static struct export_entry*
+export_alloc(struct export_entry* tmpl_e)
{
- Export* obj = (Export*) erts_alloc(ERTS_ALC_T_EXPORT, sizeof(Export));
-
- obj->fake_op_func_info_for_hipe[0] = 0;
- obj->fake_op_func_info_for_hipe[1] = 0;
- obj->code[0] = tmpl->code[0];
- obj->code[1] = tmpl->code[1];
- obj->code[2] = tmpl->code[2];
- obj->slot.index = -1;
- obj->address = obj->code+3;
- obj->code[3] = (BeamInstr) em_call_error_handler;
- obj->code[4] = 0;
- obj->match_prog_set = NULL;
- return obj;
+ struct export_blob* blob;
+ unsigned ix;
+
+ if (tmpl_e->slot.index == -1) { /* Template, allocate blob */
+ Export* tmpl = tmpl_e->ep;
+ Export* obj;
+
+ blob = (struct export_blob*) erts_alloc(ERTS_ALC_T_EXPORT, sizeof(*blob));
+ erts_smp_atomic_add_nob(&total_entries_bytes, sizeof(*blob));
+ obj = &blob->exp;
+ obj->fake_op_func_info_for_hipe[0] = 0;
+ obj->fake_op_func_info_for_hipe[1] = 0;
+ obj->code[0] = tmpl->code[0];
+ obj->code[1] = tmpl->code[1];
+ obj->code[2] = tmpl->code[2];
+ obj->code[3] = (BeamInstr) em_call_error_handler;
+ obj->code[4] = 0;
+
+ for (ix=0; ix<ERTS_NUM_CODE_IX; ix++) {
+ obj->addressv[ix] = obj->code+3;
+
+ blob->entryv[ix].slot.index = -1;
+ blob->entryv[ix].ep = &blob->exp;
+ }
+ ix = 0;
+ }
+ else { /* Existing entry in another table, use free entry in blob */
+ blob = entry_to_blob(tmpl_e);
+ for (ix = 0; blob->entryv[ix].slot.index >= 0; ix++) {
+ ASSERT(ix < ERTS_NUM_CODE_IX);
+ }
+ }
+ return &blob->entryv[ix];
}
-
-static void
-export_free(Export* obj)
+static void
+export_free(struct export_entry* obj)
{
- erts_free(ERTS_ALC_T_EXPORT, (void*) obj);
+ struct export_blob* blob = entry_to_blob(obj);
+ int i;
+ obj->slot.index = -1;
+ for (i=0; i < ERTS_NUM_CODE_IX; i++) {
+ if (blob->entryv[i].slot.index >= 0) {
+ return;
+ }
+ }
+ erts_free(ERTS_ALC_T_EXPORT, blob);
+ erts_smp_atomic_add_nob(&total_entries_bytes, -sizeof(*blob));
}
-
void
init_export_table(void)
{
HashFunctions f;
- erts_smp_rwmtx_opt_t rwmtx_opt = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER;
- rwmtx_opt.type = ERTS_SMP_RWMTX_TYPE_FREQUENT_READ;
- rwmtx_opt.lived = ERTS_SMP_RWMTX_LONG_LIVED;
+ int i;
- erts_smp_rwmtx_init_opt(&export_table_lock, &rwmtx_opt, "export_tab");
+ erts_smp_mtx_init(&export_staging_lock, "export_tab");
+ erts_smp_atomic_init_nob(&total_entries_bytes, 0);
f.hash = (H_FUN) export_hash;
f.cmp = (HCMP_FUN) export_cmp;
f.alloc = (HALLOC_FUN) export_alloc;
f.free = (HFREE_FUN) export_free;
- erts_index_init(ERTS_ALC_T_EXPORT_TABLE, &export_table, "export_list",
- EXPORT_INITIAL_SIZE, EXPORT_LIMIT, f);
- hash_init(ERTS_ALC_T_EXPORT_TABLE, &secondary_export_table,
- "secondary_export_table", 50, f);
+ for (i=0; i<ERTS_NUM_CODE_IX; i++) {
+ erts_index_init(ERTS_ALC_T_EXPORT_TABLE, &export_tables[i], "export_list",
+ EXPORT_INITIAL_SIZE, EXPORT_LIMIT, f);
+ }
}
/*
@@ -138,29 +202,42 @@ init_export_table(void)
* called through such an export entry.
* 3) This function is suitable for the implementation of erlang:apply/3.
*/
+extern Export* /* inline-helper */
+erts_find_export_entry(Eterm m, Eterm f, unsigned int a,ErtsCodeIndex code_ix);
Export*
-erts_find_export_entry(Eterm m, Eterm f, unsigned int a)
+erts_find_export_entry(Eterm m, Eterm f, unsigned int a, ErtsCodeIndex code_ix)
{
HashValue hval = EXPORT_HASH((BeamInstr) m, (BeamInstr) f, (BeamInstr) a);
int ix;
HashBucket* b;
- ix = hval % export_table.htable.size;
- b = export_table.htable.bucket[ix];
+ ix = hval % export_tables[code_ix].htable.size;
+ b = export_tables[code_ix].htable.bucket[ix];
/*
* Note: We have inlined the code from hash.c for speed.
*/
while (b != (HashBucket*) 0) {
- Export* ep = (Export *) b;
+ Export* ep = ((struct export_entry*) b)->ep;
if (ep->code[0] == m && ep->code[1] == f && ep->code[2] == a) {
- break;
+ return ep;
}
b = b->next;
}
- return (Export*)b;
+ return NULL;
+}
+
+static struct export_entry* init_template(struct export_templ* templ,
+ Eterm m, Eterm f, unsigned a)
+{
+ templ->entry.ep = &templ->exp;
+ templ->entry.slot.index = -1;
+ templ->exp.code[0] = m;
+ templ->exp.code[1] = f;
+ templ->exp.code[2] = a;
+ return &templ->entry;
}
@@ -176,47 +253,42 @@ erts_find_export_entry(Eterm m, Eterm f, unsigned int a)
*/
Export*
-erts_find_function(Eterm m, Eterm f, unsigned int a)
+erts_find_function(Eterm m, Eterm f, unsigned int a, ErtsCodeIndex code_ix)
{
- Export e;
- Export* ep;
-
- e.code[0] = m;
- e.code[1] = f;
- e.code[2] = a;
-
- ep = hash_get(&export_table.htable, (void*) &e);
- if (ep != NULL && ep->address == ep->code+3 &&
- ep->code[3] != (BeamInstr) em_call_traced_function) {
- ep = NULL;
+ struct export_templ templ;
+ struct export_entry* ee;
+
+ ee = hash_get(&export_tables[code_ix].htable, init_template(&templ, m, f, a));
+ if (ee == NULL ||
+ (ee->ep->addressv[code_ix] == ee->ep->code+3 &&
+ ee->ep->code[3] != (BeamInstr) BeamOp(op_i_generic_breakpoint))) {
+ return NULL;
}
- return ep;
+ return ee->ep;
}
/*
* Returns a pointer to an existing export entry for a MFA,
* or creates a new one and returns the pointer.
*
- * This function provides unlocked write access to the main export
- * table. It should only be used during start up or when
- * all other threads are blocked.
+ * This function acts on the staging export table. It should only be used
+ * to load new code.
*/
Export*
erts_export_put(Eterm mod, Eterm func, unsigned int arity)
{
- Export e;
- int ix;
+ ErtsCodeIndex code_ix = erts_staging_code_ix();
+ struct export_templ templ;
+ struct export_entry* ee;
- ERTS_SMP_LC_ASSERT(erts_initialized == 0
- || erts_smp_thr_progress_is_blocking());
ASSERT(is_atom(mod));
ASSERT(is_atom(func));
- e.code[0] = mod;
- e.code[1] = func;
- e.code[2] = arity;
- ix = index_put(&export_table, (void*) &e);
- return (Export*) erts_index_lookup(&export_table, ix);
+ export_staging_lock();
+ ee = (struct export_entry*) index_put_entry(&export_tables[code_ix],
+ init_template(&templ, mod, func, arity));
+ export_staging_unlock();
+ return ee->ep;
}
/*
@@ -224,77 +296,117 @@ erts_export_put(Eterm mod, Eterm func, unsigned int arity)
* export entry (making a call through it will cause the error_handler to
* be called).
*
- * Stub export entries will be placed in the secondary export table.
- * erts_export_consolidate() will move all stub export entries into the
- * main export table (will be done the next time code is loaded).
+ * Stub export entries will be placed in the loader export table.
*/
Export*
erts_export_get_or_make_stub(Eterm mod, Eterm func, unsigned int arity)
{
- Export e;
+ ErtsCodeIndex code_ix;
Export* ep;
+ IF_DEBUG(int retrying = 0;)
ASSERT(is_atom(mod));
ASSERT(is_atom(func));
-
- e.code[0] = mod;
- e.code[1] = func;
- e.code[2] = arity;
- ep = erts_find_export_entry(mod, func, arity);
- if (ep == 0) {
- /*
- * The code is not loaded (yet). Put the export in the secondary
- * export table, to avoid having to lock the main export table.
- */
- export_write_lock();
- ep = (Export *) hash_put(&secondary_export_table, (void*) &e);
- export_write_unlock();
- }
+
+ do {
+ code_ix = erts_active_code_ix();
+ ep = erts_find_export_entry(mod, func, arity, code_ix);
+ if (ep == 0) {
+ /*
+ * The code is not loaded (yet). Put the export in the staging
+ * export table, to avoid having to lock the active export table.
+ */
+ export_staging_lock();
+ if (erts_active_code_ix() == code_ix) {
+ struct export_templ templ;
+ struct export_entry* entry;
+
+ IndexTable* tab = &export_tables[erts_staging_code_ix()];
+ init_template(&templ, mod, func, arity);
+ entry = (struct export_entry *) index_put_entry(tab, &templ.entry);
+ ep = entry->ep;
+ ASSERT(ep);
+ }
+ else { /* race */
+ ASSERT(!retrying);
+ IF_DEBUG(retrying = 1);
+ }
+ export_staging_unlock();
+ }
+ } while (!ep);
return ep;
}
-/*
- * To be called before loading code (with other threads blocked).
- * This function will move all export entries from the secondary
- * export table into the primary.
- */
-void
-erts_export_consolidate(void)
+Export *export_list(int i, ErtsCodeIndex code_ix)
{
-#ifdef DEBUG
- HashInfo hi;
-#endif
-
- ERTS_SMP_LC_ASSERT(erts_initialized == 0
- || erts_smp_thr_progress_is_blocking());
-
- export_write_lock();
- erts_index_merge(&secondary_export_table, &export_table);
- erts_hash_merge(&secondary_export_table, &export_table.htable);
- export_write_unlock();
-#ifdef DEBUG
- hash_get_info(&hi, &export_table.htable);
- ASSERT(export_table.entries == hi.objs);
-#endif
+ return ((struct export_entry*) erts_index_lookup(&export_tables[code_ix], i))->ep;
}
-Export *export_list(int i)
+int export_list_size(ErtsCodeIndex code_ix)
{
- return (Export*) erts_index_lookup(&export_table, i);
+ return export_tables[code_ix].entries;
}
-int export_list_size(void)
+int export_table_sz(void)
+{
+ int i, bytes = 0;
+
+ export_staging_lock();
+ for (i=0; i<ERTS_NUM_CODE_IX; i++) {
+ bytes += index_table_sz(&export_tables[i]);
+ }
+ export_staging_unlock();
+ return bytes;
+}
+int export_entries_sz(void)
{
- return export_table.entries;
+ return erts_smp_atomic_read_nob(&total_entries_bytes);
}
+Export *export_get(Export *e)
+{
+ struct export_entry ee;
+ struct export_entry* entry;
-int export_table_sz(void)
+ ee.ep = e;
+ entry = (struct export_entry*)hash_get(&export_tables[erts_active_code_ix()].htable, &ee);
+ return entry ? entry->ep : NULL;
+}
+
+IF_DEBUG(static ErtsCodeIndex debug_start_load_ix = 0;)
+
+
+void export_start_staging(void)
{
- return index_table_sz(&export_table);
+ ErtsCodeIndex dst_ix = erts_staging_code_ix();
+ ErtsCodeIndex src_ix = erts_active_code_ix();
+ IndexTable* dst = &export_tables[dst_ix];
+ IndexTable* src = &export_tables[src_ix];
+ struct export_entry* src_entry;
+ struct export_entry* dst_entry;
+ int i;
+
+ ASSERT(dst_ix != src_ix);
+ ASSERT(debug_start_load_ix == -1);
+
+ export_staging_lock();
+ /*
+ * Insert all entries in src into dst table
+ */
+ for (i = 0; i < src->entries; i++) {
+ src_entry = (struct export_entry*) erts_index_lookup(src, i);
+ src_entry->ep->addressv[dst_ix] = src_entry->ep->addressv[src_ix];
+ dst_entry = (struct export_entry*) index_put_entry(dst, src_entry);
+ ASSERT(entry_to_blob(src_entry) == entry_to_blob(dst_entry));
+ }
+ export_staging_unlock();
+
+ IF_DEBUG(debug_start_load_ix = dst_ix);
}
-Export *export_get(Export *e)
+void export_end_staging(int commit)
{
- return hash_get(&export_table.htable, e);
+ ASSERT(debug_start_load_ix == erts_staging_code_ix());
+ IF_DEBUG(debug_start_load_ix = -1);
}
+
diff --git a/erts/emulator/beam/export.h b/erts/emulator/beam/export.h
index c604fdf7c3..ee06e69aff 100644
--- a/erts/emulator/beam/export.h
+++ b/erts/emulator/beam/export.h
@@ -28,14 +28,15 @@
#include "index.h"
#endif
+#include "code_ix.h"
+
/*
** Export entry
*/
+
typedef struct export
{
- IndexSlot slot; /* MUST BE LOCATED AT TOP OF STRUCT!!! */
- void* address; /* Pointer to code for function. */
- struct binary* match_prog_set; /* Match program for tracing. */
+ void* addressv[ERTS_NUM_CODE_IX]; /* Pointer to code for function. */
BeamInstr fake_op_func_info_for_hipe[2]; /* MUST be just before code[] */
/*
@@ -44,12 +45,12 @@ typedef struct export
* code[2]: Arity (untagged integer).
* code[3]: This entry is 0 unless the 'address' field points to it.
* Threaded code instruction to load function
- * (em_call_error_handler), execute BIF (em_apply_bif,
- * em_apply_apply), or call a traced function
- * (em_call_traced_function).
- * code[4]: Function pointer to BIF function (for BIFs only)
+ * (em_call_error_handler), execute BIF (em_apply_bif),
+ * or a breakpoint instruction (op_i_generic_breakpoint).
+ * code[4]: Function pointer to BIF function (for BIFs only),
* or pointer to threaded code if the module has an
- * on_load function that has not been run yet.
+ * on_load function that has not been run yet, or pointer
+ * to code for function code[3] is a breakpont instruction.
* Otherwise: 0.
*/
BeamInstr code[5];
@@ -59,21 +60,38 @@ typedef struct export
void init_export_table(void);
void export_info(int, void *);
-Export* erts_find_export_entry(Eterm m, Eterm f, unsigned int a);
+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);
-
Export* erts_export_get_or_make_stub(Eterm, Eterm, unsigned);
-void erts_export_consolidate(void);
-Export *export_list(int);
-int export_list_size(void);
+Export *export_list(int,ErtsCodeIndex);
+int export_list_size(ErtsCodeIndex);
int export_table_sz(void);
+int export_entries_sz(void);
Export *export_get(Export*);
+void export_start_staging(void);
+void export_end_staging(int commit);
+
+extern erts_smp_mtx_t export_staging_lock;
+#define export_staging_lock() erts_smp_mtx_lock(&export_staging_lock)
+#define export_staging_unlock() erts_smp_mtx_unlock(&export_staging_lock)
#include "beam_load.h" /* For em_* extern declarations */
#define ExportIsBuiltIn(EntryPtr) \
-(((EntryPtr)->address == (EntryPtr)->code + 3) && \
+(((EntryPtr)->addressv[erts_active_code_ix()] == (EntryPtr)->code + 3) && \
((EntryPtr)->code[3] == (BeamInstr) em_apply_bif))
-#endif
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE Export*
+erts_active_export_entry(Eterm m, Eterm f, unsigned int a)
+{
+ extern Export* erts_find_export_entry(Eterm m, Eterm f, unsigned a, ErtsCodeIndex);
+ return erts_find_export_entry(m, f, a, erts_active_code_ix());
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+#endif /* __EXPORT_H__ */
+
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 4348578694..ab1065aaa1 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -1850,8 +1850,8 @@ enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
} else {
*ep++ = FLOAT_EXT;
- /* now the sprintf which does the work */
- i = sys_double_to_chars(f.fd, (char*) ep);
+ /* now the erts_snprintf which does the work */
+ i = sys_double_to_chars(f.fd, (char*) ep, (size_t)31);
/* Don't leave garbage after the float! (Bad practice in general,
* and Purify complains.)
@@ -1889,7 +1889,9 @@ enc_term(ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
*ep++ = BINARY_INTERNAL_REF;
}
if (pb->flags) {
+ char* before_realloc = pb->val->orig_bytes;
erts_emasculate_writable_binary(pb);
+ bytes += (pb->val->orig_bytes - before_realloc);
}
erts_refc_inc(&pb->val->refc, 2);
@@ -2562,7 +2564,7 @@ dec_term_atom_common:
goto error;
}
if (edep && (edep->flags & ERTS_DIST_EXT_BTT_SAFE)) {
- if (!erts_find_export_entry(mod, name, arity))
+ if (!erts_active_export_entry(mod, name, arity))
goto error;
}
*objp = make_export(hp);
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 1b15c4ac3b..1503d793ab 100755
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -28,6 +28,7 @@
#include "hash.h"
#include "index.h"
#include "atom.h"
+#include "code_ix.h"
#include "export.h"
#include "module.h"
#include "register.h"
@@ -143,8 +144,8 @@ typedef struct ErtsXPortsList_ ErtsXPortsList;
struct port {
ErtsPortTaskSched sched;
ErtsPortTaskHandle timeout_task;
-#ifdef ERTS_SMP
erts_smp_atomic_t refc;
+#ifdef ERTS_SMP
erts_smp_mtx_t *lock;
ErtsXPortsList *xports;
erts_smp_atomic_t run_queue;
@@ -197,6 +198,8 @@ erts_port_runq(Port *prt)
#ifdef ERTS_SMP
ErtsRunQueue *rq1, *rq2;
rq1 = (ErtsRunQueue *) erts_smp_atomic_read_nob(&prt->run_queue);
+ if (!rq1)
+ return NULL;
while (1) {
erts_smp_runq_lock(rq1);
rq2 = (ErtsRunQueue *) erts_smp_atomic_read_nob(&prt->run_queue);
@@ -204,6 +207,8 @@ erts_port_runq(Port *prt)
return rq1;
erts_smp_runq_unlock(rq1);
rq1 = rq2;
+ if (!rq1)
+ return NULL;
}
#else
return ERTS_RUNQ_IX(0);
@@ -764,6 +769,8 @@ void erts_queue_monitor_message(Process *,
Eterm,
Eterm,
Eterm);
+void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a,
+ Eterm (*bif)(Process*,Eterm*));
void erts_init_bif(void);
Eterm erl_send(Process *p, Eterm to, Eterm msg);
@@ -786,24 +793,33 @@ typedef struct {
Eterm* fname_ptr; /* Pointer to fname table */
} FunctionInfo;
-struct LoaderState* erts_alloc_loader_state(void);
-Eterm erts_prepare_loading(struct LoaderState*, Process *c_p,
+Binary* erts_alloc_loader_state(void);
+Eterm erts_module_for_prepared_code(Binary* magic);
+Eterm erts_prepare_loading(Binary* loader_state, Process *c_p,
Eterm group_leader, Eterm* modp,
byte* code, Uint size);
-Eterm erts_finish_loading(struct LoaderState* stp, Process* c_p,
+Eterm erts_finish_loading(Binary* loader_state, Process* c_p,
ErtsProcLocks c_p_locks, Eterm* modp);
-Eterm erts_load_module(Process *c_p, ErtsProcLocks c_p_locks,
- Eterm group_leader, Eterm* mod, byte* code, Uint size);
+Eterm erts_preload_module(Process *c_p, ErtsProcLocks c_p_locks,
+ Eterm group_leader, Eterm* mod, byte* code, Uint size);
void init_load(void);
BeamInstr* find_function_from_pc(BeamInstr* pc);
Eterm* erts_build_mfa_item(FunctionInfo* fi, Eterm* hp,
Eterm args, Eterm* mfa_p);
-void erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info);
void erts_set_current_function(FunctionInfo* fi, BeamInstr* current);
Eterm erts_module_info_0(Process* p, Eterm module);
Eterm erts_module_info_1(Process* p, Eterm module, Eterm what);
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_end_staging_ranges(int commit);
+void erts_update_ranges(BeamInstr* code, Uint size);
+void erts_remove_from_ranges(BeamInstr* code);
+UWord erts_ranges_sz(void);
+void erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info);
+
/* break.c */
void init_break_handler(void);
void erts_set_ignore_break(void);
@@ -1024,28 +1040,29 @@ erts_smp_port_state_unlock(Port *prt)
ERTS_GLB_INLINE int
erts_smp_port_trylock(Port *prt)
{
-#ifdef ERTS_SMP
int res;
ASSERT(erts_smp_atomic_read_nob(&prt->refc) > 0);
erts_smp_atomic_inc_nob(&prt->refc);
+
+#ifdef ERTS_SMP
res = erts_smp_mtx_trylock(prt->lock);
if (res == EBUSY) {
erts_smp_atomic_dec_nob(&prt->refc);
}
+#else
+ res = 0;
+#endif
return res;
-#else /* !ERTS_SMP */
- return 0;
-#endif
}
ERTS_GLB_INLINE void
erts_smp_port_lock(Port *prt)
{
-#ifdef ERTS_SMP
ASSERT(erts_smp_atomic_read_nob(&prt->refc) > 0);
erts_smp_atomic_inc_nob(&prt->refc);
+#ifdef ERTS_SMP
erts_smp_mtx_lock(prt->lock);
#endif
}
@@ -1053,14 +1070,14 @@ erts_smp_port_lock(Port *prt)
ERTS_GLB_INLINE void
erts_smp_port_unlock(Port *prt)
{
-#ifdef ERTS_SMP
erts_aint_t refc;
+#ifdef ERTS_SMP
erts_smp_mtx_unlock(prt->lock);
+#endif
refc = erts_smp_atomic_dec_read_nob(&prt->refc);
ASSERT(refc >= 0);
if (refc == 0)
erts_port_cleanup(prt);
-#endif
}
#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
@@ -1123,12 +1140,12 @@ erts_id2port_sflgs(Eterm id, Process *c_p, ErtsProcLocks c_p_locks, Uint32 sflgs
erts_smp_port_state_unlock(prt);
prt = NULL;
}
-#ifdef ERTS_SMP
else {
erts_smp_atomic_inc_nob(&prt->refc);
erts_smp_port_state_unlock(prt);
- if (no_proc_locks)
+#ifdef ERTS_SMP
+ if (no_proc_locks)
erts_smp_mtx_lock(prt->lock);
else if (erts_smp_mtx_trylock(prt->lock) == EBUSY) {
/* Unlock process locks, and acquire locks in lock order... */
@@ -1144,21 +1161,17 @@ erts_id2port_sflgs(Eterm id, Process *c_p, ErtsProcLocks c_p_locks, Uint32 sflgs
erts_smp_port_unlock(prt); /* Also decrements refc... */
prt = NULL;
}
- }
#endif
+ }
+
return prt;
}
ERTS_GLB_INLINE void
erts_port_release(Port *prt)
{
-#ifdef ERTS_SMP
erts_smp_port_unlock(prt);
-#else
- if (prt->status & ERTS_PORT_SFLGS_DEAD)
- erts_port_cleanup(prt);
-#endif
}
ERTS_GLB_INLINE Port*
@@ -1275,6 +1288,122 @@ void erl_drv_thr_init(void);
/* utils.c */
+typedef struct {
+#ifdef DEBUG
+ int smp_api;
+#endif
+ union {
+ Uint64 not_atomic;
+#ifdef ARCH_64
+ erts_atomic_t atomic;
+#else
+ erts_dw_atomic_t atomic;
+#endif
+ } counter;
+} erts_interval_t;
+
+void erts_interval_init(erts_interval_t *);
+void erts_smp_interval_init(erts_interval_t *);
+Uint64 erts_step_interval_nob(erts_interval_t *);
+Uint64 erts_step_interval_relb(erts_interval_t *);
+Uint64 erts_smp_step_interval_nob(erts_interval_t *);
+Uint64 erts_smp_step_interval_relb(erts_interval_t *);
+Uint64 erts_ensure_later_interval_nob(erts_interval_t *, Uint64);
+Uint64 erts_ensure_later_interval_acqb(erts_interval_t *, Uint64);
+Uint64 erts_smp_ensure_later_interval_nob(erts_interval_t *, Uint64);
+Uint64 erts_smp_ensure_later_interval_acqb(erts_interval_t *, Uint64);
+#ifdef ARCH_32
+ERTS_GLB_INLINE Uint64 erts_interval_dw_aint_to_val__(erts_dw_aint_t *);
+#endif
+ERTS_GLB_INLINE Uint64 erts_current_interval_nob__(erts_interval_t *);
+ERTS_GLB_INLINE Uint64 erts_current_interval_acqb__(erts_interval_t *);
+ERTS_GLB_INLINE Uint64 erts_current_interval_nob(erts_interval_t *);
+ERTS_GLB_INLINE Uint64 erts_current_interval_acqb(erts_interval_t *);
+ERTS_GLB_INLINE Uint64 erts_smp_current_interval_nob(erts_interval_t *);
+ERTS_GLB_INLINE Uint64 erts_smp_current_interval_acqb(erts_interval_t *);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+#ifdef ARCH_32
+
+ERTS_GLB_INLINE Uint64
+erts_interval_dw_aint_to_val__(erts_dw_aint_t *dw)
+{
+#ifdef ETHR_SU_DW_NAINT_T__
+ return (Uint64) dw->dw_sint;
+#else
+ Uint64 res;
+ res = (Uint64) ((Uint32) dw->sint[ERTS_DW_AINT_HIGH_WORD]);
+ res <<= 32;
+ res |= (Uint64) ((Uint32) dw->sint[ERTS_DW_AINT_LOW_WORD]);
+ return res;
+#endif
+}
+
+#endif
+
+ERTS_GLB_INLINE Uint64
+erts_current_interval_nob__(erts_interval_t *icp)
+{
+#ifdef ARCH_64
+ return (Uint64) erts_atomic_read_nob(&icp->counter.atomic);
+#else
+ erts_dw_aint_t dw;
+ erts_dw_atomic_read_nob(&icp->counter.atomic, &dw);
+ return erts_interval_dw_aint_to_val__(&dw);
+#endif
+}
+
+ERTS_GLB_INLINE Uint64
+erts_current_interval_acqb__(erts_interval_t *icp)
+{
+#ifdef ARCH_64
+ return (Uint64) erts_atomic_read_acqb(&icp->counter.atomic);
+#else
+ erts_dw_aint_t dw;
+ erts_dw_atomic_read_acqb(&icp->counter.atomic, &dw);
+ return erts_interval_dw_aint_to_val__(&dw);
+#endif
+}
+
+ERTS_GLB_INLINE Uint64
+erts_current_interval_nob(erts_interval_t *icp)
+{
+ ASSERT(!icp->smp_api);
+ return erts_current_interval_nob__(icp);
+}
+
+ERTS_GLB_INLINE Uint64
+erts_current_interval_acqb(erts_interval_t *icp)
+{
+ ASSERT(!icp->smp_api);
+ return erts_current_interval_acqb__(icp);
+}
+
+ERTS_GLB_INLINE Uint64
+erts_smp_current_interval_nob(erts_interval_t *icp)
+{
+ ASSERT(icp->smp_api);
+#ifdef ERTS_SMP
+ return erts_current_interval_nob__(icp);
+#else
+ return icp->counter.not_atomic;
+#endif
+}
+
+ERTS_GLB_INLINE Uint64
+erts_smp_current_interval_acqb(erts_interval_t *icp)
+{
+ ASSERT(icp->smp_api);
+#ifdef ERTS_SMP
+ return erts_current_interval_acqb__(icp);
+#else
+ return icp->counter.not_atomic;
+#endif
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
/*
* To be used to silence unused result warnings, but do not abuse it.
*/
@@ -1282,9 +1411,10 @@ void erts_silence_warn_unused_result(long unused);
void erts_cleanup_offheap(ErlOffHeap *offheap);
-Uint erts_fit_in_bits(Uint);
+int erts_fit_in_bits_int64(Sint64);
+int erts_fit_in_bits_int32(Sint32);
int list_length(Eterm);
-Export* erts_find_function(Eterm, Eterm, unsigned int);
+Export* erts_find_function(Eterm, Eterm, unsigned int, ErtsCodeIndex);
int erts_is_builtin(Eterm, Eterm, int);
Uint32 make_broken_hash(Eterm);
Uint32 block_hash(byte *, unsigned, Uint32);
@@ -1386,8 +1516,12 @@ Sint erts_native_filename_need(Eterm ioterm, int encoding);
void erts_copy_utf8_to_utf16_little(byte *target, byte *bytes, int num_chars);
int erts_analyze_utf8(byte *source, Uint size,
byte **err_pos, Uint *num_chars, int *left);
-char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int allow_empty);
-
+char *erts_convert_filename_to_native(Eterm name, char *statbuf,
+ size_t statbuf_size,
+ ErtsAlcType_t alloc_type,
+ int allow_empty, int allow_atom,
+ Sint *used /* out */);
+Eterm erts_convert_native_to_filename(Process *p, byte *bytes);
#define ERTS_UTF8_OK 0
#define ERTS_UTF8_INCOMPLETE 1
#define ERTS_UTF8_ERROR 2
@@ -1552,17 +1686,19 @@ struct trace_pattern_flags {
};
extern const struct trace_pattern_flags erts_trace_pattern_flags_off;
extern int erts_call_time_breakpoint_tracing;
-int erts_set_trace_pattern(Eterm* mfa, int specified,
+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,
- Eterm meta_tracer_pid);
+ Eterm meta_tracer_pid, int is_blocking);
void
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);
+int erts_is_default_trace_enabled(void);
void erts_bif_trace_init(void);
+int erts_finish_breakpointing(void);
/*
** Call_trace uses this API for the parameter matching functions
@@ -1608,20 +1744,19 @@ extern void erts_match_prog_foreach_offheap(Binary *b,
breakpoint functions */
#define MATCH_SET_EXCEPTION_TRACE (0x4) /* exception trace requested */
#define MATCH_SET_RX_TRACE (MATCH_SET_RETURN_TRACE|MATCH_SET_EXCEPTION_TRACE)
-/*
- * Flag values when tracing bif
- * Future note: flag field is 8 bits
- */
-#define BIF_TRACE_AS_LOCAL (0x1)
-#define BIF_TRACE_AS_GLOBAL (0x2)
-#define BIF_TRACE_AS_META (0x4)
-#define BIF_TRACE_AS_CALL_TIME (0x8)
extern erts_driver_t vanilla_driver;
extern erts_driver_t spawn_driver;
extern erts_driver_t fd_driver;
/* 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,
@@ -1644,16 +1779,22 @@ erts_alloc_message_heap(Uint size,
*/
ERTS_GLB_INLINE Eterm *
-erts_alloc_message_heap(Uint size,
- ErlHeapFragment **bpp,
- ErlOffHeap **ohpp,
- Process *receiver,
- ErtsProcLocks *receiver_locks)
+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;
- ErtsProcLocks ulocks = *receiver_locks & ERTS_PROC_LOCKS_MSG_SEND;
+ 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)
@@ -1669,20 +1810,19 @@ erts_alloc_message_heap(Uint size,
#ifdef ERTS_SMP
try_allocate_on_heap:
#endif
- if (ERTS_PROC_IS_EXITING(receiver)
+ state = erts_smp_atomic32_read_nob(&receiver->state);
+ if (statep)
+ *statep = state;
+ if ((state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT))
|| HEAP_LIMIT(receiver) - HEAP_TOP(receiver) <= size) {
#ifdef ERTS_SMP
- if (locked_main)
- ulocks |= ERTS_PROC_LOCK_MAIN;
+ if (locked_main) {
+ *receiver_locks &= ~ERTS_PROC_LOCK_MAIN;
+ erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_MAIN);
+ }
#endif
goto allocate_in_mbuf;
}
-#ifdef ERTS_SMP
- if (ulocks) {
- erts_smp_proc_unlock(receiver, ulocks);
- *receiver_locks &= ~ulocks;
- }
-#endif
hp = HEAP_TOP(receiver);
HEAP_TOP(receiver) = hp + size;
*bpp = NULL;
@@ -1698,12 +1838,6 @@ erts_alloc_message_heap(Uint size,
else {
ErlHeapFragment *bp;
allocate_in_mbuf:
-#ifdef ERTS_SMP
- if (ulocks) {
- *receiver_locks &= ~ulocks;
- erts_smp_proc_unlock(receiver, ulocks);
- }
-#endif
bp = new_message_buffer(size);
hp = bp->mem;
*bpp = bp;
@@ -1713,6 +1847,17 @@ erts_alloc_message_heap(Uint size,
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
diff --git a/erts/emulator/beam/index.c b/erts/emulator/beam/index.c
index a4a3007f93..25d5cce0f3 100644
--- a/erts/emulator/beam/index.c
+++ b/erts/emulator/beam/index.c
@@ -68,14 +68,14 @@ erts_index_init(ErtsAlcType_t type, IndexTable* t, char* name,
return t;
}
-int
-index_put(IndexTable* t, void* tmpl)
+IndexSlot*
+index_put_entry(IndexTable* t, void* tmpl)
{
int ix;
IndexSlot* p = (IndexSlot*) hash_put(&t->htable, tmpl);
if (p->index >= 0) {
- return p->index;
+ return p;
}
ix = t->entries;
@@ -92,7 +92,7 @@ index_put(IndexTable* t, void* tmpl)
t->entries++;
p->index = ix;
t->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK] = p;
- return ix;
+ return p;
}
int index_get(IndexTable* t, void* tmpl)
@@ -135,3 +135,18 @@ void erts_index_merge(Hash* src, IndexTable* dst)
}
}
}
+
+void index_erase_latest_from(IndexTable* t, Uint from_ix)
+{
+ if(from_ix < (Uint)t->entries) {
+ int ix;
+ for (ix = from_ix; ix < t->entries; ix++) {
+ IndexSlot* obj = t->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK];
+ hash_erase(&t->htable, obj);
+ }
+ t->entries = from_ix;
+ }
+ else {
+ ASSERT(from_ix == t->entries);
+ }
+}
diff --git a/erts/emulator/beam/index.h b/erts/emulator/beam/index.h
index 4eb9b1f992..3afe48d007 100644
--- a/erts/emulator/beam/index.h
+++ b/erts/emulator/beam/index.h
@@ -55,12 +55,24 @@ void index_info(int, void *, IndexTable*);
int index_table_sz(IndexTable *);
int index_get(IndexTable*, void*);
-int index_put(IndexTable*, void*);
+
+IndexSlot* index_put_entry(IndexTable*, void*);
void erts_index_merge(Hash*, IndexTable*);
+/* Erase all entries with index 'ix' and higher
+*/
+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);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE int index_put(IndexTable* t, void* tmpl)
+{
+ return index_put_entry(t, tmpl)->index;
+}
+
ERTS_GLB_INLINE IndexSlot*
erts_index_lookup(IndexTable* t, Uint ix)
{
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 204bff299e..dec51f3be5 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -259,10 +259,8 @@ get_free_port(void)
}
}
port->status = ERTS_PORT_SFLG_INITIALIZING;
-#ifdef ERTS_SMP
- ERTS_SMP_LC_ASSERT(erts_smp_atomic_read_nob(&port->refc) == 0);
+ ERTS_LC_ASSERT(erts_smp_atomic_read_nob(&port->refc) == 0);
erts_smp_atomic_set_nob(&port->refc, 2); /* Port alive + lock */
-#endif
erts_smp_port_state_unlock(port);
return num & port_num_mask;
}
@@ -340,10 +338,14 @@ port_cleanup(Port *prt)
prt->drv_ptr = NULL;
ASSERT(driver);
-#ifdef ERTS_SMP
-
ASSERT(prt->status & ERTS_PORT_SFLG_FREE_SCHEDULED);
- ERTS_SMP_LC_ASSERT(erts_smp_atomic_read_nob(&prt->refc) == 0);
+ ERTS_LC_ASSERT(erts_smp_atomic_read_nob(&prt->refc) == 0);
+
+ ASSERT(prt->status & ERTS_PORT_SFLG_PORT_DEBUG);
+ ASSERT(!(prt->status & ERTS_PORT_SFLG_FREE));
+ prt->status = ERTS_PORT_SFLG_FREE;
+
+#ifdef ERTS_SMP
port_specific = (prt->status & ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK);
@@ -352,10 +354,6 @@ port_cleanup(Port *prt)
prt->lock = NULL;
- ASSERT(prt->status & ERTS_PORT_SFLG_PORT_DEBUG);
- ASSERT(!(prt->status & ERTS_PORT_SFLG_FREE));
- prt->status = ERTS_PORT_SFLG_FREE;
-
erts_smp_port_state_unlock(prt);
erts_smp_mtx_unlock(mtx);
@@ -606,10 +604,8 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */
/* Need to mark the port as free again */
erts_smp_port_state_lock(port);
port->status = ERTS_PORT_SFLG_FREE;
-#ifdef ERTS_SMP
- ERTS_SMP_LC_ASSERT(erts_smp_atomic_read_nob(&port->refc) == 2);
+ ERTS_LC_ASSERT(erts_smp_atomic_read_nob(&port->refc) == 2);
erts_smp_atomic_set_nob(&port->refc, 0);
-#endif
erts_smp_port_state_unlock(port);
return -3;
}
@@ -1314,7 +1310,7 @@ void init_io(void)
pdl_init();
- if (erts_sys_getenv("ERL_MAX_PORTS", maxports, &maxportssize) == 0)
+ if (erts_sys_getenv_raw("ERL_MAX_PORTS", maxports, &maxportssize) == 0)
erts_max_ports = atoi(maxports);
else
erts_max_ports = sys_max_files();
@@ -1330,7 +1326,7 @@ void init_io(void)
erts_max_ports = ERTS_MAX_R9_PORTS;
}
- port_extra_shift = erts_fit_in_bits(erts_max_ports - 1);
+ port_extra_shift = erts_fit_in_bits_int32(erts_max_ports - 1);
port_num_mask = (1 << ports_bits) - 1;
erts_port_tab_index_mask = ~(~((Uint) 0) << port_extra_shift);
@@ -1354,8 +1350,8 @@ void init_io(void)
for (i = 0; i < erts_max_ports; i++) {
erts_port_task_init_sched(&erts_port[i].sched);
-#ifdef ERTS_SMP
erts_smp_atomic_init_nob(&erts_port[i].refc, 0);
+#ifdef ERTS_SMP
erts_port[i].lock = NULL;
erts_port[i].xports = NULL;
erts_smp_spinlock_init_x(&erts_port[i].state_lck,
@@ -1594,6 +1590,7 @@ deliver_result(Eterm sender, Eterm pid, Eterm res)
{
Process *rp;
ErtsProcLocks rp_locks = 0;
+ int scheduler = erts_get_scheduler_id() != 0;
ERTS_SMP_CHK_NO_PROC_LOCKS;
@@ -1601,7 +1598,9 @@ deliver_result(Eterm sender, Eterm pid, Eterm res)
&& is_internal_pid(pid)
&& internal_pid_index(pid) < erts_max_processes);
- rp = erts_pid2proc_opt(NULL, 0, pid, 0, ERTS_P2P_FLG_SMP_INC_REFC);
+ rp = (scheduler
+ ? erts_proc_lookup(pid)
+ : erts_pid2proc_opt(NULL, 0, pid, 0, ERTS_P2P_FLG_SMP_INC_REFC));
if (rp) {
Eterm tuple;
@@ -1619,7 +1618,9 @@ deliver_result(Eterm sender, Eterm pid, Eterm res)
#endif
);
erts_smp_proc_unlock(rp, rp_locks);
- erts_smp_proc_dec_refc(rp);
+ if (!scheduler)
+ erts_smp_proc_dec_refc(rp);
+
}
}
@@ -1644,6 +1645,7 @@ static void deliver_read_message(Port* prt, Eterm to,
ErlHeapFragment *bp;
ErlOffHeap *ohp;
ErtsProcLocks rp_locks = 0;
+ int scheduler = erts_get_scheduler_id() != 0;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
ERTS_SMP_CHK_NO_PROC_LOCKS;
@@ -1658,7 +1660,10 @@ static void deliver_read_message(Port* prt, Eterm to,
need += 2*len;
}
- rp = erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC);
+ rp = (scheduler
+ ? erts_proc_lookup(to)
+ : erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC));
+
if (!rp)
return;
@@ -1711,8 +1716,10 @@ static void deliver_read_message(Port* prt, Eterm to,
, NIL
#endif
);
- erts_smp_proc_unlock(rp, rp_locks);
- erts_smp_proc_dec_refc(rp);
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+ if (!scheduler)
+ erts_smp_proc_dec_refc(rp);
}
/*
@@ -1779,6 +1786,7 @@ deliver_vec_message(Port* prt, /* Port */
ErlHeapFragment *bp;
ErlOffHeap *ohp;
ErtsProcLocks rp_locks = 0;
+ int scheduler = erts_get_scheduler_id() != 0;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
ERTS_SMP_CHK_NO_PROC_LOCKS;
@@ -1787,7 +1795,10 @@ deliver_vec_message(Port* prt, /* Port */
* Check arguments for validity.
*/
- rp = erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC);
+
+ rp = (scheduler
+ ? erts_proc_lookup(to)
+ : erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC));
if (!rp)
return;
@@ -1869,7 +1880,8 @@ deliver_vec_message(Port* prt, /* Port */
#endif
);
erts_smp_proc_unlock(rp, rp_locks);
- erts_smp_proc_dec_refc(rp);
+ if (!scheduler)
+ erts_smp_proc_dec_refc(rp);
}
@@ -2277,9 +2289,8 @@ void erts_port_command(Process *proc,
{
ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- Process* rp = erts_pid2proc_opt(NULL, 0,
- port->connected, rp_locks,
- ERTS_P2P_FLG_SMP_INC_REFC);
+ Process* rp = erts_pid2proc(NULL, 0,
+ port->connected, rp_locks);
if (rp) {
(void) erts_send_exit_signal(NULL,
port->id,
@@ -2290,7 +2301,6 @@ void erts_port_command(Process *proc,
NULL,
0);
erts_smp_proc_unlock(rp, rp_locks);
- erts_smp_proc_dec_refc(rp);
}
}
@@ -2841,13 +2851,17 @@ void driver_report_exit(int ix, int status)
ErlHeapFragment *bp = NULL;
ErlOffHeap *ohp;
ErtsProcLocks rp_locks = 0;
+ int scheduler = erts_get_scheduler_id() != 0;
ERTS_SMP_CHK_NO_PROC_LOCKS;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
pid = prt->connected;
ASSERT(is_internal_pid(pid));
- rp = erts_pid2proc_opt(NULL, 0, pid, 0, ERTS_P2P_FLG_SMP_INC_REFC);
+
+ rp = (scheduler
+ ? erts_proc_lookup(pid)
+ : erts_pid2proc_opt(NULL, 0, pid, 0, ERTS_P2P_FLG_SMP_INC_REFC));
if (!rp)
return;
@@ -2864,7 +2878,8 @@ void driver_report_exit(int ix, int status)
);
erts_smp_proc_unlock(rp, rp_locks);
- erts_smp_proc_dec_refc(rp);
+ if (!scheduler)
+ erts_smp_proc_dec_refc(rp);
}
@@ -2995,6 +3010,7 @@ driver_deliver_term(ErlDrvPort port,
ErlOffHeap *ohp;
ErtsProcLocks rp_locks = 0;
struct b2t_states__ b2t;
+ int scheduler = 1; /* Silence erroneous warning... */
init_b2t_states(&b2t);
@@ -3186,7 +3202,13 @@ driver_deliver_term(ErlDrvPort port,
if (res <= 0)
goto done;
- rp = erts_pid2proc_opt(NULL, 0, to, rp_locks, ERTS_P2P_FLG_SMP_INC_REFC);
+ /*
+ * Increase refc on proc if done from a non-scheduler thread.
+ */
+ scheduler = erts_get_scheduler_id() != 0;
+ rp = (scheduler
+ ? erts_proc_lookup(to)
+ : erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC));
if (!rp) {
res = 0;
goto done;
@@ -3438,7 +3460,8 @@ driver_deliver_term(ErlDrvPort port,
if (rp) {
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
- erts_smp_proc_dec_refc(rp);
+ if (!scheduler)
+ erts_smp_proc_dec_refc(rp);
}
#endif
cleanup_b2t_states(&b2t);
@@ -5227,11 +5250,11 @@ int null_func(void)
int
erl_drv_putenv(char *key, char *value)
{
- return erts_write_env(key, value);
+ return erts_sys_putenv_raw(key, value);
}
int
erl_drv_getenv(char *key, char *value, size_t *value_size)
{
- return erts_sys_getenv(key, value, value_size);
+ return erts_sys_getenv_raw(key, value, value_size);
}
diff --git a/erts/emulator/beam/module.c b/erts/emulator/beam/module.c
index b93b1ad09a..1ef71cda79 100644
--- a/erts/emulator/beam/module.c
+++ b/erts/emulator/beam/module.c
@@ -26,21 +26,32 @@
#include "global.h"
#include "module.h"
+#ifdef DEBUG
+# define IF_DEBUG(x) x
+#else
+# define IF_DEBUG(x)
+#endif
+
#define MODULE_SIZE 50
#define MODULE_LIMIT (64*1024)
-static IndexTable module_table;
+static IndexTable module_tables[ERTS_NUM_CODE_IX];
-/*
- * SMP note: We don't need to look accesses to the module table because
- * there is one only scheduler thread when we update it.
+erts_smp_rwmtx_t the_old_code_rwlocks[ERTS_NUM_CODE_IX];
+
+static erts_smp_atomic_t tot_module_bytes;
+
+/* SMP note: Active module table lookup and current module instance can be
+ * read without any locks. Old module instances are protected by
+ * "the_old_code_rwlocks" as purging is done on active module table.
+ * Staging table is protected by the "code_ix lock".
*/
#include "erl_smp.h"
void module_info(int to, void *to_arg)
{
- index_info(to, to_arg, &module_table);
+ index_info(to, to_arg, &module_tables[erts_active_code_ix()]);
}
@@ -59,45 +70,67 @@ static int module_cmp(Module* tmpl, Module* obj)
static Module* module_alloc(Module* tmpl)
{
Module* obj = (Module*) erts_alloc(ERTS_ALC_T_MODULE, sizeof(Module));
+ erts_smp_atomic_add_nob(&tot_module_bytes, sizeof(Module));
obj->module = tmpl->module;
- obj->code = 0;
- obj->old_code = 0;
- obj->code_length = 0;
- obj->old_code_length = 0;
+ obj->curr.code = 0;
+ obj->old.code = 0;
+ obj->curr.code_length = 0;
+ obj->old.code_length = 0;
obj->slot.index = -1;
- obj->nif = NULL;
- obj->old_nif = NULL;
+ obj->curr.nif = NULL;
+ obj->old.nif = NULL;
+ obj->curr.num_breakpoints = 0;
+ obj->old.num_breakpoints = 0;
+ obj->curr.num_traced_exports = 0;
+ obj->old.num_traced_exports = 0;
return obj;
}
+static void module_free(Module* mod)
+{
+ erts_free(ERTS_ALC_T_MODULE, mod);
+ erts_smp_atomic_add_nob(&tot_module_bytes, -sizeof(Module));
+}
void init_module_table(void)
{
HashFunctions f;
+ int i;
f.hash = (H_FUN) module_hash;
f.cmp = (HCMP_FUN) module_cmp;
f.alloc = (HALLOC_FUN) module_alloc;
- f.free = 0;
+ f.free = (HFREE_FUN) module_free;
+
+ for (i = 0; i < ERTS_NUM_CODE_IX; i++) {
+ erts_index_init(ERTS_ALC_T_MODULE_TABLE, &module_tables[i], "module_code",
+ MODULE_SIZE, MODULE_LIMIT, f);
+ }
- erts_index_init(ERTS_ALC_T_MODULE_TABLE, &module_table, "module_code",
- MODULE_SIZE, MODULE_LIMIT, f);
+ for (i=0; i<ERTS_NUM_CODE_IX; i++) {
+ erts_smp_rwmtx_init_x(&the_old_code_rwlocks[i], "old_code", make_small(i));
+ }
+ erts_smp_atomic_init_nob(&tot_module_bytes, 0);
}
Module*
-erts_get_module(Eterm mod)
+erts_get_module(Eterm mod, ErtsCodeIndex code_ix)
{
Module e;
int index;
+ IndexTable* mod_tab;
ASSERT(is_atom(mod));
+
+ mod_tab = &module_tables[code_ix];
+
e.module = atom_val(mod);
- index = index_get(&module_table, (void*) &e);
+ index = index_get(mod_tab, (void*) &e);
if (index == -1) {
return NULL;
} else {
- return (Module*) erts_index_lookup(&module_table, index);
+ return (Module*) erts_index_lookup(mod_tab, index);
}
}
@@ -105,27 +138,101 @@ Module*
erts_put_module(Eterm mod)
{
Module e;
- int index;
+ IndexTable* mod_tab;
+ int oldsz, newsz;
+ Module* res;
ASSERT(is_atom(mod));
ERTS_SMP_LC_ASSERT(erts_initialized == 0
- || erts_smp_thr_progress_is_blocking());
+ || erts_is_code_ix_locked());
+
+ mod_tab = &module_tables[erts_staging_code_ix()];
e.module = atom_val(mod);
- index = index_put(&module_table, (void*) &e);
- return (Module*) erts_index_lookup(&module_table, index);
+ oldsz = index_table_sz(mod_tab);
+ res = (Module*) index_put_entry(mod_tab, (void*) &e);
+ newsz = index_table_sz(mod_tab);
+ erts_smp_atomic_add_nob(&tot_module_bytes, (newsz - oldsz));
+ return res;
}
-Module *module_code(int i)
+Module *module_code(int i, ErtsCodeIndex code_ix)
{
- return (Module*) erts_index_lookup(&module_table, i);
+ return (Module*) erts_index_lookup(&module_tables[code_ix], i);
}
-int module_code_size(void)
+int module_code_size(ErtsCodeIndex code_ix)
{
- return module_table.entries;
+ return module_tables[code_ix].entries;
}
int module_table_sz(void)
{
- return index_table_sz(&module_table);
+ return erts_smp_atomic_read_nob(&tot_module_bytes);
+}
+
+#ifdef DEBUG
+static ErtsCodeIndex dbg_load_code_ix = 0;
+#endif
+
+static int entries_at_start_staging = 0;
+
+void module_start_staging(void)
+{
+ IndexTable* src = &module_tables[erts_active_code_ix()];
+ IndexTable* dst = &module_tables[erts_staging_code_ix()];
+ Module* src_mod;
+ Module* dst_mod;
+ int i, oldsz, newsz;
+
+ ASSERT(dbg_load_code_ix == -1);
+ ASSERT(dst->entries <= src->entries);
+
+ /*
+ * Make sure our existing modules are up-to-date
+ */
+ for (i = 0; i < dst->entries; i++) {
+ src_mod = (Module*) erts_index_lookup(src, i);
+ dst_mod = (Module*) erts_index_lookup(dst, i);
+ ASSERT(src_mod->module == dst_mod->module);
+
+ dst_mod->curr = src_mod->curr;
+ dst_mod->old = src_mod->old;
+ }
+
+ /*
+ * Copy all new modules from active table
+ */
+ oldsz = index_table_sz(dst);
+ for (i = dst->entries; i < src->entries; i++) {
+ src_mod = (Module*) erts_index_lookup(src, i);
+ dst_mod = (Module*) index_put_entry(dst, src_mod);
+ ASSERT(dst_mod != src_mod);
+
+ dst_mod->curr = src_mod->curr;
+ dst_mod->old = src_mod->old;
+ }
+ newsz = index_table_sz(dst);
+ erts_smp_atomic_add_nob(&tot_module_bytes, (newsz - oldsz));
+
+ entries_at_start_staging = dst->entries;
+ IF_DEBUG(dbg_load_code_ix = erts_staging_code_ix());
+}
+
+void module_end_staging(int commit)
+{
+ ASSERT(dbg_load_code_ix == erts_staging_code_ix());
+
+ if (!commit) { /* abort */
+ IndexTable* tab = &module_tables[erts_staging_code_ix()];
+ int oldsz, newsz;
+
+ ASSERT(entries_at_start_staging <= tab->entries);
+ oldsz = index_table_sz(tab);
+ index_erase_latest_from(tab, entries_at_start_staging);
+ newsz = index_table_sz(tab);
+ erts_smp_atomic_add_nob(&tot_module_bytes, (newsz - oldsz));
+ }
+
+ IF_DEBUG(dbg_load_code_ix = -1);
}
+
diff --git a/erts/emulator/beam/module.h b/erts/emulator/beam/module.h
index 694e4ab72f..6e123a0ffe 100644
--- a/erts/emulator/beam/module.h
+++ b/erts/emulator/beam/module.h
@@ -24,28 +24,72 @@
#include "index.h"
#endif
+struct erl_module_instance {
+ BeamInstr* code;
+ int code_length; /* Length of loaded code in bytes. */
+ unsigned catches;
+ struct erl_module_nif* nif;
+ int num_breakpoints;
+ int num_traced_exports;
+};
typedef struct erl_module {
IndexSlot slot; /* Must be located at top of struct! */
int module; /* Atom index for module (not tagged). */
- BeamInstr* code;
- BeamInstr* old_code;
- int code_length; /* Length of loaded code in bytes. */
- int old_code_length; /* Length of old loaded code in bytes */
- unsigned catches, old_catches;
- struct erl_module_nif* nif;
- struct erl_module_nif* old_nif;
+ struct erl_module_instance curr;
+ struct erl_module_instance old; /* protected by "old_code" rwlock */
} Module;
-Module* erts_get_module(Eterm mod);
+Module* erts_get_module(Eterm mod, ErtsCodeIndex code_ix);
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 *);
-Module *module_code(int);
-int module_code_size(void);
+Module *module_code(int, ErtsCodeIndex);
+int module_code_size(ErtsCodeIndex);
int module_table_sz(void);
+ERTS_GLB_INLINE void erts_rwlock_old_code(ErtsCodeIndex);
+ERTS_GLB_INLINE void erts_rwunlock_old_code(ErtsCodeIndex);
+ERTS_GLB_INLINE void erts_rlock_old_code(ErtsCodeIndex);
+ERTS_GLB_INLINE void erts_runlock_old_code(ErtsCodeIndex);
+#ifdef ERTS_ENABLE_LOCK_CHECK
+int erts_is_old_code_rlocked(ErtsCodeIndex);
+#endif
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+extern erts_smp_rwmtx_t the_old_code_rwlocks[ERTS_NUM_CODE_IX];
+
+ERTS_GLB_INLINE void erts_rwlock_old_code(ErtsCodeIndex code_ix)
+{
+ erts_smp_rwmtx_rwlock(&the_old_code_rwlocks[code_ix]);
+}
+ERTS_GLB_INLINE void erts_rwunlock_old_code(ErtsCodeIndex code_ix)
+{
+ erts_smp_rwmtx_rwunlock(&the_old_code_rwlocks[code_ix]);
+}
+ERTS_GLB_INLINE void erts_rlock_old_code(ErtsCodeIndex code_ix)
+{
+ erts_smp_rwmtx_rlock(&the_old_code_rwlocks[code_ix]);
+}
+ERTS_GLB_INLINE void erts_runlock_old_code(ErtsCodeIndex code_ix)
+{
+ erts_smp_rwmtx_runlock(&the_old_code_rwlocks[code_ix]);
+}
+
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ERTS_GLB_INLINE int erts_is_old_code_rlocked(ErtsCodeIndex code_ix)
+{
+ return erts_smp_lc_rwmtx_is_rlocked(&the_old_code_rwlocks[code_ix]);
+}
#endif
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+
+#endif /* !__MODULE_H__ */
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index 9b168889dd..6764e88c81 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -62,11 +62,8 @@ label L
i_func_info I a a I
int_code_end
-i_trace_breakpoint
-i_mtrace_breakpoint
+i_generic_breakpoint
i_debug_breakpoint
-i_count_breakpoint
-i_time_breakpoint
i_return_time_trace
i_return_to_trace
i_yield
@@ -522,7 +519,6 @@ apply_bif
call_nif
call_error_handler
error_action_code
-call_traced_function
return_trace
#
@@ -829,16 +825,20 @@ call_ext_only Ar=u==2 Bif=u$bif:erlang:load_nif/2 => allocate u Ar | i_call_ext
#
-# The apply/2 and apply/3 BIFs are instructions.
+# apply/2 is an instruction, not a BIF.
#
call_ext u==2 u$func:erlang:apply/2 => i_apply_fun
call_ext_last u==2 u$func:erlang:apply/2 D => i_apply_fun_last D
call_ext_only u==2 u$func:erlang:apply/2 => i_apply_fun_only
-call_ext u==3 u$func:erlang:apply/3 => i_apply
-call_ext_last u==3 u$func:erlang:apply/3 D => i_apply_last D
-call_ext_only u==3 u$func:erlang:apply/3 => i_apply_only
+#
+# The apply/3 BIF is an instruction.
+#
+
+call_ext u==3 u$bif:erlang:apply/3 => i_apply
+call_ext_last u==3 u$bif:erlang:apply/3 D => i_apply_last D
+call_ext_only u==3 u$bif:erlang:apply/3 => i_apply_only
#
# The exit/1 and throw/1 BIFs never execute the instruction following them;
diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c
index 26d64887d0..c02872ef80 100644
--- a/erts/emulator/beam/register.c
+++ b/erts/emulator/beam/register.c
@@ -93,19 +93,14 @@ reg_safe_write_lock(Process *c_p, ErtsProcLocks *c_p_locks)
reg_write_lock();
}
+#endif
+
static ERTS_INLINE int
is_proc_alive(Process *p)
{
- int res;
- erts_pix_lock_t *pixlck = ERTS_PID2PIXLOCK(p->id);
- erts_pix_lock(pixlck);
- res = !p->is_exiting;
- erts_pix_unlock(pixlck);
- return res;
+ return !ERTS_PROC_IS_EXITING(p);
}
-#endif
-
void register_info(int to, void *to_arg)
{
int lock = !ERTS_IS_CRASH_DUMPING;
@@ -389,8 +384,7 @@ erts_whereis_name(Process *c_p,
}
#else
if (rp->p
- && ((flags & ERTS_P2P_FLG_ALLOW_OTHER_X)
- || rp->p->status != P_EXITING))
+ && ((flags & ERTS_P2P_FLG_ALLOW_OTHER_X) || is_proc_alive(rp->p)))
*proc = rp->p;
else
*proc = NULL;
@@ -402,7 +396,9 @@ erts_whereis_name(Process *c_p,
if (!rp || !rp->pt)
*port = NULL;
else {
-#ifdef ERTS_SMP
+#ifndef ERTS_SMP
+ erts_smp_atomic_inc_nob(&rp->pt->refc);
+#else
if (pending_port == rp->pt)
pending_port = NULL;
else {
@@ -509,9 +505,11 @@ int erts_unregister_name(Process *c_p,
if ((rp = (RegProc*) hash_get(&process_reg, (void*) &r)) != NULL) {
if (rp->pt) {
if (port != rp->pt) {
-#ifdef ERTS_SMP
+#ifndef ERTS_SMP
+ erts_smp_atomic_inc_nob(&rp->pt->refc);
+#else
if (port) {
- ERTS_SMP_LC_ASSERT(port != c_prt);
+ ASSERT(port != c_prt);
erts_smp_port_unlock(port);
port = NULL;
}
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 7b2bb81f62..0e6bec352e 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -25,11 +25,6 @@
# define NO_FPE_SIGNALS
#endif
-/* xxxP __VXWORKS__ */
-#ifdef VXWORKS
-#include <vxWorks.h>
-#endif
-
#ifdef DISABLE_CHILD_WAITER_THREAD
#undef ENABLE_CHILD_WAITER_THREAD
#endif
@@ -39,10 +34,10 @@
#define ENABLE_CHILD_WAITER_THREAD 1
#endif
+#define ERTS_I64_LITERAL(X) X##LL
+
#if defined (__WIN32__)
# include "erl_win_sys.h"
-#elif defined (VXWORKS)
-# include "erl_vxworks_sys.h"
#else
# include "erl_unix_sys.h"
#ifndef UNIX
@@ -91,14 +86,22 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType;
# endif
#endif
-#ifdef __GNUC__
-# if __GNUC__ < 3 && (__GNUC__ != 2 || __GNUC_MINOR__ < 96)
-# define ERTS_LIKELY(BOOL) (BOOL)
-# define ERTS_UNLIKELY(BOOL) (BOOL)
-# else
-# define ERTS_LIKELY(BOOL) __builtin_expect((BOOL), !0)
-# define ERTS_UNLIKELY(BOOL) __builtin_expect((BOOL), 0)
-# 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)
#else
# define ERTS_LIKELY(BOOL) (BOOL)
# define ERTS_UNLIKELY(BOOL) (BOOL)
@@ -172,10 +175,16 @@ void erl_assert_error(char* expr, char* file, int line);
# define const
#endif
-#ifdef VXWORKS
-/* Replace VxWorks' printf with a real one that does fprintf(stdout, ...) */
-int real_printf(const char *fmt, ...);
-# define printf real_printf
+#undef __deprecated
+#if ERTS_AT_LEAST_GCC_VSN__(3, 0, 0)
+# define __deprecated __attribute__((deprecated))
+#else
+# define __deprecated
+#endif
+#if ERTS_AT_LEAST_GCC_VSN__(3, 0, 4)
+# define erts_align_attribute(SZ) __attribute__ ((aligned (SZ)))
+#else
+# define erts_align_attribute(SZ)
#endif
/* In VC++, noreturn is a declspec that has to be before the types,
@@ -185,12 +194,6 @@ int real_printf(const char *fmt, ...);
#if __GNUC__
# define __decl_noreturn
# define __noreturn __attribute__((noreturn))
-# undef __deprecated
-# if __GNUC__ >= 3
-# define __deprecated __attribute__((deprecated))
-# else
-# define __deprecated
-# endif
#else
# if defined(__WIN32__) && defined(_MSC_VER)
# define __noreturn
@@ -199,7 +202,6 @@ int real_printf(const char *fmt, ...);
# define __noreturn
# define __decl_noreturn
# endif
-# define __deprecated
#endif
/*
@@ -471,38 +473,28 @@ static unsigned long zero_value = 0, one_value = 1;
# define SET_NONBLOCKING(fd) ioctlsocket((fd), FIONBIO, &one_value)
# else
-# ifdef VXWORKS
-# include <fcntl.h> /* xxxP added for O_WRONLY etc ... macro:s ... */
-# include <ioLib.h>
-static const int zero_value = 0, one_value = 1;
-# define SET_BLOCKING(fd) ioctl((fd), FIONBIO, (int)&zero_value)
-# define SET_NONBLOCKING(fd) ioctl((fd), FIONBIO, (int)&one_value)
-# define ERRNO_BLOCK EWOULDBLOCK
-
-# else
-# ifdef NB_FIONBIO /* Old BSD */
-# include <sys/ioctl.h>
+# ifdef NB_FIONBIO /* Old BSD */
+# include <sys/ioctl.h>
static const int zero_value = 0, one_value = 1;
-# define SET_BLOCKING(fd) ioctl((fd), FIONBIO, &zero_value)
-# define SET_NONBLOCKING(fd) ioctl((fd), FIONBIO, &one_value)
-# define ERRNO_BLOCK EWOULDBLOCK
-# else /* !NB_FIONBIO */
-# include <fcntl.h>
-# ifdef NB_O_NDELAY /* Nothing needs this? */
-# define NB_FLAG O_NDELAY
-# ifndef ERRNO_BLOCK /* allow override (e.g. EAGAIN) via Makefile */
-# define ERRNO_BLOCK EWOULDBLOCK
-# endif
-# else /* !NB_O_NDELAY */ /* The True Way - POSIX!:-) */
-# define NB_FLAG O_NONBLOCK
-# define ERRNO_BLOCK EAGAIN
-# endif /* !NB_O_NDELAY */
-# define SET_BLOCKING(fd) fcntl((fd), F_SETFL, \
- fcntl((fd), F_GETFL, 0) & ~NB_FLAG)
-# define SET_NONBLOCKING(fd) fcntl((fd), F_SETFL, \
- fcntl((fd), F_GETFL, 0) | NB_FLAG)
-# endif /* !NB_FIONBIO */
-# endif /* _WXWORKS_ */
+# define SET_BLOCKING(fd) ioctl((fd), FIONBIO, &zero_value)
+# define SET_NONBLOCKING(fd) ioctl((fd), FIONBIO, &one_value)
+# define ERRNO_BLOCK EWOULDBLOCK
+# else /* !NB_FIONBIO */
+# include <fcntl.h>
+# ifdef NB_O_NDELAY /* Nothing needs this? */
+# define NB_FLAG O_NDELAY
+# ifndef ERRNO_BLOCK /* allow override (e.g. EAGAIN) via Makefile */
+# define ERRNO_BLOCK EWOULDBLOCK
+# endif
+# else /* !NB_O_NDELAY */ /* The True Way - POSIX!:-) */
+# define NB_FLAG O_NONBLOCK
+# define ERRNO_BLOCK EAGAIN
+# endif /* !NB_O_NDELAY */
+# define SET_BLOCKING(fd) fcntl((fd), F_SETFL, \
+ fcntl((fd), F_GETFL, 0) & ~NB_FLAG)
+# define SET_NONBLOCKING(fd) fcntl((fd), F_SETFL, \
+ fcntl((fd), F_GETFL, 0) | NB_FLAG)
+# endif /* !NB_FIONBIO */
# endif /* !__WIN32__ */
#endif /* WANT_NONBLOCKING */
@@ -699,22 +691,25 @@ void fini_getenv_state(GETENV_STATE *);
/* xxxP */
void init_sys_float(void);
int sys_chars_to_double(char*, double*);
-int sys_double_to_chars(double, char*);
-void sys_get_pid(char *);
+int sys_double_to_chars(double, char*, size_t);
+void sys_get_pid(char *, size_t);
/* erts_sys_putenv() returns, 0 on success and a value != 0 on failure. */
-int erts_sys_putenv(char *key_value, int sep_ix);
+int erts_sys_putenv(char *key, char *value);
+/* Simple variant used from drivers, raw eightbit interface */
+int erts_sys_putenv_raw(char *key, char *value);
/* erts_sys_getenv() returns 0 on success (length of value string in
*size), a value > 0 if value buffer is too small (*size is set to needed
size), and a value < 0 on failure. */
int erts_sys_getenv(char *key, char *value, size_t *size);
+/* Simple variant used from drivers, raw eightbit interface */
+int erts_sys_getenv_raw(char *key, char *value, size_t *size);
/* erts_sys_getenv__() is only allowed to be used in early init phase */
int erts_sys_getenv__(char *key, char *value, size_t *size);
/* Easier to use, but not as efficient, environment functions */
char *erts_read_env(char *key);
void erts_free_read_env(void *value);
-int erts_write_env(char *key, char *value);
/* utils.c */
@@ -855,13 +850,6 @@ erts_refc_read(erts_refc_t *refcp, erts_aint_t min_val)
extern int erts_use_kernel_poll;
#endif
-#if defined(VXWORKS)
-/* NOTE! sys_calloc2 does not exist on other
- platforms than VxWorks and OSE */
-void* sys_calloc2(Uint, Uint);
-#endif /* VXWORKS || OSE */
-
-
#define sys_memcpy(s1,s2,n) memcpy(s1,s2,n)
#define sys_memmove(s1,s2,n) memmove(s1,s2,n)
#define sys_memcmp(s1,s2,n) memcmp(s1,s2,n)
@@ -968,43 +956,6 @@ void erl_bin_write(unsigned char *, int, int);
# define DEBUGF(x)
#endif
-
-#ifdef VXWORKS
-/* This includes redefines of malloc etc
- this should be done after sys_alloc, etc, above */
-# include "reclaim.h"
-/*********************Malloc and friends************************
- * There is a problem with the naming of malloc and friends,
- * malloc is used throughout sys.c and the resolver to mean save_alloc,
- * but it should actually mean either sys_alloc or sys_alloc2,
- * so the definitions from reclaim_master.h are not any
- * good, i redefine the malloc family here, although it's quite
- * ugly, actually it would be preferrable to use the
- * names sys_alloc and so on throughout the offending code, but
- * that will be saved as an later exercise...
- * I also add an own calloc, to make the BSD resolver source happy.
- ***************************************************************/
-/* Undefine malloc and friends */
-# ifdef malloc
-# undef malloc
-# endif
-# ifdef calloc
-# undef calloc
-# endif
-# ifdef realloc
-# undef realloc
-# endif
-# ifdef free
-# undef free
-# endif
-/* Redefine malloc and friends */
-# define malloc sys_alloc
-# define calloc sys_calloc
-# define realloc sys_realloc
-# define free sys_free
-
-#endif
-
#ifdef __WIN32__
#ifdef ARCH_64
#define ERTS_ALLOC_ALIGN_BYTES 16
@@ -1020,23 +971,20 @@ void erl_bin_write(unsigned char *, int, int);
#ifdef __WIN32__
-
void call_break_handler(void);
char* last_error(void);
char* win32_errorstr(int);
-
-
#endif
/************************************************************************
* Find out the native filename encoding of the process (look at locale of
* Unix processes and just do UTF16 on windows
************************************************************************/
-#define ERL_FILENAME_UNKNOWN 0
-#define ERL_FILENAME_LATIN1 1
-#define ERL_FILENAME_UTF8 2
-#define ERL_FILENAME_UTF8_MAC 3
-#define ERL_FILENAME_WIN_WCHAR 4
+#define ERL_FILENAME_UNKNOWN (0)
+#define ERL_FILENAME_LATIN1 (1)
+#define ERL_FILENAME_UTF8 (2)
+#define ERL_FILENAME_UTF8_MAC (3)
+#define ERL_FILENAME_WIN_WCHAR (4)
int erts_get_native_filename_encoding(void);
/* The set function is only to be used by erl_init! */
@@ -1046,4 +994,3 @@ int erts_get_user_requested_filename_encoding(void);
void erts_init_sys_common_misc(void);
#endif
-
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index a36d15204e..1969fc762c 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -268,16 +268,42 @@ list_length(Eterm list)
return i;
}
-Uint erts_fit_in_bits(Uint n)
+static const struct {
+ Sint64 mask;
+ int bits;
+} fib_data[] = {{ERTS_I64_LITERAL(0x2), 1},
+ {ERTS_I64_LITERAL(0xc), 2},
+ {ERTS_I64_LITERAL(0xf0), 4},
+ {ERTS_I64_LITERAL(0xff00), 8},
+ {ERTS_I64_LITERAL(0xffff0000), 16},
+ {ERTS_I64_LITERAL(0xffffffff00000000), 32}};
+
+static ERTS_INLINE int
+fit_in_bits(Sint64 value, int start)
{
- Uint i;
+ int bits = 0;
+ int i;
- i = 0;
- while (n > 0) {
- i++;
- n >>= 1;
- }
- return i;
+ for (i = start; i >= 0; i--) {
+ if (value & fib_data[i].mask) {
+ value >>= fib_data[i].bits;
+ bits |= fib_data[i].bits;
+ }
+ }
+
+ bits++;
+
+ return bits;
+}
+
+int erts_fit_in_bits_int64(Sint64 value)
+{
+ return fit_in_bits(value, 5);
+}
+
+int erts_fit_in_bits_int32(Sint32 value)
+{
+ return fit_in_bits((Sint64) (Uint32) value, 4);
}
int
@@ -1640,12 +1666,20 @@ static int do_send_to_logger(Eterm tag, Eterm gleader, char *buf, int len)
}
#ifndef ERTS_SMP
- if (
#ifdef USE_THREADS
- !erts_get_scheduler_data() || /* Must be scheduler thread */
+ p = NULL;
+ if (erts_get_scheduler_data()) /* Must be scheduler thread */
#endif
- (p = erts_whereis_process(NULL, 0, am_error_logger, 0, 0)) == NULL
- || p->status == P_RUNNING) {
+ {
+ p = erts_whereis_process(NULL, 0, am_error_logger, 0, 0);
+ if (p) {
+ erts_aint32_t state = erts_smp_atomic32_read_acqb(&p->state);
+ if (state & ERTS_PSFLG_RUNNING)
+ p = NULL;
+ }
+ }
+
+ if (!p) {
/* buf *always* points to a null terminated string */
erts_fprintf(stderr, "(no error logger present) %T: \"%s\"\n",
tag, buf);
@@ -3240,7 +3274,7 @@ ptimer_timeout(ErtsSmpPTimer *ptimer)
ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS,
ERTS_P2P_FLG_ALLOW_OTHER_X);
if (p) {
- if (!p->is_exiting
+ if (!ERTS_PROC_IS_EXITING(p)
&& !(ptimer->timer.flags & ERTS_PTMR_FLG_CANCELLED)) {
ASSERT(*ptimer->timer.timer_ref == ptimer);
*ptimer->timer.timer_ref = NULL;
@@ -3409,7 +3443,7 @@ erts_read_env(char *key)
char *value = erts_alloc(ERTS_ALC_T_TMP, value_len);
int res;
while (1) {
- res = erts_sys_getenv(key, value, &value_len);
+ res = erts_sys_getenv_raw(key, value, &value_len);
if (res <= 0)
break;
value = erts_realloc(ERTS_ALC_T_TMP, value, value_len);
@@ -3428,28 +3462,6 @@ erts_free_read_env(void *value)
erts_free(ERTS_ALC_T_TMP, value);
}
-int
-erts_write_env(char *key, char *value)
-{
- int ix, res;
- size_t key_len = sys_strlen(key), value_len = sys_strlen(value);
- char *key_value = erts_alloc_fnf(ERTS_ALC_T_TMP,
- key_len + 1 + value_len + 1);
- if (!key_value) {
- errno = ENOMEM;
- return -1;
- }
- sys_memcpy((void *) key_value, (void *) key, key_len);
- ix = key_len;
- key_value[ix++] = '=';
- sys_memcpy((void *) key_value, (void *) value, value_len);
- ix += value_len;
- key_value[ix] = '\0';
- res = erts_sys_putenv(key_value, key_len);
- erts_free(ERTS_ALC_T_TMP, key_value);
- return res;
-}
-
/*
* To be used to silence unused result warnings, but do not abuse it.
*/
@@ -3458,6 +3470,254 @@ void erts_silence_warn_unused_result(long unused)
}
+/*
+ * Interval counts
+ */
+void
+erts_interval_init(erts_interval_t *icp)
+{
+#ifdef ARCH_64
+ erts_atomic_init_nob(&icp->counter.atomic, 0);
+#else
+ erts_dw_aint_t dw;
+#ifdef ETHR_SU_DW_NAINT_T__
+ dw.dw_sint = 0;
+#else
+ dw.sint[ERTS_DW_AINT_HIGH_WORD] = 0;
+ dw.sint[ERTS_DW_AINT_LOW_WORD] = 0;
+#endif
+ erts_dw_atomic_init_nob(&icp->counter.atomic, &dw);
+
+#endif
+#ifdef DEBUG
+ icp->smp_api = 0;
+#endif
+}
+
+void
+erts_smp_interval_init(erts_interval_t *icp)
+{
+#ifdef ERTS_SMP
+ erts_interval_init(icp);
+#else
+ icp->counter.not_atomic = 0;
+#endif
+#ifdef DEBUG
+ icp->smp_api = 1;
+#endif
+}
+
+static ERTS_INLINE Uint64
+step_interval_nob(erts_interval_t *icp)
+{
+#ifdef ARCH_64
+ return (Uint64) erts_atomic_inc_read_nob(&icp->counter.atomic);
+#else
+ erts_dw_aint_t exp;
+
+ erts_dw_atomic_read_nob(&icp->counter.atomic, &exp);
+ while (1) {
+ erts_dw_aint_t new = exp;
+
+#ifdef ETHR_SU_DW_NAINT_T__
+ new.dw_sint++;
+#else
+ new.sint[ERTS_DW_AINT_LOW_WORD]++;
+ if (new.sint[ERTS_DW_AINT_LOW_WORD] == 0)
+ new.sint[ERTS_DW_AINT_HIGH_WORD]++;
+#endif
+
+ if (erts_dw_atomic_cmpxchg_nob(&icp->counter.atomic, &new, &exp))
+ return erts_interval_dw_aint_to_val__(&new);
+
+ }
+#endif
+}
+
+static ERTS_INLINE Uint64
+step_interval_relb(erts_interval_t *icp)
+{
+#ifdef ARCH_64
+ return (Uint64) erts_atomic_inc_read_relb(&icp->counter.atomic);
+#else
+ erts_dw_aint_t exp;
+
+ erts_dw_atomic_read_nob(&icp->counter.atomic, &exp);
+ while (1) {
+ erts_dw_aint_t new = exp;
+
+#ifdef ETHR_SU_DW_NAINT_T__
+ new.dw_sint++;
+#else
+ new.sint[ERTS_DW_AINT_LOW_WORD]++;
+ if (new.sint[ERTS_DW_AINT_LOW_WORD] == 0)
+ new.sint[ERTS_DW_AINT_HIGH_WORD]++;
+#endif
+
+ if (erts_dw_atomic_cmpxchg_relb(&icp->counter.atomic, &new, &exp))
+ return erts_interval_dw_aint_to_val__(&new);
+
+ }
+#endif
+}
+
+
+static ERTS_INLINE Uint64
+ensure_later_interval_nob(erts_interval_t *icp, Uint64 ic)
+{
+ Uint64 curr_ic;
+#ifdef ARCH_64
+ curr_ic = (Uint64) erts_atomic_read_nob(&icp->counter.atomic);
+ if (curr_ic > ic)
+ return curr_ic;
+ return (Uint64) erts_atomic_inc_read_nob(&icp->counter.atomic);
+#else
+ erts_dw_aint_t exp;
+
+ erts_dw_atomic_read_nob(&icp->counter.atomic, &exp);
+ curr_ic = erts_interval_dw_aint_to_val__(&exp);
+ if (curr_ic > ic)
+ return curr_ic;
+
+ while (1) {
+ erts_dw_aint_t new = exp;
+
+#ifdef ETHR_SU_DW_NAINT_T__
+ new.dw_sint++;
+#else
+ new.sint[ERTS_DW_AINT_LOW_WORD]++;
+ if (new.sint[ERTS_DW_AINT_LOW_WORD] == 0)
+ new.sint[ERTS_DW_AINT_HIGH_WORD]++;
+#endif
+
+ if (erts_dw_atomic_cmpxchg_nob(&icp->counter.atomic, &new, &exp))
+ return erts_interval_dw_aint_to_val__(&new);
+
+ curr_ic = erts_interval_dw_aint_to_val__(&exp);
+ if (curr_ic > ic)
+ return curr_ic;
+ }
+#endif
+}
+
+
+static ERTS_INLINE Uint64
+ensure_later_interval_acqb(erts_interval_t *icp, Uint64 ic)
+{
+ Uint64 curr_ic;
+#ifdef ARCH_64
+ curr_ic = (Uint64) erts_atomic_read_acqb(&icp->counter.atomic);
+ if (curr_ic > ic)
+ return curr_ic;
+ return (Uint64) erts_atomic_inc_read_acqb(&icp->counter.atomic);
+#else
+ erts_dw_aint_t exp;
+
+ erts_dw_atomic_read_acqb(&icp->counter.atomic, &exp);
+ curr_ic = erts_interval_dw_aint_to_val__(&exp);
+ if (curr_ic > ic)
+ return curr_ic;
+
+ while (1) {
+ erts_dw_aint_t new = exp;
+
+#ifdef ETHR_SU_DW_NAINT_T__
+ new.dw_sint++;
+#else
+ new.sint[ERTS_DW_AINT_LOW_WORD]++;
+ if (new.sint[ERTS_DW_AINT_LOW_WORD] == 0)
+ new.sint[ERTS_DW_AINT_HIGH_WORD]++;
+#endif
+
+ if (erts_dw_atomic_cmpxchg_acqb(&icp->counter.atomic, &new, &exp))
+ return erts_interval_dw_aint_to_val__(&new);
+
+ curr_ic = erts_interval_dw_aint_to_val__(&exp);
+ if (curr_ic > ic)
+ return curr_ic;
+ }
+#endif
+}
+
+Uint64
+erts_step_interval_nob(erts_interval_t *icp)
+{
+ ASSERT(!icp->smp_api);
+ return step_interval_nob(icp);
+}
+
+Uint64
+erts_step_interval_relb(erts_interval_t *icp)
+{
+ ASSERT(!icp->smp_api);
+ return step_interval_relb(icp);
+}
+
+Uint64
+erts_smp_step_interval_nob(erts_interval_t *icp)
+{
+ ASSERT(icp->smp_api);
+#ifdef ERTS_SMP
+ return step_interval_nob(icp);
+#else
+ return ++icp->counter.not_atomic;
+#endif
+}
+
+Uint64
+erts_smp_step_interval_relb(erts_interval_t *icp)
+{
+ ASSERT(icp->smp_api);
+#ifdef ERTS_SMP
+ return step_interval_relb(icp);
+#else
+ return ++icp->counter.not_atomic;
+#endif
+}
+
+Uint64
+erts_ensure_later_interval_nob(erts_interval_t *icp, Uint64 ic)
+{
+ ASSERT(!icp->smp_api);
+ return ensure_later_interval_nob(icp, ic);
+}
+
+Uint64
+erts_ensure_later_interval_acqb(erts_interval_t *icp, Uint64 ic)
+{
+ ASSERT(!icp->smp_api);
+ return ensure_later_interval_acqb(icp, ic);
+}
+
+Uint64
+erts_smp_ensure_later_interval_nob(erts_interval_t *icp, Uint64 ic)
+{
+ ASSERT(icp->smp_api);
+#ifdef ERTS_SMP
+ return ensure_later_interval_nob(icp, ic);
+#else
+ if (icp->counter.not_atomic > ic)
+ return icp->counter.not_atomic;
+ else
+ return ++icp->counter.not_atomic;
+#endif
+}
+
+Uint64
+erts_smp_ensure_later_interval_acqb(erts_interval_t *icp, Uint64 ic)
+{
+ ASSERT(icp->smp_api);
+#ifdef ERTS_SMP
+ return ensure_later_interval_acqb(icp, ic);
+#else
+ if (icp->counter.not_atomic > ic)
+ return icp->counter.not_atomic;
+ else
+ return ++icp->counter.not_atomic;
+#endif
+}
+
+
#ifdef DEBUG
/*
* Handy functions when using a debugger - don't use in the code!
@@ -3490,7 +3750,7 @@ Process *p;
void ppi(Eterm pid)
{
- pp(erts_pid2proc_unlocked(pid));
+ pp(erts_proc_lookup(pid));
}
void td(Eterm x)
diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c
index 603d1d47b6..912f5d3d8b 100644
--- a/erts/emulator/drivers/common/efile_drv.c
+++ b/erts/emulator/drivers/common/efile_drv.c
@@ -206,6 +206,9 @@ dt_private *get_dt_private(int);
# define KEY(desc) (&(desc)->key)
#endif
+#ifndef MAX
+# define MAX(x, y) (((x) > (y)) ? (x) : (y))
+#endif
#ifdef FILENAMES_16BIT
#ifdef USE_VM_PROBES
@@ -522,7 +525,7 @@ static void *ef_safe_alloc(Uint s)
static void *ef_safe_realloc(void *op, Uint s)
{
void *p = EF_REALLOC(op, s);
- if (!p) erl_exit(1, "efile drv: Can't reallocate %d bytes of memory\n", s);
+ if (!p) erl_exit(1, "efile drv: Can't reallocate %lu bytes of memory\n", (unsigned long)s);
return p;
}
@@ -2597,7 +2600,7 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
#ifdef USE_VM_PROBES
dt_s1 = d->b;
dt_s2 = d->b + namelen;
- dt_utag = buf + namelen + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
+ dt_utag = buf + namelen + FILENAME_BYTELEN(new_name) + FILENAME_CHARSIZE;
#endif
d->flags = desc->flags;
d->fd = fd;
@@ -2848,8 +2851,9 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
case FILE_READLINK:
{
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + RESBUFSIZE + 1);
-
+ d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 +
+ MAX(RESBUFSIZE, (FILENAME_BYTELEN(name) +
+ FILENAME_CHARSIZE)) + 1);
FILENAME_COPY(d->b, name);
#ifdef USE_VM_PROBES
dt_s1 = d->b;
@@ -2864,7 +2868,9 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
case FILE_ALTNAME:
{
- d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + RESBUFSIZE + 1);
+ d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 +
+ MAX(RESBUFSIZE, (FILENAME_BYTELEN(name) +
+ FILENAME_CHARSIZE)) + 1);
FILENAME_COPY(d->b, name);
#ifdef USE_VM_PROBES
dt_s1 = d->b;
diff --git a/erts/emulator/drivers/common/gzio.c b/erts/emulator/drivers/common/gzio.c
index a9303d55bc..ca6d25adb4 100644
--- a/erts/emulator/drivers/common/gzio.c
+++ b/erts/emulator/drivers/common/gzio.c
@@ -21,11 +21,6 @@
#include "erl_driver.h"
#include "sys.h"
-#ifdef VXWORKS
-/* pull in FOPEN from zutil.h instead */
-#undef F_OPEN
-#endif
-
#ifdef __WIN32__
#ifndef HAVE_CONFLICTING_FREAD_DECLARATION
#define HAVE_CONFLICTING_FREAD_DECLARATION
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 76a9b55179..dea910e89f 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -284,27 +284,15 @@ static unsigned long one_value = 1;
#else
-#ifdef VXWORKS
-#include <sockLib.h>
-#include <sys/times.h>
-#include <iosLib.h>
-#include <taskLib.h>
-#include <selectLib.h>
-#include <ioLib.h>
-#else
#include <sys/time.h>
#ifdef NETDB_H_NEEDS_IN_H
#include <netinet/in.h>
#endif
#include <netdb.h>
-#endif
#include <sys/socket.h>
#include <netinet/in.h>
-#ifdef VXWORKS
-#include <rpc/rpctypes.h>
-#endif
#ifdef DEF_INADDR_LOOPBACK_IN_RPC_TYPES_H
#include <rpc/types.h>
#endif
@@ -312,12 +300,10 @@ static unsigned long one_value = 1;
#include <netinet/tcp.h>
#include <arpa/inet.h>
-#if (!defined(VXWORKS))
#include <sys/param.h>
#ifdef HAVE_ARPA_NAMESER_H
#include <arpa/nameser.h>
#endif
-#endif
#ifdef HAVE_SYS_SOCKIO_H
#include <sys/sockio.h>
@@ -331,7 +317,7 @@ static unsigned long one_value = 1;
/* SCTP support -- currently for UNIX platforms only: */
#undef HAVE_SCTP
-#if (!defined(VXWORKS) && !defined(__WIN32__) && defined(HAVE_SCTP_H))
+#if (!defined(__WIN32__) && defined(HAVE_SCTP_H))
#include <netinet/sctp.h>
@@ -478,15 +464,8 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#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))
-#ifdef VXWORKS
-#define sock_getopt(s,t,n,v,l) wrap_sockopt(&getsockopt,\
- s,t,n,v,(unsigned int)(l))
-#define sock_setopt(s,t,n,v,l) wrap_sockopt(&setsockopt,\
- s,t,n,v,(unsigned int)(l))
-#else
#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))
-#endif
#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))
@@ -535,6 +514,12 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#endif /* __WIN32__ */
+#ifdef HAVE_SOCKLEN_T
+# define SOCKLEN_T socklen_t
+#else
+# define SOCKLEN_T int
+#endif
+
#include "packet_parser.h"
#define get_int24(s) ((((unsigned char*) (s))[0] << 16) | \
@@ -685,7 +670,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define INET_LOPT_EXITONCLOSE 26 /* exit port on active close or not ! */
#define INET_LOPT_TCP_HIWTRMRK 27 /* set local high watermark */
#define INET_LOPT_TCP_LOWTRMRK 28 /* set local low watermark */
-#define INET_LOPT_BIT8 29 /* set 8 bit detection */
+ /* 29 unused */
#define INET_LOPT_TCP_SEND_TIMEOUT 30 /* set send timeout */
#define INET_LOPT_TCP_DELAY_SEND 31 /* Delay sends until next poll */
#define INET_LOPT_PACKET_SIZE 32 /* Max packet size */
@@ -720,12 +705,6 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define INET_IFOPT_FLAGS 6
#define INET_IFOPT_HWADDR 7
-/* INET_LOPT_BIT8 options */
-#define INET_BIT8_CLEAR 0
-#define INET_BIT8_SET 1
-#define INET_BIT8_ON 2
-#define INET_BIT8_OFF 3
-
/* INET_REQ_GETSTAT enumeration */
#define INET_STAT_RECV_CNT 1
#define INET_STAT_RECV_MAX 2
@@ -921,7 +900,6 @@ typedef struct {
int mode; /* BINARY | LIST
(affect how to interpret hsz) */
int exitf; /* exit port on close or not */
- int bit8f; /* check if data has bit number 7 set */
int deliver; /* Delivery mode, TERM or PORT */
ErlDrvTermData caller; /* recipient of sync reply */
@@ -940,8 +918,6 @@ typedef struct {
int sfamily; /* address family */
enum PacketParseType htype; /* header type (TCP only?) */
unsigned int psize; /* max packet size (TCP only?) */
- int bit8; /* set if bit8f==true and data some data
- seen had the 7th bit set */
inet_address remote; /* remote address for connected sockets */
inet_address peer_addr; /* fake peer address */
inet_address name_addr; /* fake local address */
@@ -1228,6 +1204,27 @@ struct erl_drv_entry inet_driver_entry =
NULL,
};
+#if HAVE_IN6
+# if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY
+# if HAVE_DECL_IN6ADDR_ANY_INIT
+static const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } };
+# else
+static const struct in6_addr in6addr_any =
+ { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } };
+# endif /* HAVE_IN6ADDR_ANY_INIT */
+# endif /* ! HAVE_DECL_IN6ADDR_ANY */
+
+# if ! defined(HAVE_IN6ADDR_LOOPBACK) || ! HAVE_IN6ADDR_LOOPBACK
+# if HAVE_DECL_IN6ADDR_LOOPBACK_INIT
+static const struct in6_addr in6addr_loopback =
+ { { IN6ADDR_LOOPBACK_INIT } };
+# else
+static const struct in6_addr in6addr_loopback =
+ { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } };
+# endif /* HAVE_IN6ADDR_LOOPBACk_INIT */
+# endif /* ! HAVE_DECL_IN6ADDR_LOOPBACK */
+#endif /* HAVE_IN6 */
+
/* XXX: is this a driver interface function ??? */
void erl_exit(int n, char*, ...);
@@ -3342,17 +3339,6 @@ static int packet_error_message(udp_descriptor* udesc, int err)
}
-/* scan buffer for bit 7 */
-static void scanbit8(inet_descriptor* desc, const char* buf, int len)
-{
- int c;
-
- if (!desc->bit8f || desc->bit8) return;
- c = 0;
- while(len--) c |= *buf++;
- desc->bit8 = ((c & 0x80) != 0);
-}
-
/*
** active=TRUE:
** (NOTE! distribution MUST use active=TRUE, deliver=PORT)
@@ -3370,8 +3356,6 @@ static int tcp_reply_data(tcp_descriptor* desc, char* buf, int len)
packet_get_body(desc->inet.htype, &body, &bodylen);
- scanbit8(INETP(desc), body, bodylen);
-
if (desc->inet.deliver == INET_DELIVER_PORT) {
code = inet_port_data(INETP(desc), body, bodylen);
}
@@ -3403,8 +3387,6 @@ tcp_reply_binary_data(tcp_descriptor* desc, ErlDrvBinary* bin, int offs, int len
packet_get_body(desc->inet.htype, &body, &bodylen);
offs = body - bin->orig_bytes; /* body offset now */
- scanbit8(INETP(desc), body, bodylen);
-
if (desc->inet.deliver == INET_DELIVER_PORT)
code = inet_port_binary_data(INETP(desc), bin, offs, bodylen);
else if ((code=packet_parse(desc->inet.htype, buf, len, &desc->http_state,
@@ -3430,8 +3412,6 @@ packet_reply_binary_data(inet_descriptor* desc, unsigned int hsz,
{
int code;
- scanbit8(desc, bin->orig_bytes+offs, len);
-
if (desc->active == INET_PASSIVE)
/* "inet" is actually for both UDP and SCTP, as well as TCP! */
return inet_async_binary_data(desc, hsz, bin, offs, len, extra);
@@ -3706,6 +3686,9 @@ static char* inet_set_address(int family, inet_address* dst,
if ((family == AF_INET) && (*len >= 2+4)) {
sys_memzero((char*)dst, sizeof(struct sockaddr_in));
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);
@@ -3716,6 +3699,9 @@ static char* inet_set_address(int family, inet_address* dst,
else if ((family == AF_INET6) && (*len >= 2+16)) {
sys_memzero((char*)dst, sizeof(struct sockaddr_in6));
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 ?? */
@@ -3726,7 +3712,7 @@ static char* inet_set_address(int family, inet_address* dst,
#endif
return NULL;
}
-#ifdef HAVE_SCTP
+
/*
** Set an inaddr structure, address family comes from source data,
** or from argument if source data specifies constant address.
@@ -3770,6 +3756,9 @@ static char *inet_set_faddress(int family, inet_address* dst,
return NULL;
}
sys_memzero((char*)dst, sizeof(struct sockaddr_in));
+#ifndef NO_SA_LEN
+ dst->sai.sin_len = sizeof(struct sockaddr_in6);
+#endif
dst->sai.sin_family = family;
dst->sai.sin_port = sock_htons(port);
dst->sai.sin_addr.s_addr = addr.s_addr;
@@ -3789,6 +3778,9 @@ static char *inet_set_faddress(int family, inet_address* dst,
return NULL;
}
sys_memzero((char*)dst, sizeof(struct sockaddr_in6));
+#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 ?? */
@@ -3806,7 +3798,7 @@ static char *inet_set_faddress(int family, inet_address* dst,
}
return inet_set_address(family, dst, src, len);
}
-#endif /* HAVE_SCTP */
+
/* Get a inaddr structure
** src = inaddr structure
@@ -5280,50 +5272,6 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p,
#endif
-
-
-#ifdef VXWORKS
-/*
-** THIS is a terrible creature, a bug in the TCP part
-** of the old VxWorks stack (non SENS) created a race.
-** If (and only if?) a socket got closed from the other
-** end and we tried a set/getsockopt on the TCP level,
-** the task would generate a bus error...
-*/
-static STATUS wrap_sockopt(STATUS (*function)() /* Yep, no parameter
- check */,
- int s, int level, int optname,
- char *optval, unsigned int optlen
- /* optlen is a pointer if function
- is getsockopt... */)
-{
- fd_set rs;
- struct timeval timeout;
- int to_read;
- int ret;
-
- FD_ZERO(&rs);
- FD_SET(s,&rs);
- memset(&timeout,0,sizeof(timeout));
- if (level == IPPROTO_TCP) {
- taskLock();
- if (select(s+1,&rs,NULL,NULL,&timeout)) {
- if (ioctl(s,FIONREAD,(int)&to_read) == ERROR ||
- to_read == 0) { /* End of file, other end closed? */
- sock_errno() = EBADF;
- taskUnlock();
- return ERROR;
- }
- }
- ret = (*function)(s,level,optname,optval,optlen);
- taskUnlock();
- } else {
- ret = (*function)(s,level,optname,optval,optlen);
- }
- return ret;
-}
-#endif
-
/* Per H @ Tail-f: The original code here had problems that possibly
only occur if you abuse it for non-INET sockets, but anyway:
a) If the getsockopt for SO_PRIORITY or IP_TOS failed, the actual
@@ -5349,13 +5297,8 @@ static int setopt_prio_tos_trick
int res;
int res_prio;
int res_tos;
-#ifdef HAVE_SOCKLEN_T
- socklen_t
-#else
- int
-#endif
- tmp_arg_sz_prio = sizeof(tmp_ival_prio),
- tmp_arg_sz_tos = sizeof(tmp_ival_tos);
+ SOCKLEN_T tmp_arg_sz_prio = sizeof(tmp_ival_prio);
+ SOCKLEN_T tmp_arg_sz_tos = sizeof(tmp_ival_tos);
res_prio = sock_getopt(fd, SOL_SOCKET, SO_PRIORITY,
(char *) &tmp_ival_prio, &tmp_arg_sz_prio);
@@ -5499,29 +5442,6 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
desc->exitf = ival;
continue;
- case INET_LOPT_BIT8:
- DEBUGF(("inet_set_opts(%ld): s=%d, BIT8=%d\r\n",
- (long)desc->port, desc->s, ival));
- switch(ival) {
- case INET_BIT8_ON:
- desc->bit8f = 1;
- desc->bit8 = 0;
- break;
- case INET_BIT8_OFF:
- desc->bit8f = 0;
- desc->bit8 = 0;
- break;
- case INET_BIT8_CLEAR:
- desc->bit8f = 1;
- desc->bit8 = 0;
- break;
- case INET_BIT8_SET:
- desc->bit8f = 1;
- desc->bit8 = 1;
- break;
- }
- continue;
-
case INET_LOPT_TCP_HIWTRMRK:
if (desc->stype == SOCK_STREAM) {
tcp_descriptor* tdesc = (tcp_descriptor*) desc;
@@ -5603,23 +5523,11 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
case INET_OPT_SNDBUF: type = SO_SNDBUF;
DEBUGF(("inet_set_opts(%ld): s=%d, SO_SNDBUF=%d\r\n",
(long)desc->port, desc->s, ival));
- /*
- * Setting buffer sizes in VxWorks gives unexpected results
- * our workaround is to leave it at default.
- */
-#ifdef VXWORKS
- goto skip_os_setopt;
-#else
break;
-#endif
case INET_OPT_RCVBUF: type = SO_RCVBUF;
DEBUGF(("inet_set_opts(%ld): s=%d, SO_RCVBUF=%d\r\n",
(long)desc->port, desc->s, ival));
-#ifdef VXWORKS
- goto skip_os_setopt;
-#else
break;
-#endif
case INET_OPT_LINGER: type = SO_LINGER;
if (len < 4)
return -1;
@@ -5741,9 +5649,6 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
}
DEBUGF(("inet_set_opts(%ld): s=%d returned %d\r\n",
(long)desc->port, desc->s, res));
-#ifdef VXWORKS
-skip_os_setopt:
-#endif
if (type == SO_RCVBUF) {
/* make sure we have desc->bufsz >= SO_RCVBUF */
if (ival > desc->bufsz)
@@ -6404,15 +6309,6 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc,
put_int32(desc->exitf, ptr);
continue;
- case INET_LOPT_BIT8:
- *ptr++ = opt;
- if (desc->bit8f) {
- put_int32(desc->bit8, ptr);
- } else {
- put_int32(INET_BIT8_OFF, ptr);
- }
- continue;
-
case INET_LOPT_TCP_HIWTRMRK:
if (desc->stype == SOCK_STREAM) {
*ptr++ = opt;
@@ -7471,8 +7367,6 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol)
desc->mode = INET_MODE_LIST; /* list mode */
desc->exitf = 1; /* exit port when close on active
socket */
- desc->bit8f = 0;
- desc->bit8 = 0;
desc->deliver = INET_DELIVER_TERM; /* standard term format */
desc->active = INET_PASSIVE; /* start passive */
desc->oph = NULL;
@@ -7771,7 +7665,7 @@ 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_address(desc->sfamily, &local, buf, &len) == NULL)
+ if (inet_set_faddress(desc->sfamily, &local, buf, &len) == NULL)
return ctl_error(EINVAL, rbuf, rsize);
if (IS_SOCKET_ERROR(sock_bind(desc->s,(struct sockaddr*) &local, len)))
@@ -7780,8 +7674,8 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
desc->state = INET_STATE_BOUND;
if ((port = inet_address_port(&local)) == 0) {
- len = sizeof(local);
- sock_name(desc->s, (struct sockaddr*) &local, (unsigned int*)&len);
+ SOCKLEN_T adrlen = sizeof(local);
+ sock_name(desc->s, &local.sa, &adrlen);
port = inet_address_port(&local);
}
port = sock_ntohs(port);
@@ -7816,8 +7710,6 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize);
}
-#ifndef VXWORKS
-
case INET_REQ_GETSERVBYNAME: { /* L1 Name-String L2 Proto-String */
char namebuf[256];
char protobuf[256];
@@ -7868,8 +7760,6 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
return ctl_reply(INET_REP_OK, srv->s_name, len, rbuf, rsize);
}
-#endif /* !VXWORKS */
-
default:
return ctl_xerror(EXBADPORT, rbuf, rsize);
}
@@ -8100,7 +7990,6 @@ static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s,
/* Some flags must be inherited at this point */
copy_desc->inet.mode = desc->inet.mode;
copy_desc->inet.exitf = desc->inet.exitf;
- copy_desc->inet.bit8f = desc->inet.bit8f;
copy_desc->inet.deliver = desc->inet.deliver;
copy_desc->inet.htype = desc->inet.htype;
copy_desc->inet.psize = desc->inet.psize;
@@ -9805,7 +9694,6 @@ static udp_descriptor* sctp_inet_copy(udp_descriptor* desc, SOCKET s, int* err)
/* Some flags must be inherited at this point */
copy_desc->inet.mode = desc->inet.mode;
copy_desc->inet.exitf = desc->inet.exitf;
- copy_desc->inet.bit8f = desc->inet.bit8f;
copy_desc->inet.deliver = desc->inet.deliver;
copy_desc->inet.htype = desc->inet.htype;
copy_desc->inet.psize = desc->inet.psize;
@@ -10136,12 +10024,13 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
case SCTP_REQ_BINDX:
{ /* Multi-homing bind for SCTP: */
- /* Construct the list of addresses we bind to. The curr limit is
- 256 addrs. Buff structure: Flags(1), ListItem,...:
+ /* Add additional addresses by calling sctp_bindx with one address
+ at a time, since this is what some OSes promise will work.
+ Buff structure: Flags(1), ListItem,...:
*/
- struct sockaddr addrs[256];
+ inet_address addr;
char* curr;
- int add_flag, n, rflag;
+ int add_flag, rflag;
if (!IS_SCTP(desc))
return ctl_xerror(EXBADPORT, rbuf, rsize);
@@ -10150,27 +10039,22 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
add_flag = get_int8(curr);
curr++;
- for(n=0; n < 256 && curr < buf+len; n++)
+ /* Make the real flags: */
+ rflag = add_flag ? SCTP_BINDX_ADD_ADDR : SCTP_BINDX_REM_ADDR;
+
+ while (curr < buf+len)
{
- /* List item format: Port(2), IP(4|16) -- compatible with
- "inet_set_address": */
- inet_address tmp;
+ /* List item format: see "inet_set_faddress": */
ErlDrvSizeT alen = buf + len - curr;
- curr = inet_set_address(desc->sfamily, &tmp, curr, &alen);
+ curr = inet_set_faddress(desc->sfamily, &addr, curr, &alen);
if (curr == NULL)
return ctl_error(EINVAL, rbuf, rsize);
- /* Now: we need to squeeze "tmp" into the size of "sockaddr",
- which is smaller than "tmp" for IPv6 (extra IN6 info will
- be cut off): */
- memcpy(addrs + n, &tmp, sizeof(struct sockaddr));
+ /* Invoke the call: */
+ if (p_sctp_bindx(desc->s, (struct sockaddr *)&addr, 1,
+ rflag) < 0)
+ return ctl_error(sock_errno(), rbuf, rsize);
}
- /* Make the real flags: */
- rflag = add_flag ? SCTP_BINDX_ADD_ADDR : SCTP_BINDX_REM_ADDR;
-
- /* Invoke the call: */
- if (p_sctp_bindx(desc->s, addrs, n, rflag) < 0)
- return ctl_error(sock_errno(), rbuf, rsize);
desc->state = INET_STATE_BOUND;
diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c
index ad112f7590..2aa373aa7d 100644
--- a/erts/emulator/drivers/unix/unix_efile.c
+++ b/erts/emulator/drivers/unix/unix_efile.c
@@ -45,23 +45,6 @@
#include <fcntl.h>
#endif /* DARWIN */
-#ifdef VXWORKS
-#include <ioLib.h>
-#include <dosFsLib.h>
-#include <nfsLib.h>
-#include <sys/stat.h>
-/*
-** Not nice to include usrLib.h as MANY normal variable names get reported
-** as shadowing globals, like 'i' for example.
-** Instead we declare the only function we use here
-*/
-/*
- * #include <usrLib.h>
- */
-extern STATUS copy(char *, char *);
-#include <errno.h>
-#endif
-
#ifdef SUNOS4
# define getcwd(buf, size) getwd(buf)
#endif
@@ -93,276 +76,27 @@ extern STATUS copy(char *, char *);
#define DIR_MODE 0777
#endif
-#ifdef VXWORKS /* Currently only used on vxworks */
-
-#define EF_ALLOC(S) driver_alloc((S))
-#define EF_REALLOC(P, S) driver_realloc((P), (S))
-#define EF_SAFE_ALLOC(S) ef_safe_alloc((S))
-#define EF_SAFE_REALLOC(P, S) ef_safe_realloc((P), (S))
-#define EF_FREE(P) do { if((P)) driver_free((P)); } while(0)
-
-void erl_exit(int n, char *fmt, ...);
-
-static void *ef_safe_alloc(Uint s)
-{
- void *p = EF_ALLOC(s);
- if (!p) erl_exit(1,
- "unix efile drv: Can't allocate %d bytes of memory\n",
- s);
- return p;
-}
-
-#if 0 /* Currently not used */
-
-static void *ef_safe_realloc(void *op, Uint s)
-{
- void *p = EF_REALLOC(op, s);
- if (!p) erl_exit(1,
- "unix efile drv: Can't reallocate %d bytes of memory\n",
- s);
- return p;
-}
-
-#endif /* #if 0 */
-#endif /* #ifdef VXWORKS */
-
#define IS_DOT_OR_DOTDOT(s) \
(s[0] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0')))
-#ifdef VXWORKS
-static int vxworks_to_posix(int vx_errno);
-#endif
-
-/*
-** VxWorks (not) strikes again. Too long RESULTING paths
-** may give the infamous bus error. Have to check ALL
-** filenames and pathnames. No wonder the emulator is slow on
-** these cards...
-*/
-#ifdef VXWORKS
-#define CHECK_PATHLEN(Name, ErrInfo) \
- if (path_size(Name) > PATH_MAX) { \
- errno = ENAMETOOLONG; \
- return check_error(-1, ErrInfo); \
- }
-#else
-#define CHECK_PATHLEN(X,Y) /* Nothing */
-#endif
-
static int check_error(int result, Efile_error* errInfo);
static int
check_error(int result, Efile_error *errInfo)
{
if (result < 0) {
-#ifdef VXWORKS
- errInfo->posix_errno = errInfo->os_errno = vxworks_to_posix(errno);
-#else
errInfo->posix_errno = errInfo->os_errno = errno;
-#endif
return 0;
}
return 1;
}
-#ifdef VXWORKS
-
-/*
- * VxWorks has different error codes for different file systems.
- * We map those to POSIX ones.
- */
-static int
-vxworks_to_posix(int vx_errno)
-{
- DEBUGF(("[vxworks_to_posix] vx_errno: %08x\n", vx_errno));
- switch (vx_errno) {
- /* dosFsLib mapping */
-#ifdef S_dosFsLib_FILE_ALREADY_EXISTS
- case S_dosFsLib_FILE_ALREADY_EXISTS: return EEXIST;
-#else
- case S_dosFsLib_FILE_EXISTS: return EEXIST;
-#endif
-#ifdef S_dosFsLib_BAD_DISK
- case S_dosFsLib_BAD_DISK: return EIO;
-#endif
-#ifdef S_dosFsLib_CANT_CHANGE_ROOT
- case S_dosFsLib_CANT_CHANGE_ROOT: return EINVAL;
-#endif
-#ifdef S_dosFsLib_NO_BLOCK_DEVICE
- case S_dosFsLib_NO_BLOCK_DEVICE: return ENOTBLK;
-#endif
-#ifdef S_dosFsLib_BAD_SEEK
- case S_dosFsLib_BAD_SEEK: return ESPIPE;
-#endif
- case S_dosFsLib_VOLUME_NOT_AVAILABLE: return ENXIO;
- case S_dosFsLib_DISK_FULL: return ENOSPC;
- case S_dosFsLib_FILE_NOT_FOUND: return ENOENT;
- case S_dosFsLib_NO_FREE_FILE_DESCRIPTORS: return ENFILE;
- case S_dosFsLib_INVALID_NUMBER_OF_BYTES: return EINVAL;
- case S_dosFsLib_ILLEGAL_NAME: return EINVAL;
- case S_dosFsLib_CANT_DEL_ROOT: return EACCES;
- case S_dosFsLib_NOT_FILE: return EISDIR;
- case S_dosFsLib_NOT_DIRECTORY: return ENOTDIR;
- case S_dosFsLib_NOT_SAME_VOLUME: return EXDEV;
- case S_dosFsLib_READ_ONLY: return EACCES;
- case S_dosFsLib_ROOT_DIR_FULL: return ENOSPC;
- case S_dosFsLib_DIR_NOT_EMPTY: return EEXIST;
- case S_dosFsLib_NO_LABEL: return ENXIO;
- case S_dosFsLib_INVALID_PARAMETER: return EINVAL;
- case S_dosFsLib_NO_CONTIG_SPACE: return ENOSPC;
- case S_dosFsLib_FD_OBSOLETE: return EBADF;
- case S_dosFsLib_DELETED: return EINVAL;
- case S_dosFsLib_INTERNAL_ERROR: return EIO;
- case S_dosFsLib_WRITE_ONLY: return EACCES;
- /* nfsLib mapping - is needed since Windriver has used */
- /* inconsistent error codes (errno.h/nfsLib.h). */
- case S_nfsLib_NFS_OK: return 0;
- case S_nfsLib_NFSERR_PERM: return EPERM;
- case S_nfsLib_NFSERR_NOENT: return ENOENT;
- case S_nfsLib_NFSERR_IO: return EIO;
- case S_nfsLib_NFSERR_NXIO: return ENXIO;
-#ifdef S_nfsLib_NFSERR_ACCES
- case S_nfsLib_NFSERR_ACCES: return EACCES;
-#else
- case S_nfsLib_NFSERR_ACCESS: return EACCES;
-#endif
- case S_nfsLib_NFSERR_EXIST: return EEXIST;
- case S_nfsLib_NFSERR_NODEV: return ENODEV;
- case S_nfsLib_NFSERR_NOTDIR: return ENOTDIR;
- case S_nfsLib_NFSERR_ISDIR: return EISDIR;
- case S_nfsLib_NFSERR_FBIG: return EFBIG;
- case S_nfsLib_NFSERR_NOSPC: return ENOSPC;
- case S_nfsLib_NFSERR_ROFS: return EROFS;
- case S_nfsLib_NFSERR_NAMETOOLONG: return ENAMETOOLONG;
- case S_nfsLib_NFSERR_NOTEMPTY: return EEXIST;
- case S_nfsLib_NFSERR_DQUOT: return ENOSPC;
- case S_nfsLib_NFSERR_STALE: return EINVAL;
- case S_nfsLib_NFSERR_WFLUSH: return ENXIO;
- /* And sometimes (...) the error codes are from ioLib (as in the */
- /* case of the (for nfsLib) unimplemented rename function) */
- case S_ioLib_DISK_NOT_PRESENT: return EIO;
-#if S_ioLib_DISK_NOT_PRESENT != S_ioLib_NO_DRIVER
- case S_ioLib_NO_DRIVER: return ENXIO;
-#endif
- case S_ioLib_UNKNOWN_REQUEST: return ENOSYS;
- case S_ioLib_DEVICE_TIMEOUT: return EIO;
-#ifdef S_ioLib_UNFORMATED
- /* Added (VxWorks 5.2 -> 5.3.1) */
- #if S_ioLib_UNFORMATED != S_ioLib_DEVICE_TIMEOUT
- case S_ioLib_UNFORMATED: return EIO;
- #endif
-#endif
-#if S_ioLib_DEVICE_TIMEOUT != S_ioLib_DEVICE_ERROR
- case S_ioLib_DEVICE_ERROR: return ENXIO;
-#endif
- case S_ioLib_WRITE_PROTECTED: return EACCES;
- case S_ioLib_NO_FILENAME: return EINVAL;
- case S_ioLib_CANCELLED: return EINTR;
- case S_ioLib_NO_DEVICE_NAME_IN_PATH: return EINVAL;
- case S_ioLib_NAME_TOO_LONG: return ENAMETOOLONG;
-#ifdef S_objLib_OBJ_UNAVAILABLE
- case S_objLib_OBJ_UNAVAILABLE: return ENOENT;
-#endif
-
- /* Temporary workaround for a weird error in passFs
- (VxWorks Simsparc only). File operation fails because of
- ENOENT, but errno is not set. */
-#ifdef SIMSPARCSOLARIS
- case 0: return ENOENT;
-#endif
-
- }
- /* If the error code matches none of the above, assume */
- /* it is a POSIX one already. The upper bits (>=16) are */
- /* cleared since VxWorks uses those bits to indicate in */
- /* what module the error occured. */
- return vx_errno & 0xffff;
-}
-
-static int
-vxworks_enotsup(Efile_error *errInfo)
-{
- errInfo->posix_errno = errInfo->os_errno = ENOTSUP;
- return 0;
-}
-
-static int
-count_path_length(char *pathname, char *pathname2)
-{
- static int stack[PATH_MAX / 2 + 1];
- int sp = 0;
- char *tmp;
- char *cpy = NULL;
- int i;
- int sum;
- for(i = 0;i < 2;++i) {
- if (!i) {
- cpy = EF_SAFE_ALLOC(strlen(pathname)+1);
- strcpy(cpy, pathname);
- } else if (pathname2 != NULL) {
- EF_FREE(cpy);
- cpy = EF_SAFE_ALLOC(strlen(pathname2)+1);
- strcpy(cpy, pathname2);
- } else
- break;
-
- for (tmp = strtok(cpy,"/"); tmp != NULL; tmp = strtok(NULL,"/")) {
- if (!strcmp(tmp,"..") && sp > 0)
- --sp;
- else if (strcmp(tmp,"."))
- stack[sp++] = strlen(tmp);
- }
- }
- if (cpy != NULL)
- EF_FREE(cpy);
- sum = 0;
- for(i = 0;i < sp; ++i)
- sum += stack[i]+1;
- return (sum) ? sum : 1;
-}
-
-static int
-path_size(char *pathname)
-{
- static char currdir[PATH_MAX+2];
- if (*pathname == '/')
- return count_path_length(pathname,NULL);
- ioDefPathGet(currdir);
- strcat(currdir,"/");
- return count_path_length(currdir,pathname);
-}
-
-#endif /* VXWORKS */
-
int
efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */
char* name) /* Name of directory to create. */
{
- CHECK_PATHLEN(name,errInfo);
#ifdef NO_MKDIR_MODE
-#ifdef VXWORKS
- /* This is a VxWorks/nfs workaround for erl_tar to create
- * non-existant directories. (of some reason (...) VxWorks
- * returns, the *non-module-prefixed*, 0xd code when
- * trying to create a directory in a directory that doesn't exist).
- * (see efile_openfile)
- */
- if (mkdir(name) < 0) {
- struct stat sb;
- if (name[0] == '\0') {
- /* Return the correct error code enoent */
- errno = S_nfsLib_NFSERR_NOENT;
- } else if (stat(name, &sb) == OK) {
- errno = S_nfsLib_NFSERR_EXIST;
- } else if((strchr(name, '/') != NULL) && (errno == 0xd)) {
- /* Return the correct error code enoent */
- errno = S_nfsLib_NFSERR_NOENT;
- }
- return check_error(-1, errInfo);
- } else return 1;
-#else
return check_error(mkdir(name), errInfo);
-#endif
#else
return check_error(mkdir(name, DIR_MODE), errInfo);
#endif
@@ -372,16 +106,9 @@ int
efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */
char* name) /* Name of directory to delete. */
{
- CHECK_PATHLEN(name, errInfo);
if (rmdir(name) == 0) {
return 1;
}
-#ifdef VXWORKS
- if (name[0] == '\0') {
- /* Return the correct error code enoent */
- errno = S_nfsLib_NFSERR_NOENT;
- }
-#else
if (errno == ENOTEMPTY) {
errno = EEXIST;
}
@@ -401,7 +128,6 @@ efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */
}
errno = saved_errno;
}
-#endif
return check_error(-1, errInfo);
}
@@ -409,7 +135,6 @@ int
efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */
char* name) /* Name of file to delete. */
{
- CHECK_PATHLEN(name,errInfo);
if (unlink(name) == 0) {
return 1;
}
@@ -457,32 +182,13 @@ efile_rename(Efile_error* errInfo, /* Where to return error codes. */
char* src, /* Original name. */
char* dst) /* New name. */
{
- CHECK_PATHLEN(src,errInfo);
- CHECK_PATHLEN(dst,errInfo);
-#ifdef VXWORKS
-
- /* First check if src == dst, if so, just return. */
- /* VxWorks dos file system destroys the file otherwise, */
- /* VxWorks nfs file system rename doesn't work at all. */
- if(strcmp(src, dst) == 0)
- return 1;
-#endif
if (rename(src, dst) == 0) {
return 1;
}
-#ifdef VXWORKS
- /* nfs for VxWorks doesn't support rename. We try to emulate it */
- /* (by first copying src to dst and then deleting src). */
- if(errno == S_ioLib_UNKNOWN_REQUEST && /* error code returned
- by ioLib (!) */
- copy(src, dst) == OK &&
- unlink(src) == OK)
- return 1;
-#endif
if (errno == ENOTEMPTY) {
errno = EEXIST;
}
-#if defined (sparc) && !defined(VXWORKS)
+#if defined (sparc)
/*
* SunOS 4.1.4 reports overwriting a non-empty directory with a
* directory as EINVAL instead of EEXIST (first rule out the correct
@@ -543,7 +249,6 @@ int
efile_chdir(Efile_error* errInfo, /* Where to return error codes. */
char* name) /* Name of directory to make current. */
{
- CHECK_PATHLEN(name, errInfo);
return check_error(chdir(name), errInfo);
}
@@ -600,8 +305,6 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */
* If this is the first call, we must open the directory.
*/
- CHECK_PATHLEN(name, errInfo);
-
if (*p_dir_handle == NULL) {
dp = opendir(name);
if (dp == NULL)
@@ -641,26 +344,8 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
struct stat statbuf;
int fd;
int mode; /* Open mode. */
-#ifdef VXWORKS
- char pathbuff[PATH_MAX+2];
- char sbuff[PATH_MAX*2];
- char *totbuff = sbuff;
- int nameneed;
-#endif
-
-
- CHECK_PATHLEN(name, errInfo);
-
-#ifdef VXWORKS
- /* Have to check that it's not a directory. */
- if (stat(name,&statbuf) != ERROR && ISDIR(statbuf)) {
- errno = EISDIR;
- return check_error(-1, errInfo);
- }
-#endif
if (stat(name, &statbuf) >= 0 && !ISREG(statbuf)) {
-#if !defined(VXWORKS) && !defined(OSE)
/*
* For UNIX only, here is some ugly code to allow
* /dev/null to be opened as a file.
@@ -677,12 +362,9 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
}
}
if (!(dev_null_ino && statbuf.st_ino == dev_null_ino)) {
-#endif
errno = EISDIR;
return check_error(-1, errInfo);
-#if !defined(VXWORKS) && !defined(OSE)
}
-#endif
}
switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) {
@@ -706,49 +388,14 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
if (flags & EFILE_MODE_APPEND) {
mode &= ~O_TRUNC;
-#ifndef VXWORKS
- mode |= O_APPEND; /* Dont make VxWorks think things it shouldn't */
-#endif
+ mode |= O_APPEND;
}
if (flags & EFILE_MODE_EXCL) {
mode |= O_EXCL;
}
-#ifdef VXWORKS
- if (*name != '/') {
- /* Make sure it is an absolute pathname, because ftruncate needs it */
- ioDefPathGet(pathbuff);
- strcat(pathbuff,"/");
- nameneed = strlen(pathbuff) + strlen(name) + 1;
- if (nameneed > PATH_MAX*2)
- totbuff = EF_SAFE_ALLOC(nameneed);
- strcpy(totbuff,pathbuff);
- strcat(totbuff,name);
- fd = open(totbuff, mode, FILE_MODE);
- if (totbuff != sbuff)
- EF_FREE(totbuff);
- } else {
- fd = open(name, mode, FILE_MODE);
- }
-#else
fd = open(name, mode, FILE_MODE);
-#endif
-
-#ifdef VXWORKS
-
- /* This is a VxWorks/nfs workaround for erl_tar to create
- * non-existant directories. (of some reason (...) VxWorks
- * returns, the *non-module-prefixed*, 0xd code when
- * trying to write a file in a directory that doesn't exist).
- * (see efile_mkdir)
- */
- if ((fd < 0) && (strchr(name, '/') != NULL) && (errno == 0xd)) {
- /* Return the correct error code enoent */
- errno = S_nfsLib_NFSERR_NOENT;
- return check_error(-1, errInfo);
- }
-#endif
if (!check_error(fd, errInfo))
return 0;
@@ -797,11 +444,7 @@ efile_fsync(Efile_error *errInfo, /* Where to return error codes. */
int fd) /* File descriptor for file to sync. */
{
#ifdef NO_FSYNC
-#ifdef VXWORKS
- return check_error(ioctl(fd, FIOSYNC, 0), errInfo);
-#else
- undefined fsync
-#endif /* VXWORKS */
+ undefined fsync /* XXX: Really? */
#else
#if defined(DARWIN) && defined(F_FULLFSYNC)
return check_error(fcntl(fd, F_FULLFSYNC), errInfo);
@@ -818,21 +461,8 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
struct stat statbuf; /* Information about the file */
int result;
-#ifdef VXWORKS
- if (*name == '\0') {
- errInfo->posix_errno = errInfo->os_errno = ENOENT;
- return 0;
- }
-#endif
-
- CHECK_PATHLEN(name, errInfo);
-
if (info_for_link) {
-#if (defined(VXWORKS))
- result = stat(name, &statbuf);
-#else
result = lstat(name, &statbuf);
-#endif
} else {
result = stat(name, &statbuf);
}
@@ -849,19 +479,9 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
#ifdef NO_ACCESS
/* Just look at read/write access for owner. */
-#ifdef VXWORKS
-
- pInfo->access = FA_NONE;
- if(statbuf.st_mode & S_IRUSR)
- pInfo->access |= FA_READ;
- if(statbuf.st_mode & S_IWUSR)
- pInfo->access |= FA_WRITE;
-
-#else
pInfo->access = ((statbuf.st_mode >> 6) & 07) >> 1;
-#endif /* VXWORKS */
#else
pInfo->access = FA_NONE;
if (access(name, R_OK) == 0)
@@ -902,35 +522,6 @@ efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name)
{
struct utimbuf tval;
- CHECK_PATHLEN(name, errInfo);
-
-#ifdef VXWORKS
-
- if (pInfo->mode != -1) {
- int fd;
- struct stat statbuf;
-
- fd = open(name, O_RDONLY, 0);
- if (!check_error(fd, errInfo))
- return 0;
- if (fstat(fd, &statbuf) < 0) {
- close(fd);
- return check_error(-1, errInfo);
- }
- if (pInfo->mode & S_IWUSR) {
- /* clear read only bit */
- statbuf.st_attrib &= ~DOS_ATTR_RDONLY;
- } else {
- /* set read only bit */
- statbuf.st_attrib |= DOS_ATTR_RDONLY;
- }
- /* This should work for dos files but not for nfs ditos, so don't
- * report errors (to avoid problems when running e.g. erl_tar)
- */
- ioctl(fd, FIOATTRIBSET, statbuf.st_attrib);
- close(fd);
- }
-#else
/*
* 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
@@ -952,20 +543,10 @@ efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name)
}
}
-#endif /* !VXWORKS */
-
tval.actime = pInfo->accessTime;
tval.modtime = pInfo->modifyTime;
-#ifdef VXWORKS
- /* VxWorks' utime doesn't work when the file is a nfs mounted
- * one, don't report error if utime fails.
- */
- utime(name, &tval);
- return 1;
-#else
return check_error(utime(name, &tval), errInfo);
-#endif
}
@@ -979,11 +560,6 @@ efile_write(Efile_error* errInfo, /* Where to return error codes. */
{
ssize_t written; /* Bytes written in last operation. */
-#ifdef VXWORKS
- if (flags & EFILE_MODE_APPEND) {
- lseek(fd, 0, SEEK_END); /* Naive append emulation on VXWORKS */
- }
-#endif
while (count > 0) {
if ((written = write(fd, buf, count)) < 0) {
if (errno != EINTR)
@@ -1012,12 +588,6 @@ efile_writev(Efile_error* errInfo, /* Where to return error codes */
ASSERT(iovcnt >= 0);
-#ifdef VXWORKS
- if (flags & EFILE_MODE_APPEND) {
- lseek(fd, 0, SEEK_END); /* Naive append emulation on VXWORKS */
- }
-#endif
-
while (cnt < iovcnt) {
if ((! iov[cnt].iov_base) || (iov[cnt].iov_len <= 0)) {
/* Empty buffer - skip */
@@ -1226,118 +796,6 @@ efile_seek(Efile_error* errInfo, /* Where to return error codes. */
int
efile_truncate_file(Efile_error* errInfo, int *fd, int flags)
{
-#ifdef VXWORKS
- off_t offset;
- char namebuf[PATH_MAX+1];
- char namebuf2[PATH_MAX+10];
- int new;
- int dummy;
- int i;
- int left;
- static char buff[1024];
- struct stat st;
- Efile_error tmperr;
-
- if ((offset = lseek(*fd, 0, 1)) < 0) {
- return check_error((int) offset,errInfo);
- }
- if (ftruncate(*fd, offset) < 0) {
- if (vxworks_to_posix(errno) != EINVAL) {
- return check_error(-1, errInfo);
- }
- /*
- ** Kludge
- */
- if(ioctl(*fd,FIOGETNAME,(int) namebuf) < 0) {
- return check_error(-1, errInfo);
- }
- for(i=0;i<1000;++i) {
- sprintf(namebuf2,"%s%d",namebuf,i);
- CHECK_PATHLEN(namebuf2,errInfo);
- if (stat(namebuf2,&st) < 0) {
- break;
- }
- }
- if (i > 1000) {
- errno = EINVAL;
- return check_error(-1, errInfo);
- }
- if (close(*fd) < 0) {
- return check_error(-1, errInfo);
- }
- if (efile_rename(&tmperr,namebuf,namebuf2) < 0) {
- i = check_error(-1,&tmperr);
- if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,
- fd,&dummy)) {
- *fd = -1;
- } else {
- *errInfo = tmperr;
- }
- return i;
- }
- if ((*fd = open(namebuf2, O_RDONLY, 0)) < 0) {
- i = check_error(-1,errInfo);
- efile_rename(&tmperr,namebuf2,namebuf); /* at least try */
- if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,
- fd,&dummy)) {
- *fd = -1;
- } else {
- lseek(*fd,offset,SEEK_SET);
- }
- return i;
- }
- /* Point of no return... */
-
- if ((new = open(namebuf,O_RDWR | O_CREAT, FILE_MODE)) < 0) {
- close(*fd);
- *fd = -1;
- return 0;
- }
- left = offset;
-
- while (left) {
- if ((i = read(*fd,buff,(left > 1024) ? 1024 : left)) < 0) {
- i = check_error(-1,errInfo);
- close(new);
- close(*fd);
- unlink(namebuf);
- efile_rename(&tmperr,namebuf2,namebuf); /* at least try */
- if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,
- fd,&dummy)) {
- *fd = -1;
- } else {
- lseek(*fd,offset,SEEK_SET);
- }
- return i;
- }
- left -= i;
- if (write(new,buff,i) < 0) {
- i = check_error(-1,errInfo);
- close(new);
- close(*fd);
- unlink(namebuf);
- rename(namebuf2,namebuf); /* at least try */
- if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,
- fd,&dummy)) {
- *fd = -1;
- } else {
- lseek(*fd,offset,SEEK_SET);
- }
- return i;
- }
- }
- close(*fd);
- unlink(namebuf2);
- close(new);
- i = efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,fd,
- &dummy);
- if (i) {
- lseek(*fd,offset,SEEK_SET);
- }
- return i;
- }
- return 1;
-#else
#ifndef NO_FTRUNCATE
off_t offset;
@@ -1347,15 +805,11 @@ efile_truncate_file(Efile_error* errInfo, int *fd, int flags)
#else
return 1;
#endif
-#endif
}
int
efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size)
{
-#ifdef VXWORKS
- return vxworks_enotsup(errInfo);
-#else
int len;
ASSERT(size > 0);
len = readlink(name, buffer, size-1);
@@ -1364,7 +818,6 @@ efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size)
}
buffer[len] = '\0';
return 1;
-#endif
}
int
@@ -1377,21 +830,13 @@ efile_altname(Efile_error* errInfo, char* name, char* buffer, size_t size)
int
efile_link(Efile_error* errInfo, char* old, char* new)
{
-#ifdef VXWORKS
- return vxworks_enotsup(errInfo);
-#else
return check_error(link(old, new), errInfo);
-#endif
}
int
efile_symlink(Efile_error* errInfo, char* old, char* new)
{
-#ifdef VXWORKS
- return vxworks_enotsup(errInfo);
-#else
return check_error(symlink(old, new), errInfo);
-#endif
}
int
diff --git a/erts/emulator/drivers/win32/registry_drv.c b/erts/emulator/drivers/win32/registry_drv.c
index 1fad34e380..5b200ebd32 100644
--- a/erts/emulator/drivers/win32/registry_drv.c
+++ b/erts/emulator/drivers/win32/registry_drv.c
@@ -344,7 +344,7 @@ fix_value_result(RegPort* rp, LONG result, DWORD type,
#ifdef DEBUG
if (ok != ERROR_SUCCESS) {
char buff[256];
- sprintf(buff,"Failure in registry_drv line %d, error = %d",
+ erts_snprintf(buff, sizeof(buff), "Failure in registry_drv line %d, error = %d",
__LINE__, GetLastError());
MessageBox(NULL, buff, "Internal error", MB_OK);
ASSERT(ok == ERROR_SUCCESS);
diff --git a/erts/emulator/drivers/win32/win_con.c b/erts/emulator/drivers/win32/win_con.c
index 6b45b92cbe..7b9cadd32b 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-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2012. 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
@@ -569,7 +569,7 @@ FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
return 1;
case WM_SETFOCUS :
CreateCaret(hClientWnd, NULL, cxChar, cyChar);
- SetCaretPos(GetXFromCurrentY(GetDC(hwnd),0,cur_x), (cur_y-iVscrollPos)*cyChar);
+ SetCaretPos(GetXFromCurrentY(GetDC(hClientWnd),iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
ShowCaret(hClientWnd);
return 0;
case WM_KILLFOCUS:
diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c
index 606fa1d7de..dc7add01f7 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-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2012. 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
@@ -897,7 +897,8 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
we should be able to find its target */
WCHAR target_name[_MAX_PATH];
if (efile_readlink(errInfo, (char *) name,
- (char *) target_name,256) == 1) {
+ (char *) target_name,
+ _MAX_PATH * sizeof(WCHAR)) == 1) {
FindClose(findhandle);
return efile_fileinfo(errInfo, pInfo,
(char *) target_name, info_for_link);
@@ -1386,7 +1387,7 @@ efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size)
HANDLE h = CreateFileW(wname, GENERIC_READ, 0,NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
int len;
if(h != INVALID_HANDLE_VALUE) {
- success = pGetFinalPathNameByHandle(h, wbuffer, size,0);
+ success = pGetFinalPathNameByHandle(h, wbuffer, size / sizeof(WCHAR),0);
/* GetFinalPathNameByHandle prepends path with "\\?\": */
len = wcslen(wbuffer);
wmemmove(wbuffer,wbuffer+4,len-3);
diff --git a/erts/emulator/hipe/hipe_arm.c b/erts/emulator/hipe/hipe_arm.c
index 651d0e3a75..3db3ffe9b1 100644
--- a/erts/emulator/hipe/hipe_arm.c
+++ b/erts/emulator/hipe/hipe_arm.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2012. 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/hipe/hipe_arm_bifs.m4 b/erts/emulator/hipe/hipe_arm_bifs.m4
index 17c013f1fb..bd8bc5ab6b 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-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2012. 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/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c
index af593229c0..1562748f2d 100644
--- a/erts/emulator/hipe/hipe_bif0.c
+++ b/erts/emulator/hipe/hipe_bif0.c
@@ -609,8 +609,8 @@ static Uint *hipe_find_emu_address(Eterm mod, Eterm name, unsigned int arity)
Uint *code_base;
int i, n;
- modp = erts_get_module(mod);
- if (modp == NULL || (code_base = modp->code) == NULL)
+ modp = erts_get_module(mod, erts_active_code_ix());
+ if (modp == NULL || (code_base = modp->curr.code) == NULL)
return NULL;
n = code_base[MI_NUM_FUNCTIONS];
for (i = 0; i < n; ++i) {
@@ -648,7 +648,7 @@ static void *hipe_get_emu_address(Eterm m, Eterm f, unsigned int arity, int is_r
/* if not found, stub it via the export entry */
/* no lock needed around erts_export_get_or_make_stub() */
Export *export_entry = erts_export_get_or_make_stub(m, f, arity);
- address = export_entry->address;
+ address = export_entry->addressv[erts_active_code_ix()];
}
return address;
}
@@ -1583,14 +1583,6 @@ BIF_RETTYPE hipe_nonclosure_address(BIF_ALIST_2)
goto badfun;
m = ep->code[0];
f = ep->code[1];
- } else if (hdr == make_arityval(2)) {
- Eterm *tp = tuple_val(BIF_ARG_1);
- m = tp[1];
- f = tp[2];
- if (is_not_atom(m) || is_not_atom(f))
- goto badfun;
- if (!erts_find_export_entry(m, f, BIF_ARG_2))
- goto badfun;
} else
goto badfun;
address = hipe_get_na_nofail(m, f, BIF_ARG_2, 1);
@@ -1755,6 +1747,41 @@ BIF_RETTYPE hipe_bifs_mark_referred_from_1(BIF_ALIST_1) /* get_refs_from */
BIF_RET(NIL);
}
+/* Called by init:restart after unloading all hipe compiled modules
+ * to work around bug causing execution of deallocated beam code.
+ * Can be removed when delete/purge of native modules works better.
+ * Test: Do init:restart in debug compiled vm with hipe compiled kernel.
+ */
+static void hipe_purge_all_refs(void)
+{
+ struct hipe_mfa_info **bucket;
+ unsigned int i, nrbuckets;
+
+ hipe_mfa_info_table_lock();
+
+ bucket = hipe_mfa_info_table.bucket;
+ nrbuckets = 1 << hipe_mfa_info_table.log2size;
+ for (i = 0; i < nrbuckets; ++i) {
+ while (bucket[i] != NULL) {
+ struct hipe_mfa_info* mfa = bucket[i];
+ bucket[i] = mfa->bucket.next;
+
+ while (mfa->refers_to) {
+ struct hipe_mfa_info_list *to = mfa->refers_to;
+ mfa->refers_to = to->next;
+ erts_free(ERTS_ALC_T_HIPE, to);
+ }
+ while (mfa->referred_from) {
+ struct ref* from = mfa->referred_from;
+ mfa->referred_from = from->next;
+ erts_free(ERTS_ALC_T_HIPE, from);
+ }
+ erts_free(ERTS_ALC_T_HIPE, mfa);
+ }
+ }
+ hipe_mfa_info_table_unlock();
+}
+
BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1)
{
struct mfa mfa;
@@ -1762,6 +1789,11 @@ BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1)
struct hipe_mfa_info_list *refers_to, *tmp_refers_to;
struct ref **prev, *ref;
+ if (BIF_ARG_1 == am_all) {
+ hipe_purge_all_refs();
+ BIF_RET(NIL);
+ }
+
if (!term_to_mfa(BIF_ARG_1, &mfa))
BIF_ERROR(BIF_P, BADARG);
hipe_mfa_info_table_lock();
@@ -1799,6 +1831,7 @@ BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1)
BIF_RET(NIL);
}
+
/* redirect_referred_from(CalleeMFA)
* Redirect all pending-redirect refs in CalleeMFA's referred_from.
* Then remove any pending-redirect && pending-remove refs from CalleeMFA's referred_from.
diff --git a/erts/emulator/hipe/hipe_bif1.c b/erts/emulator/hipe/hipe_bif1.c
index 64de754e18..56767ef04b 100644
--- a/erts/emulator/hipe/hipe_bif1.c
+++ b/erts/emulator/hipe/hipe_bif1.c
@@ -2,7 +2,7 @@
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2012. 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/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c
index 37a1cc193b..e09988e2c5 100644
--- a/erts/emulator/hipe/hipe_bif2.c
+++ b/erts/emulator/hipe/hipe_bif2.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2012. 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
@@ -173,3 +173,10 @@ BIF_RETTYPE hipe_debug_bif_wrapper(BIF_ALIST_1)
#endif /* ERTS_ENABLE_LOCK_CHECK && ERTS_SMP */
+
+BIF_RETTYPE hipe_bifs_debug_native_called_2(BIF_ALIST_2)
+{
+ erts_printf("hipe_debug_native_called: %T(%T)\r\n", BIF_ARG_1, BIF_ARG_2);
+ BIF_RET(am_ok);
+}
+
diff --git a/erts/emulator/hipe/hipe_bif2.tab b/erts/emulator/hipe/hipe_bif2.tab
index aac27e8bed..45a395bf57 100644
--- a/erts/emulator/hipe/hipe_bif2.tab
+++ b/erts/emulator/hipe/hipe_bif2.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. 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
@@ -29,3 +29,4 @@ bif hipe_bifs:show_term/1
bif hipe_bifs:in_native/0
bif hipe_bifs:modeswitch_debug_on/0
bif hipe_bifs:modeswitch_debug_off/0
+bif hipe_bifs:debug_native_called/2
diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4
index 942fa0c5cb..d9aa09b79e 100644
--- a/erts/emulator/hipe/hipe_bif_list.m4
+++ b/erts/emulator/hipe/hipe_bif_list.m4
@@ -165,6 +165,7 @@ gc_bif_interface_2(nbif_put_2, put_2)
gc_bif_interface_1(nbif_hipe_bifs_show_nstack_1, hipe_show_nstack_1)
gc_bif_interface_1(nbif_hipe_bifs_show_pcb_1, hipe_bifs_show_pcb_1)
gc_bif_interface_0(nbif_hipe_bifs_nstack_used_size_0, hipe_bifs_nstack_used_size_0)
+gc_bif_interface_2(nbif_hipe_bifs_debug_native_called, hipe_bifs_debug_native_called_2)
/*
* Arithmetic operators called indirectly by the HiPE compiler.
diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c
index 7ca11f8c6c..37615bf718 100644
--- a/erts/emulator/hipe/hipe_debug.c
+++ b/erts/emulator/hipe/hipe_debug.c
@@ -188,8 +188,6 @@ void hipe_print_pcb(Process *p)
U("old_htop ", old_htop);
U("old_head ", old_heap);
U("min_heap_..", min_heap_size);
- U("status ", status);
- U("rstatus ", rstatus);
U("rcount ", rcount);
U("id ", id);
U("prio ", prio);
diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c
index 07e4b8a4d6..86c4068072 100644
--- a/erts/emulator/hipe/hipe_gc.c
+++ b/erts/emulator/hipe/hipe_gc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2012. 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/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c
index 6e9041c84a..cbbf1db2e5 100644
--- a/erts/emulator/hipe/hipe_mkliterals.c
+++ b/erts/emulator/hipe/hipe_mkliterals.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2012. 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/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c
index 6a3ce5608f..3c782b2f56 100644
--- a/erts/emulator/hipe/hipe_mode_switch.c
+++ b/erts/emulator/hipe/hipe_mode_switch.c
@@ -360,7 +360,8 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
p->flags &= ~F_HIBERNATE_SCHED;
goto do_schedule;
}
- if (p->status == P_WAITING) {
+
+ if (!(erts_smp_atomic32_read_acqb(&p->state) & ERTS_PSFLG_ACTIVE)) {
for (i = 0; i < p->arity; ++i)
p->arg_reg[i] = reg[i];
goto do_schedule;
@@ -451,10 +452,6 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
case HIPE_MODE_SWITCH_RES_SUSPEND: {
p->i = hipe_beam_pc_resume;
p->arity = 0;
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- if (p->status != P_SUSPENDED)
- erts_add_to_runq(p);
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
goto do_schedule;
}
case HIPE_MODE_SWITCH_RES_WAIT:
@@ -470,7 +467,8 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
#endif
p->i = hipe_beam_pc_resume;
p->arity = 0;
- p->status = P_WAITING;
+ erts_smp_atomic32_read_band_relb(&p->state,
+ ~ERTS_PSFLG_ACTIVE);
erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_MSG_RECEIVE);
do_schedule:
{
diff --git a/erts/emulator/hipe/hipe_native_bif.h b/erts/emulator/hipe/hipe_native_bif.h
index 9e3a156fbc..3f460a5a5c 100644
--- a/erts/emulator/hipe/hipe_native_bif.h
+++ b/erts/emulator/hipe/hipe_native_bif.h
@@ -110,6 +110,9 @@ int hipe_bs_put_big_integer(Eterm, Uint, byte*, unsigned, unsigned);
AEXTERN(Eterm,nbif_check_get_msg,(Process*));
Eterm hipe_check_get_msg(Process*);
+AEXTERN(BIF_RETTYPE,nbif_hipe_bifs_debug_native_called,(Process*,Eterm,Eterm));
+BIF_RETTYPE hipe_bifs_debug_native_called_2(BIF_ALIST_2);
+
/*
* SMP-specific stuff
*/
diff --git a/erts/emulator/hipe/hipe_ppc_bifs.m4 b/erts/emulator/hipe/hipe_ppc_bifs.m4
index d09551d10d..7cc2b5c7b6 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-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2012. 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
@@ -32,12 +32,12 @@ include(`hipe/hipe_ppc_asm.m4')
.text
.p2align 2
-define(TEST_GOT_MBUF,`LOAD r4, P_MBUF(P) # `TEST_GOT_MBUF'
+define(TEST_GOT_MBUF,`LOAD r4, P_MBUF(P) /* `TEST_GOT_MBUF' */
CMPI r4, 0
bne- 3f
2:')
define(HANDLE_GOT_MBUF,`
-3: bl CSYM(nbif_$1_gc_after_bif) # `HANDLE_GOT_MBUF'
+3: bl CSYM(nbif_$1_gc_after_bif) /* `HANDLE_GOT_MBUF' */
b 2b')
@@ -62,7 +62,7 @@ ASYM($1):
/* Save caller-save registers and call the C function. */
SAVE_CONTEXT_BIF
- STORE r4, P_ARG0(r3) # Store BIF__ARGS in def_arg_reg[]
+ STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */
addi r4, r3, P_ARG0
CALL_BIF($2)
TEST_GOT_MBUF
@@ -92,7 +92,7 @@ ASYM($1):
/* Save caller-save registers and call the C function. */
SAVE_CONTEXT_BIF
- STORE r4, P_ARG0(r3) # Store BIF__ARGS in def_arg_reg[]
+ STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */
STORE r5, P_ARG1(r3)
addi r4, r3, P_ARG0
CALL_BIF($2)
@@ -124,7 +124,7 @@ ASYM($1):
/* Save caller-save registers and call the C function. */
SAVE_CONTEXT_BIF
- STORE r4, P_ARG0(r3) # Store BIF__ARGS in def_arg_reg[]
+ 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
@@ -214,7 +214,7 @@ ASYM($1):
/* 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 r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */
addi r4, r3, P_ARG0
CALL_BIF($2)
TEST_GOT_MBUF
@@ -244,7 +244,7 @@ ASYM($1):
/* 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 r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */
STORE r5, P_ARG1(r3)
addi r4, r3, P_ARG0
CALL_BIF($2)
diff --git a/erts/emulator/hipe/hipe_primops.h b/erts/emulator/hipe/hipe_primops.h
index 38509c105b..52b4681cfe 100644
--- a/erts/emulator/hipe/hipe_primops.h
+++ b/erts/emulator/hipe/hipe_primops.h
@@ -80,6 +80,7 @@ PRIMOP_LIST(am_fclearerror_error, &nbif_fclearerror_error)
#ifdef NO_FPE_SIGNALS
PRIMOP_LIST(am_emulate_fpe, &nbif_emulate_fpe)
#endif
+PRIMOP_LIST(am_debug_native_called, &nbif_hipe_bifs_debug_native_called)
#if defined(__sparc__)
#include "hipe_sparc_primops.h"
diff --git a/erts/emulator/hipe/hipe_stack.c b/erts/emulator/hipe/hipe_stack.c
index da462a64e1..53c316ba52 100644
--- a/erts/emulator/hipe/hipe_stack.c
+++ b/erts/emulator/hipe/hipe_stack.c
@@ -130,7 +130,7 @@ struct sdesc *hipe_decode_sdesc(Eterm arg)
struct sdesc *sdesc;
if (is_not_tuple(arg) ||
- (tuple_val(arg))[0] != make_arityval(5) ||
+ (tuple_val(arg))[0] != make_arityval(6) ||
term_to_Uint((tuple_val(arg))[1], &ra) == 0 ||
term_to_Uint((tuple_val(arg))[2], &exnra) == 0 ||
is_not_small((tuple_val(arg))[3]) ||
@@ -183,5 +183,13 @@ struct sdesc *hipe_decode_sdesc(Eterm arg)
off = unsigned_val(live[i]);
sdesc->livebits[off / 32] |= (1 << (off & 31));
}
+#ifdef DEBUG
+ {
+ Eterm mfa_tpl = tuple_val(arg)[6];
+ sdesc->dbg_M = tuple_val(mfa_tpl)[1];
+ sdesc->dbg_F = tuple_val(mfa_tpl)[2];
+ sdesc->dbg_A = tuple_val(mfa_tpl)[3];
+ }
+#endif
return sdesc;
}
diff --git a/erts/emulator/hipe/hipe_stack.h b/erts/emulator/hipe/hipe_stack.h
index 4e3076caf5..66f9f04c73 100644
--- a/erts/emulator/hipe/hipe_stack.h
+++ b/erts/emulator/hipe/hipe_stack.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2012. 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
@@ -35,6 +35,10 @@ struct sdesc {
struct sdesc *next; /* hash collision chain */
} bucket;
unsigned int summary; /* frame size, exn handler presence flag, arity */
+#ifdef DEBUG
+ Eterm dbg_M, dbg_F;
+ unsigned dbg_A;
+#endif
unsigned int livebits[1]; /* size depends on arch & data in summary field */
};
diff --git a/erts/emulator/hipe/hipe_x86_gc.h b/erts/emulator/hipe/hipe_x86_gc.h
index e4607ad27d..aa4abb6f59 100644
--- a/erts/emulator/hipe/hipe_x86_gc.h
+++ b/erts/emulator/hipe/hipe_x86_gc.h
@@ -69,6 +69,11 @@ nstack_walk_init_sdesc(const Process *p, struct nstack_walk_state *state)
nstkarity = 0;
state->sdesc0[0].summary = (0 << 9) | (0 << 8) | nstkarity;
state->sdesc0[0].livebits[0] = 0;
+# ifdef DEBUG
+ state->sdesc0[0].dbg_M = 0;
+ state->sdesc0[0].dbg_F = am_init;
+ state->sdesc0[0].dbg_A = 0;
+# endif
/* XXX: this appears to prevent a gcc-4.1.1 bug on x86 */
__asm__ __volatile__("" : : "m"(*state) : "memory");
return &state->sdesc0[0];
diff --git a/erts/emulator/hipe/hipe_x86_glue.h b/erts/emulator/hipe/hipe_x86_glue.h
index b0db93267c..63ad250d60 100644
--- a/erts/emulator/hipe/hipe_x86_glue.h
+++ b/erts/emulator/hipe/hipe_x86_glue.h
@@ -62,6 +62,9 @@ static __inline__ void hipe_arch_glue_init(void)
.sdesc = {
.bucket = { .hvalue = (unsigned long)nbif_return },
.summary = (1<<8),
+ #ifdef DEBUG
+ .dbg_F = am_return,
+ #endif
},
};
hipe_init_sdesc_table(&nbif_return_sdesc.sdesc);
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index c1336c60d9..ce014c19c2 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -39,7 +39,6 @@
#include "dtrace-wrapper.h"
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
-# define ERTS_DRV_EV_STATE_EXTRA_SIZE 128
#else
# include "safe_hash.h"
# define DRV_EV_STATE_HTAB_SIZE 1024
@@ -334,7 +333,9 @@ static void
grow_drv_ev_state(int min_ix)
{
int i;
- int new_len = min_ix + 1 + ERTS_DRV_EV_STATE_EXTRA_SIZE;
+ int new_len;
+
+ new_len = ERTS_POLL_EXPORT(erts_poll_get_table_len)(min_ix + 1);
if (new_len > max_fds)
new_len = max_fds;
diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c
index 26858052c4..a523d67158 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-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2012. 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
@@ -55,17 +55,12 @@
# ifdef SYS_SELECT_H
# include <sys/select.h>
# endif
-# ifdef VXWORKS
-# include <selectLib.h>
-# endif
#endif
-#ifndef VXWORKS
-# ifdef NO_SYSCONF
-# if ERTS_POLL_USE_SELECT
-# include <sys/param.h>
-# else
-# include <limits.h>
-# endif
+#ifdef NO_SYSCONF
+# if ERTS_POLL_USE_SELECT
+# include <sys/param.h>
+# else
+# include <limits.h>
# endif
#endif
#include "erl_thr_progress.h"
@@ -105,8 +100,8 @@
#define ERTS_POLL_COALESCE_KP_RES (ERTS_POLL_USE_KQUEUE || ERTS_POLL_USE_EPOLL)
-#define FDS_STATUS_EXTRA_FREE_SIZE 128
-#define POLL_FDS_EXTRA_FREE_SIZE 128
+#define ERTS_EV_TABLE_MIN_LENGTH 1024
+#define ERTS_EV_TABLE_EXP_THRESHOLD (2048*1024)
#ifdef ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT
# define ERTS_POLL_ASYNC_INTERRUPT_SUPPORT 1
@@ -563,6 +558,28 @@ free_update_requests_block(ErtsPollSet ps,
* --- Growing poll set structures -------------------------------------------
*/
+int
+ERTS_POLL_EXPORT(erts_poll_get_table_len) (int new_len)
+{
+ if (new_len < ERTS_EV_TABLE_MIN_LENGTH) {
+ new_len = ERTS_EV_TABLE_MIN_LENGTH;
+ } else if (new_len < ERTS_EV_TABLE_EXP_THRESHOLD) {
+ /* find next power of 2 */
+ --new_len;
+ new_len |= new_len >> 1;
+ new_len |= new_len >> 2;
+ new_len |= new_len >> 4;
+ new_len |= new_len >> 8;
+ new_len |= new_len >> 16;
+ ++new_len;
+ } else {
+ /* grow incrementally */
+ new_len += ERTS_EV_TABLE_EXP_THRESHOLD;
+ }
+ return new_len;
+}
+
+
#if ERTS_POLL_USE_KERNEL_POLL
static void
grow_res_events(ErtsPollSet ps, int new_len)
@@ -575,7 +592,7 @@ grow_res_events(ErtsPollSet ps, int new_len)
#elif ERTS_POLL_USE_KQUEUE
struct kevent
#endif
- )*new_len;
+ ) * ERTS_POLL_EXPORT(erts_poll_get_table_len)(new_len);
/* We do not need to save previously stored data */
if (ps->res_events)
erts_free(ERTS_ALC_T_POLL_RES_EVS, ps->res_events);
@@ -589,7 +606,7 @@ static void
grow_poll_fds(ErtsPollSet ps, int min_ix)
{
int i;
- int new_len = min_ix + 1 + POLL_FDS_EXTRA_FREE_SIZE;
+ int new_len = ERTS_POLL_EXPORT(erts_poll_get_table_len)(min_ix + 1);
if (new_len > max_fds)
new_len = max_fds;
ps->poll_fds = (ps->poll_fds_len
@@ -611,7 +628,7 @@ static void
grow_fds_status(ErtsPollSet ps, int min_fd)
{
int i;
- int new_len = min_fd + 1 + FDS_STATUS_EXTRA_FREE_SIZE;
+ int new_len = ERTS_POLL_EXPORT(erts_poll_get_table_len)(min_fd + 1);
ASSERT(min_fd < max_fds);
if (new_len > max_fds)
new_len = max_fds;
@@ -2200,10 +2217,6 @@ ERTS_POLL_EXPORT(erts_poll_max_fds)(void)
* --- Initialization --------------------------------------------------------
*/
-#ifdef VXWORKS
-extern int erts_vxworks_max_files;
-#endif
-
void
ERTS_POLL_EXPORT(erts_poll_init)(void)
{
@@ -2212,9 +2225,7 @@ ERTS_POLL_EXPORT(erts_poll_init)(void)
errno = 0;
-#if defined(VXWORKS)
- max_fds = erts_vxworks_max_files;
-#elif !defined(NO_SYSCONF)
+#if !defined(NO_SYSCONF)
max_fds = sysconf(_SC_OPEN_MAX);
#elif ERTS_POLL_USE_SELECT
max_fds = NOFILE;
diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h
index 8dde619105..502290e4bb 100644
--- a/erts/emulator/sys/common/erl_poll.h
+++ b/erts/emulator/sys/common/erl_poll.h
@@ -246,4 +246,6 @@ void ERTS_POLL_EXPORT(erts_poll_get_selected_events)(ErtsPollSet,
ErtsPollEvents [],
int);
+int ERTS_POLL_EXPORT(erts_poll_get_table_len)(int);
+
#endif /* #ifndef ERL_POLL_H__ */
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index bf69f3bf90..97756e8434 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2012. 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
@@ -551,7 +551,7 @@ erl_sys_init(void)
size_t bindirsz = sizeof(bindir);
Uint csp_path_sz;
- res = erts_sys_getenv("BINDIR", bindir, &bindirsz);
+ res = erts_sys_getenv_raw("BINDIR", bindir, &bindirsz);
if (res != 0) {
if (res < 0)
erl_exit(-1,
@@ -570,7 +570,7 @@ erl_sys_init(void)
+ 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);
- sprintf(child_setup_prog,
+ erts_snprintf(child_setup_prog, csp_path_sz,
"%s%c%s",
bindir,
DIR_SEPARATOR_CHAR,
@@ -708,7 +708,7 @@ prepare_crash_dump(void)
}
envsz = sizeof(env);
- i = erts_sys_getenv("ERL_CRASH_DUMP_NICE", env, &envsz);
+ i = erts_sys_getenv_raw("ERL_CRASH_DUMP_NICE", env, &envsz);
if (i >= 0) {
int nice_val;
nice_val = i != 0 ? 0 : atoi(env);
@@ -719,7 +719,7 @@ prepare_crash_dump(void)
}
envsz = sizeof(env);
- i = erts_sys_getenv("ERL_CRASH_DUMP_SECONDS", env, &envsz);
+ i = erts_sys_getenv_raw("ERL_CRASH_DUMP_SECONDS", env, &envsz);
if (i >= 0) {
unsigned sec;
sec = (unsigned) i != 0 ? 0 : atoi(env);
@@ -1356,9 +1356,9 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op
int no_vfork;
size_t no_vfork_sz = sizeof(no_vfork);
- no_vfork = (erts_sys_getenv("ERL_NO_VFORK",
- (char *) &no_vfork,
- &no_vfork_sz) >= 0);
+ no_vfork = (erts_sys_getenv_raw("ERL_NO_VFORK",
+ (char *) &no_vfork,
+ &no_vfork_sz) >= 0);
#endif
switch (opts->read_write) {
@@ -1532,12 +1532,13 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op
}
#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[44]; /* 44 bytes are enough to */
- char dup2_op[CS_ARGV_NO_OF_DUP2_OPS][44]; /* hold any "%d:%d" string */
- /* on a 64-bit machine. */
+ 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) */
@@ -1545,23 +1546,23 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op
if (opts->use_stdio) {
if (opts->read_write & DO_READ){
/* stdout for process */
- sprintf(&dup2_op[i++][0], "%d:%d", ifd[1], 1);
+ erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 1);
if(opts->redir_stderr)
/* stderr for process */
- sprintf(&dup2_op[i++][0], "%d:%d", ifd[1], 2);
+ erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 2);
}
if (opts->read_write & DO_WRITE)
/* stdin for process */
- sprintf(&dup2_op[i++][0], "%d:%d", ofd[0], 0);
+ 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)
- sprintf(&dup2_op[i++][0], "%d:%d", ifd[1], 4);
+ erts_snprintf(&dup2_op[i++][0], ENOUGH_BYTES, "%d:%d", ifd[1], 4);
if (opts->read_write & DO_WRITE)
- sprintf(&dup2_op[i++][0], "%d:%d", ofd[0], 3);
+ 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], "-");
- sprintf(fd_close_range, "%d:%d", opts->use_stdio ? 3 : 5, max_files-1);
+ 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 : ".";
@@ -1612,6 +1613,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op
}
erts_free(ERTS_ALC_T_TMP,cs_argv);
}
+#undef ENOUGH_BYTES
#endif
erts_sched_bind_atfork_parent(unbind);
@@ -2355,28 +2357,38 @@ void erts_do_break_handling(void)
** 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){
+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... */
- sprintf(buffer,"%lu",(unsigned long) p);
+ erts_snprintf(buffer, buffer_size, "%lu",(unsigned long) p);
}
int
-erts_sys_putenv(char *buffer, int sep_ix)
+erts_sys_putenv_raw(char *key, char *value) {
+ return erts_sys_putenv(key, value);
+}
+int
+erts_sys_putenv(char *key, char *value)
{
int res;
char *env;
+ Uint need = strlen(key) + strlen(value) + 2;
+
#ifdef HAVE_COPYING_PUTENV
- env = buffer;
+ env = erts_alloc(ERTS_ALC_T_TMP, need);
#else
- Uint sz = strlen(buffer)+1;
- env = erts_alloc(ERTS_ALC_T_PUTENV_STR, sz);
- erts_smp_atomic_add_nob(&sys_misc_mem_sz, sz);
- strcpy(env,buffer);
+ env = erts_alloc(ERTS_ALC_T_PUTENV_STR, need);
+ erts_smp_atomic_add_nob(&sys_misc_mem_sz, need);
#endif
+ strcpy(env,key);
+ strcat(env,"=");
+ strcat(env,value);
erts_smp_rwmtx_rwlock(&environ_rwmtx);
res = putenv(env);
erts_smp_rwmtx_rwunlock(&environ_rwmtx);
+#ifdef HAVE_COPYING_PUTENV
+ erts_free(ERTS_ALC_T_TMP, env);
+#endif
return res;
}
@@ -2403,6 +2415,11 @@ erts_sys_getenv__(char *key, char *value, size_t *size)
}
int
+erts_sys_getenv_raw(char *key, char *value, size_t *size) {
+ return erts_sys_getenv(key, value, size);
+}
+
+int
erts_sys_getenv(char *key, char *value, size_t *size)
{
int res;
@@ -3001,7 +3018,7 @@ erl_sys_args(int* argc, char** argv)
if (erts_use_kernel_poll) {
char no_kp[10];
size_t no_kp_sz = sizeof(no_kp);
- int res = erts_sys_getenv("ERL_NO_KERNEL_POLL", no_kp, &no_kp_sz);
+ int res = erts_sys_getenv_raw("ERL_NO_KERNEL_POLL", no_kp, &no_kp_sz);
if (res > 0
|| (res == 0
&& sys_strcmp("false", no_kp) != 0
diff --git a/erts/emulator/sys/unix/sys_float.c b/erts/emulator/sys/unix/sys_float.c
index 8ec7b31ce0..3fcb4d88dc 100644
--- a/erts/emulator/sys/unix/sys_float.c
+++ b/erts/emulator/sys/unix/sys_float.c
@@ -745,18 +745,18 @@ void erts_sys_unblock_fpe(int unmasked)
*/
int
-sys_double_to_chars(double fp, char *buf)
+sys_double_to_chars(double fp, char *buffer, size_t buffer_size)
{
- char *s = buf;
+ char *s = buffer;
- (void) sprintf(buf, "%.20e", fp);
+ (void) erts_snprintf(buffer, buffer_size, "%.20e", fp);
/* 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-buf; /* i.e strlen(buf) */
+ return s-buffer; /* i.e strlen(buffer) */
}
/* Float conversion */
diff --git a/erts/emulator/sys/vxworks/driver_int.h b/erts/emulator/sys/vxworks/driver_int.h
deleted file mode 100644
index f6bc71a799..0000000000
--- a/erts/emulator/sys/vxworks/driver_int.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2009. 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
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
-/*----------------------------------------------------------------------
-** Purpose : System dependant driver declarations
-**---------------------------------------------------------------------- */
-
-#ifndef __DRIVER_INT_H__
-#define __DRIVER_INT_H__
-
-#include <ioLib.h>
-
-typedef struct iovec SysIOVec;
-
-#endif
diff --git a/erts/emulator/sys/vxworks/erl_main.c b/erts/emulator/sys/vxworks/erl_main.c
deleted file mode 100644
index c9b44a635a..0000000000
--- a/erts/emulator/sys/vxworks/erl_main.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2000-2009. 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
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include "sys.h"
-#include "erl_vm.h"
-
-#if defined(__GNUC__)
-/*
- * The generated assembler does the usual trick (relative
- * branch-and-link to next instruction) to get a copy of the
- * instruction ptr. Instead of branching to an explicit zero offset,
- * it branches to the symbol `__eabi' --- which is expected to be
- * undefined and thus zero (if it is defined as non-zero, things will
- * be interesting --- as in the Chinese curse). To shut up the VxWorks
- * linker, we define `__eabi' as zero.
- *
- * This is just a work around. It's really Wind River's GCC's code
- * generator that should be fixed.
- */
-__asm__(".equ __eabi, 0");
-#endif
-
-void
-erl_main(int argc, char **argv)
-{
- erl_start(argc, argv);
-}
diff --git a/erts/emulator/sys/vxworks/erl_vxworks_sys.h b/erts/emulator/sys/vxworks/erl_vxworks_sys.h
deleted file mode 100644
index 3d53238ea6..0000000000
--- a/erts/emulator/sys/vxworks/erl_vxworks_sys.h
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2011. 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
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
-#ifndef __ERL_VXWORKS_SYS_H__
-#define __ERL_VXWORKS_SYS_H__
-
-/* stdarg.h don't work without this one... */
-#include <vxWorks.h>
-
-#include <stdio.h>
-#include <math.h>
-#include <limits.h>
-#include <stdlib.h>
-#define index StringIndexFunctionThatIDontWantDeclared
-#include <string.h>
-#undef index
-
-
-
-#include <sys/times.h>
-#include <time.h>/* xxxP */
-
-#include <dirent.h>
-#include <sys/stat.h>
-
-/* xxxP from unix_sys.h begin */
-
-/*
- * Make sure that MAXPATHLEN is defined.
- */
-
-#ifndef MAXPATHLEN
-# ifdef PATH_MAX
-# define MAXPATHLEN PATH_MAX
-# else
-# define MAXPATHLEN 2048
-# endif
-#endif
-
-/* xxxP end */
-
-
-/* Unimplemented math functions */
-#define NO_ASINH
-#define NO_ACOSH
-#define NO_ATANH
-#define NO_ERF
-#define NO_ERFC
-
-/* Stuff that is useful for port programs, drivers, etc */
-#ifndef VXWORKS
-#define VXWORKS
-#endif
-
-#define DONT_USE_MAIN
-#define NO_FSYNC
-#define NO_MKDIR_MODE
-#define NO_UMASK
-#define NO_SYMBOLIC_LINKS
-#define NO_DEVICE_FILES
-#define NO_UID
-#define NO_ACCESS
-#define NO_FCNTL
-#define NO_SYSLOG
-#define NO_SYSCONF
-#define NO_PWD /* XXX Means what? */
-#define NO_DAEMON
-/* This chooses ~250 reductions instead of 500 in config.h */
-#if (CPU == CPU32)
-#define SLOW_PROCESSOR
-#endif
-
-/*
- * Even though we does not always have small memories on VxWorks
- * we certainly does not have virtual memory.
- */
-#if !defined(LARGE_MEMORY)
-#define SMALL_MEMORY
-#endif
-
-/*************** Floating point exception handling ***************/
-
-/* There are no known ways to customize the handling of invalid floating
- point operations, such as matherr() or ieee_handler(), in VxWorks 5.1. */
-
-#if (CPU == MC68040 || CPU == CPU32 || CPU == PPC860 || CPU == PPC32 || \
- CPU == PPC603 || CPU == PPC604 || CPU == SIMSPARCSOLARIS)
-
-/* VxWorks 5.1 on Motorola 68040 never generates SIGFPE, but sets the
- result of invalid floating point ops to Inf and NaN - unfortunately
- the way to test for those values is undocumented and hidden in a
- "private" include file... */
-/* Haven't found any better way, as of yet, for ppc860 xxxP*/
-
-#include <private/mathP.h>
-#define NO_FPE_SIGNALS
-#define erts_get_current_fp_exception() NULL
-#define __ERTS_FP_CHECK_INIT(fpexnp) do {} while (0)
-#define __ERTS_FP_ERROR(fpexnp, f, Action) if (isInf(f) || isNan(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_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_SAVE_FP_EXCEPTION(p) __ERTS_SAVE_FP_EXCEPTION(&(p)->fp_exception)
-#define ERTS_RESTORE_FP_EXCEPTION(p) __ERTS_RESTORE_FP_EXCEPTION(&(p)->fp_exception)
-#define ERTS_FP_ERROR_THOROUGH(p, f, A) __ERTS_FP_ERROR_THOROUGH(&(p)->fp_exception, f, A)
-
-#define erts_sys_block_fpe() 0
-#define erts_sys_unblock_fpe(x) do{}while(0)
-
-#if (CPU == PPC603)
-/* Need fppLib to change the Floating point registers
- (fix_registers in sys.c)*/
-
-#include <fppLib.h>
-
-#endif /* PPC603 */
-
-#else
-
-Unsupported CPU value !
-
-#endif
-
-typedef void *GETENV_STATE;
-
-#define HAVE_GETHRTIME
-
-extern int erts_clock_rate;
-
-#define SYS_CLK_TCK (erts_clock_rate)
-
-#define SYS_CLOCK_RESOLUTION 1
-
-typedef struct _vxworks_tms {
- clock_t tms_utime;
- clock_t tms_stime;
- clock_t tms_cutime;
- clock_t tms_cstime;
-} SysTimes;
-
-typedef long long SysHrTime;
-
-typedef time_t erts_time_t;
-typedef struct timeval SysTimeval;
-
-extern int sys_init_hrtime(void);
-extern SysHrTime sys_gethrtime(void);
-extern void sys_gettimeofday(SysTimeval *tvp);
-extern clock_t sys_times(SysTimes *t);
-
-#define SIZEOF_SHORT 2
-#define SIZEOF_INT 4
-#define SIZEOF_LONG 4
-#define SIZEOF_VOID_P 4
-#define SIZEOF_SIZE_T 4
-#define SIZEOF_OFF_T 4
-
-/*
- * Temporary buffer *only* used in sys code.
- */
-#define SYS_TMP_BUF_SIZE 65536
-
-/* Need to be able to interrupt erts_poll_wait() from signal handler */
-#define ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT
-
-#endif /* __ERL_VXWORKS_SYS_H__ */
diff --git a/erts/emulator/sys/vxworks/erl_vxworks_sys_ddll.c b/erts/emulator/sys/vxworks/erl_vxworks_sys_ddll.c
deleted file mode 100644
index c56c633b2f..0000000000
--- a/erts/emulator/sys/vxworks/erl_vxworks_sys_ddll.c
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2006-2009. 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
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
-
-/*
- * Interface functions to the dynamic linker using dl* functions.
- * (As far as I know it works on SunOS 4, 5, Linux and FreeBSD. /Seb)
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include <vxWorks.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include <a_out.h>
-#include <symLib.h>
-#include <loadLib.h>
-#include <unldLib.h>
-#include <moduleLib.h>
-#include <sysSymTbl.h>
-#include "sys.h"
-#include "global.h"
-#include "erl_alloc.h"
-#include "erl_driver.h"
-
-#define EXT_LEN 4
-#define FILE_EXT ".eld"
-#define ALT_FILE_EXT ".o"
-/* ALT_FILE_EXT must not be longer than FILE_EXT */
-#define DRIVER_INIT_SUFFIX "_init"
-
-static MODULE_ID get_mid(char *);
-static FUNCPTR lookup(char *);
-
-typedef enum {
- NoError,
- ModuleNotFound,
- ModuleNotUnloadable,
- UnknownError
-} FakeSytemError;
-
-static char *errcode_tab[] = {
- "No error",
- "Module/file not found",
- "Module cannot be unloaded",
- "Unknown error"
-};
-
-void erl_sys_ddll_init(void) {
- return;
-}
-/*
- * Open a shared object
- */
-int erts_sys_ddll_open2(char *full_name, void **handle, ErtsSysDdllError* err)
-{
- int len;
-
- if (erts_sys_ddll_open_noext(full_name, handle, err) == ERL_DE_NO_ERROR) {
- return ERL_DE_NO_ERROR;
- }
- if ((len = sys_strlen(full_name)) > PATH_MAX-EXT_LEN) {
- return ERL_DE_LOAD_ERROR_NAME_TO_LONG;
- } else {
- static char dlname[PATH_MAX + 1];
-
- sys_strcpy(dlname, full_name);
- sys_strcpy(dlname+len, FILE_EXT);
- if (erts_sys_ddll_open_noext(dlname, handle, err) == ERL_DE_NO_ERROR) {
- return ERL_DE_NO_ERROR;
- }
- sys_strcpy(dlname+len, ALT_FILE_EXT);
- return erts_sys_ddll_open_noext(dlname, handle, err);
- }
-}
-int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err)
-{
- MODULE_ID mid;
-
- if((mid = get_mid(dlname)) == NULL) {
- return ERL_DE_DYNAMIC_ERROR_OFFSET - ((int) ModuleNotFound);
- }
- *handle = (void *) mid;
- return ERL_DE_NO_ERROR;
-}
-
-/*
- * Find a symbol in the shared object
- */
-#define PREALLOC_BUFFER_SIZE 256
-int erts_sys_ddll_sym2(void *handle, char *func_name, void **function, ErtsSysDdllError* err)
-{
- FUNCPTR proc;
- static char statbuf[PREALLOC_BUFFER_SIZE];
- char *buf = statbuf;
- int need;
-
- if ((proc = lookup(func_name)) == NULL) {
- if ((need = strlen(func_name)+2) > PREALLOC_BUFFER_SIZE) {
- buf = erts_alloc(ERTS_ALC_T_DDLL_TMP_BUF,need);
- }
- buf[0] = '_';
- sys_strcpy(buf+1,func_name);
- proc = lookup(buf);
- if (buf != statbuf) {
- erts_free(ERTS_ALC_T_DDLL_TMP_BUF, buf);
- }
- if (proc == NULL) {
- return ERL_DE_LOOKUP_ERROR_NOT_FOUND;
- }
- }
- *function = (void *) proc;
- return ERL_DE_NO_ERROR;
-}
-
-/* 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)
-{
- MODULE_ID mid = (MODULE_ID) handle;
- char *modname;
- char *cp;
- static char statbuf[PREALLOC_BUFFER_SIZE];
- char *fname = statbuf;
- int len;
- int res;
- void *func;
- int need;
-
- if((modname = moduleNameGet(mid)) == NULL) {
- return ERL_DE_DYNAMIC_ERROR_OFFSET - ((int) ModuleNotFound);
- }
-
- if((cp = strrchr(modname, '.')) == NULL) {
- len = strlen(modname);
- } else {
- len = cp - modname;
- }
-
- need = len + strlen(DRIVER_INIT_SUFFIX) + 1;
- if (need > PREALLOC_BUFFER_SIZE) {
- fname = erts_alloc(ERTS_ALC_T_DDLL_TMP_BUF, need); /* erts_alloc exits on failure */
- }
- sys_strncpy(fname, modname, len);
- fname[len] = '\0';
- sys_strcat(fname, DRIVER_INIT_SUFFIX);
- res = erts_sys_ddll_sym(handle, fname, &func);
- if (fname != statbuf) {
- erts_free(ERTS_ALC_T_DDLL_TMP_BUF, fname);
- }
- if ( res != ERL_DE_NO_ERROR) {
- return res;
- }
- *function = func;
- return ERL_DE_NO_ERROR;
-}
-
-int erts_sys_ddll_load_nif_init(void *handle, void **function, ErtsSysDdllError* err)
-{
- /* NIFs not implemented for vxworks */
- return ERL_DE_ERROR_NO_DDLL_FUNCTIONALITY;
-}
-
-/*
- * 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)
-{
- MODULE_ID mid = (MODULE_ID) handle;
- if (unld(mid, 0) < 0) {
- return ERL_DE_DYNAMIC_ERROR_OFFSET - ((int) ModuleNotUnloadable);
- }
- return ERL_DE_NO_ERROR;
-}
-
-/*
- * Return string that describes the (current) error
- */
-char *erts_sys_ddll_error(int code)
-{
- int actual_code;
- if (code > ERL_DE_DYNAMIC_ERROR_OFFSET) {
- return "Unspecified error";
- }
- actual_code = -1*(code - ERL_DE_DYNAMIC_ERROR_OFFSET);
- if (actual_code > ((int) UnknownError)) {
- actual_code = UnknownError;
- }
- return errcode_tab[actual_code];
-}
-
-static FUNCPTR lookup(char *sym)
-{
- FUNCPTR entry;
- SYM_TYPE type;
-
- if (symFindByNameAndType(sysSymTbl, sym, (char **)&entry,
- &type, N_EXT | N_TEXT, N_EXT | N_TEXT) != OK) {
- return NULL ;
- }
- return entry;
-}
-
-static MODULE_ID get_mid(char* name)
-{
- int fd;
- MODULE_ID mid = NULL;
-
- if((fd = open(name, O_RDONLY, 0664)) >= 0) {
- mid = loadModule(fd, GLOBAL_SYMBOLS);
- close(fd);
- }
- return mid;
-}
-
-void erts_sys_ddll_free_error(ErtsSysDdllError* err)
-{
- /* NYI */
-}
-
diff --git a/erts/emulator/sys/vxworks/sys.c b/erts/emulator/sys/vxworks/sys.c
deleted file mode 100644
index 739b026fb1..0000000000
--- a/erts/emulator/sys/vxworks/sys.c
+++ /dev/null
@@ -1,2610 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2011. 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
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
-/*
- * system-dependent functions
- *
- */
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include <vxWorks.h>
-#include <version.h>
-#include <string.h>
-#include <types.h>
-#include <sigLib.h>
-#include <ioLib.h>
-#include <iosLib.h>
-#include <envLib.h>
-#include <fioLib.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <symLib.h>
-#include <sysLib.h>
-#include <sysSymTbl.h>
-#include <loadLib.h>
-#include <taskLib.h>
-#include <taskVarLib.h>
-#include <taskHookLib.h>
-#include <tickLib.h>
-#include <time.h>
-#include <rngLib.h>
-#include <semLib.h>
-#include <selectLib.h>
-#include <sockLib.h>
-#include <a_out.h>
-#include <wdLib.h>
-#include <timers.h>
-#include <ctype.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <stdarg.h>
-
-
-#ifndef WANT_NONBLOCKING
-#define WANT_NONBLOCKING
-#endif
-
-#include "sys.h"
-#include "erl_alloc.h"
-
-/* 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 "elib_stat.h"
-
-#include "reclaim_private.h" /* Some more or less private reclaim facilities */
-
-#ifndef RETSIGTYPE
-#define RETSIGTYPE void
-#endif
-
-EXTERN_FUNCTION(void, erl_start, (int, char**));
-EXTERN_FUNCTION(void, erl_exit, (int n, char*, _DOTS_));
-EXTERN_FUNCTION(void, erl_error, (char*, va_list));
-EXTERN_FUNCTION(int, driver_interrupt, (int, int));
-EXTERN_FUNCTION(void, increment_time, (int));
-EXTERN_FUNCTION(int, erts_next_time, (_VOID_));
-EXTERN_FUNCTION(void, set_reclaim_free_function, (FreeFunction));
-EXTERN_FUNCTION(int, erl_mem_info_get, (MEM_PART_STATS *));
-EXTERN_FUNCTION(void, erl_crash_dump, (char* file, int line, char* fmt, ...));
-
-#define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG)
-
-/* these are defined in usrLib.c */
-extern int spTaskPriority, spTaskOptions;
-
-/* forward declarations */
-static FUNCTION(FUNCPTR, lookup, (char*));
-static FUNCTION(int, read_fill, (int, char*, int));
-#if (CPU == SPARC)
-static FUNCTION(RETSIGTYPE, fpe_sig_handler, (int)); /*where is this fun? */
-#elif (CPU == PPC603)
-static FUNCTION(void, fix_registers, (void));
-#endif
-static FUNCTION(void, close_pipes, (int*, int*, int));
-static FUNCTION(void, delete_hook, (void));
-static FUNCTION(void, initialize_allocation, (void));
-
-FUNCTION(STATUS, uxPipeDrv, (void));
-FUNCTION(STATUS, pipe, (int*));
-FUNCTION(void, uxPipeShow, (int));
-
-void erl_main(int argc, char **argv);
-void argcall(char *args);
-
-/* Malloc-realted functions called from the VxWorks shell */
-EXTERN_FUNCTION(int, erl_set_memory_block,
- (int, int, int, int, int, int, int, int, int, int));
-EXTERN_FUNCTION(int, erl_memory_show,
- (int, int, int, int, int, int, int, int, int, int));
-
-#define DEFAULT_PORT_STACK_SIZE 100000
-static int port_stack_size;
-
-static int erlang_id = 0; /* Inited at loading, set/reset at each run */
-
-/* interval time reported to emulator */
-static int sys_itime;
-
-/* XXX - This is defined in .../config/all/configAll.h (NUM_FILES),
- and not easily accessible at compile or run time - however,
- in VxWorks 5.1 it is stored in the (undocumented?) maxFiles variable;
- probably shouldn't depend on it, but we try to pick it up... */
-static int max_files = 50; /* default configAll.h */
-
-int erts_vxworks_max_files;
-
-/*
- * used by the break handler (set by signal handler on ctl-c)
- */
-volatile int erts_break_requested;
-
-/********************* General functions ****************************/
-
-/*
- * Reset the terminal to the original settings on exit
- * (nothing to do for WxWorks).
- */
-void sys_tty_reset(int exit_code)
-{
-}
-
-Uint
-erts_sys_misc_mem_sz(void)
-{
- Uint res = erts_check_io_size();
- /* res += FIXME */
- return res;
-}
-
-/*
- * XXX This declaration should not be here.
- */
-void erl_sys_schedule_loop(void);
-
-#ifdef SOFTDEBUG
-static void do_trace(int line, char *file, char *format, ...)
-{
- va_list va;
- int tid = taskIdSelf();
- char buff[512];
-
- va_start(va, format);
- sprintf(buff,"Trace: Task: 0x%08x, %s:%d - ",
- tid, file, line);
- vsprintf(buff + strlen(buff), format, va);
- va_end(va);
- strcat(buff,"\r\n");
- write(2,buff,strlen(buff));
-}
-
-#define TRACE() do_trace(__LINE__, __FILE__,"")
-#define TRACEF(Args...) do_trace(__LINE__,__FILE__, ## Args)
-#endif
-
-void
-erts_sys_pre_init(void)
-{
- if (erlang_id != 0) {
- /* NOTE: This particular case must *not* call erl_exit() */
- erts_fprintf(stderr, "Sorry, erlang is already running (as task %d)\n",
- erlang_id);
- exit(1);
- }
-
- /* This must be done as early as possible... */
- if(!reclaim_init())
- fprintf(stderr, "Warning : reclaim facility should be initiated before "
- "erlang is started!\n");
- erts_vxworks_max_files = max_files = reclaim_max_files();
-
- /* Floating point exceptions */
-#if (CPU == SPARC)
- sys_sigset(SIGFPE, fpe_sig_handler);
-#elif (CPU == PPC603)
- fix_registers();
-#endif
-
- /* register the private delete hook in reclaim */
- save_delete_hook((FUNCPTR)delete_hook, (caddr_t)0);
- erlang_id = taskIdSelf();
-#ifdef DEBUG
- printf("emulator task id = 0x%x\n", erlang_id);
-#endif
-}
-
-void erts_sys_alloc_init(void)
-{
- initialize_allocation();
-}
-
-void
-erl_sys_init(void)
-{
- setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ);
- /* XXX Bug in VxWorks stdio loses fputch()'ed output after the
- setvbuf() but before a *printf(), and possibly worse (malloc
- errors, crash?) - so let's give it a *printf().... */
- fprintf(stdout, "%s","");
-}
-
-void
-erl_sys_args(int* argc, char** argv)
-{
- erts_init_check_io();
- max_files = erts_check_io_max_files();
- ASSERT(max_files <= erts_vxworks_max_files);
-}
-
-void
-erts_sys_schedule_interrupt(int set)
-{
- erts_check_io_interrupt(set);
-}
-
-/*
- * 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)
-{
- erts_check_io(!runnable);
-}
-
-void erts_do_break_handling(void)
-{
- SET_BLOCKING(0);
- /* call the break handling function, reset the flag */
- do_break();
- erts_break_requested = 0;
- SET_NONBLOCKING(0);
-}
-
-/* signal handling */
-RETSIGTYPE (*sys_sigset(sig, func))()
- int sig;
- RETSIGTYPE (*func)();
-{
- struct sigaction act, oact;
-
- sigemptyset(&act.sa_mask);
- act.sa_flags = 0;
- act.sa_handler = func;
- sigaction(sig, &act, &oact);
- return(oact.sa_handler);
-}
-
-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);
-}
-
-void
-erts_sys_prepare_crash_dump(void)
-{
-
-}
-
-/* register signal handlers XXX - they don't work, need to find out why... */
-/* set up signal handlers for break and quit */
-static void request_break(void)
-{
- /* just set a flag - checked for and handled
- * in main thread (not signal handler).
- * see check_io()
- */
-#ifdef DEBUG
- fprintf(stderr,"break!\n");
-#endif
- erts_break_requested = 1;
- erts_check_io_async_sig_interrupt(1); /* Make sure we don't sleep in erts_poll_wait */
-}
-
-static void do_quit(void)
-{
- halt_0(0);
-}
-
-void erts_set_ignore_break(void) {
-}
-
-void init_break_handler(void)
-{
- sys_sigset(SIGINT, request_break);
- sys_sigset(SIGQUIT, do_quit);
-}
-
-void erts_replace_intr(void) {
-}
-
-int sys_max_files(void)
-{
- return(max_files);
-}
-
-/******************* Routines for time measurement *********************/
-
-int sys_init_time(void)
-{
- erts_clock_rate = sysClkRateGet();
- /*
- ** One could imagine that it would be better returning
- ** a resolution more near the clock rate, like in:
- ** return 1000 / erts_clock_rate;
- ** but tests show that such isn't the case (rounding errors?)
- ** Well, we go for the Unix variant of returning 1
- ** as a constant virtual clock rate.
- */
- return SYS_CLOCK_RESOLUTION;
-}
-
-int erts_clock_rate;
-static volatile int ticks_inuse;
-static volatile unsigned long ticks_collected; /* will wrap */
-static WDOG_ID watchdog_id;
-static ULONG user_time;
-static int this_task_id, sys_itime;
-static SysHrTime hrtime_wrap;
-static unsigned long last_tick_count;
-
-static void tolerant_time_clockint(int count)
-{
- if (watchdog_id != NULL) {
- if (taskIsReady(this_task_id))
- user_time += 1;
- ++count;
- if (!ticks_inuse) {
- ticks_collected += count;
- count = 0;
- }
- wdStart(watchdog_id, 1, (FUNCPTR)tolerant_time_clockint, count);
- }
-}
-
-int sys_init_hrtime(void)
-{
- this_task_id = taskIdSelf(); /* OK, this only works for one single task
- in the system... */
- user_time = 0;
-
- ticks_inuse = 0;
- ticks_collected = 0;
- hrtime_wrap = 0;
- last_tick_count = 0;
-
- sys_itime = 1000 / erts_clock_rate;
- watchdog_id = wdCreate();
- wdStart(watchdog_id, 1, (FUNCPTR) tolerant_time_clockint, 0);
- return 0;
-}
-
-SysHrTime sys_gethrtime(void)
-{
- SysHrTime ticks;
-
- ++ticks_inuse;
- ticks = (SysHrTime) (ticks_collected & 0x7FFFFFFF);
- ticks_inuse = 0;
- if (ticks < (SysHrTime) last_tick_count) {
- hrtime_wrap += 1UL << 31;
- }
- last_tick_count = ticks;
- return (ticks + hrtime_wrap) * ((SysHrTime) (1000000000UL /
- erts_clock_rate));
-}
-
-void sys_gettimeofday(SysTimeval *tvp)
-{
- struct timespec now;
-
- clock_gettime(CLOCK_REALTIME, &now);
- tvp->tv_sec = now.tv_sec;
- tvp->tv_usec = now.tv_nsec / 1000;
-}
-
-clock_t sys_times(SysTimes *t)
-{
- t->tms_stime = t->tms_cutime = t->tms_cstime = 0;
- ++ticks_inuse;
- t->tms_utime = user_time;
- ticks_inuse = 0;
- return tickGet(); /* The best we can do... */
-}
-
-/* This is called when *this task* is deleted */
-static void delete_hook(void)
-{
- if (watchdog_id != NULL) {
- wdDelete(watchdog_id);
- watchdog_id = NULL;
- }
- erlang_id = 0;
- this_task_id = 0;
-}
-
-/************************** OS info *******************************/
-
-/* Used by erlang:info/1. */
-/* (This code was formerly in drv.XXX/XXX_os_drv.c) */
-
-#define MAX_VER_STR 9 /* Number of characters to
- consider in version string */
-
-static FUNCTION(int, get_number, (char** str_ptr));
-
-char os_type[] = "vxworks";
-
-static int
-get_number(char **str_ptr)
-{
- char* s = *str_ptr; /* Pointer to beginning of string. */
- char* dot; /* Pointer to dot in string or NULL. */
-
- if (!isdigit(*s))
- return 0;
- if ((dot = strchr(s, '.')) == NULL) {
- *str_ptr = s+strlen(s);
- return atoi(s);
- } else {
- *dot = '\0';
- *str_ptr = dot+1;
- return atoi(s);
- }
-}
-
-/* namebuf; Where to return the name. */
-/* size; Size of name buffer. */
-void
-os_flavor(char *namebuf, unsigned size)
-{
- strcpy(namebuf, "-");
-}
-
-/* int* pMajor; Pointer to major version. */
-/* int* pMinor; Pointer to minor version. */
-/* int* pBuild; Pointer to build number. */
-void
-os_version(int *pMajor, int *pMinor, int *pBuild)
-{
- char os_ver[MAX_VER_STR+2];
- char* release; /* Pointer to the release string:
- * X.Y or X.Y.Z.
- */
- strncpy(os_ver, vxWorksVersion, MAX_VER_STR);
- release = os_ver;
- *pMajor = get_number(&release);
- *pMinor = get_number(&release);
- *pBuild = get_number(&release);
-}
-
-void init_getenv_state(GETENV_STATE *state)
-{
- *state = NULL;
-}
-
-char *getenv_string(GETENV_STATE *state0)
-{
- return NULL;
-}
-
-void fini_getenv_state(GETENV_STATE *state)
-{
- *state = NULL;
-}
-
-/************************** Port I/O *******************************/
-
-
-/* I. Common stuff */
-
-#define TMP_BUF_MAX (tmp_buf_size - 1024)
-static byte *tmp_buf;
-static Uint tmp_buf_size;
-
-/* II. The spawn/fd/vanilla drivers */
-
-/* This data is shared by these drivers - initialized by spawn_init() */
-static struct driver_data {
- int port_num, ofd, packet_bytes, report_exit;
- int exitcode, exit_reported; /* For returning of exit codes. */
-} *driver_data; /* indexed by fd */
-
-/*
- * Locking only for exitcodes and exit_reported, one global sem for all
- * spawn ports as this is rare.
- */
-static SEM_ID driver_data_sem = NULL;
-/*
- * Also locking when looking up entries in the load table
- */
-static SEM_ID entry_data_sem = NULL;
-
-/* We maintain a linked fifo queue of these structs in order */
-/* to manage unfinnished reads/and writes on differenet fd's */
-
-typedef struct pend {
- char *cpos;
- int fd;
- int remain;
- struct pend *next;
- char buf[1]; /* this is a trick to be able to malloc one chunk */
-} Pend;
-
-static struct fd_data {
- int inport, outport;
- char *buf, *cpos;
- int sz, remain; /* for input on fd */
- Pend* pending; /* pending outputs */
-
-} *fd_data; /* indexed by fd */
-
-
-/* Driver interfaces */
-static ErlDrvData spawn_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts);
-static ErlDrvData fd_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts);
-static ErlDrvData vanilla_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts);
-static int spawn_init(void);
-static void fd_stop(ErlDrvData);
-static void stop(ErlDrvData);
-static void ready_input(ErlDrvData fd, ErlDrvEvent ready_fd);
-static void ready_output(ErlDrvData fd, ErlDrvEvent ready_fd);
-static void output(ErlDrvData fd, char *buf, ErlDrvSizeT len);
-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, /* finish */
- NULL, /* handle */
- NULL, /* control */
- 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, /* handle2 */
- NULL, /* process_exit */
- stop_select
-
-};
-struct erl_drv_entry fd_driver_entry = {
- NULL,
- fd_start,
- fd_stop,
- output,
- ready_input,
- ready_output,
- "fd",
- NULL, /* finish */
- NULL, /* handle */
- NULL, /* control */
- 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, /* handle2 */
- NULL, /* process_exit */
- stop_select
-};
-struct erl_drv_entry vanilla_driver_entry = {
- NULL,
- vanilla_start,
- stop,
- output,
- ready_input,
- ready_output,
- "vanilla",
- NULL, /* finish */
- NULL, /* handle */
- NULL, /* control */
- 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, /* handle2 */
- NULL, /* process_exit */
- stop_select
-};
-
-/*
-** Set up enough of the driver_data structure to be able to report exit status.
-** Some things may be initiated again, but that is no real problem.
-*/
-static int pre_set_driver_data(int ifd, int ofd,
- int read_write, int report_exit) {
- if (read_write & DO_READ) {
- driver_data[ifd].report_exit = report_exit;
- driver_data[ifd].exitcode = 0;
- driver_data[ifd].exit_reported = 0;
- if (read_write & DO_WRITE) {
- driver_data[ifd].ofd = ofd;
- if (ifd != ofd) {
- driver_data[ofd] = driver_data[ifd];
- driver_data[ofd].report_exit = 0;
- }
- } else { /* DO_READ only */
- driver_data[ifd].ofd = -1;
- }
- return(ifd);
- } else { /* DO_WRITE only */
- driver_data[ofd].report_exit = 0;
- driver_data[ofd].exitcode = 0;
- driver_data[ofd].exit_reported = 0;
- driver_data[ofd].ofd = ofd;
- return(ofd);
- }
-}
-
-/*
-** Set up the driver_data structure, it may have been initiated
-** partly by the function above, but we dont care.
-*/
-static int set_driver_data(int port_num, int ifd, int ofd,
- int packet_bytes, int read_write,
- int report_exit)
-{
- 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;
- if (read_write & DO_WRITE) {
- driver_data[ifd].ofd = ofd;
- if (ifd != ofd) {
- driver_data[ofd] = driver_data[ifd];
- driver_data[ofd].report_exit = 0;
- }
- } 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 = 0;
- driver_data[ofd].ofd = ofd;
- return(ofd);
- }
-}
-
-static int need_new_sems = 1;
-
-static int spawn_init(void)
-{
- char *stackenv;
- int size;
- driver_data = (struct driver_data *)
- erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(struct driver_data));
- if (need_new_sems) {
- driver_data_sem = semMCreate
- (SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
- entry_data_sem = semMCreate
- (SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
- }
- if (driver_data_sem == NULL || entry_data_sem == NULL) {
- erl_exit(1,"Could not allocate driver locking semaphore.");
- }
- need_new_sems = 0;
-
- (void)uxPipeDrv(); /* Install pipe driver */
-
- if ((stackenv = getenv("ERLPORTSTACKSIZE")) != NULL &&
- (size = atoi(stackenv)) > 0)
- port_stack_size = size;
- else
- port_stack_size = DEFAULT_PORT_STACK_SIZE;
- return 0;
-}
-
-/* Argv has to be built vith the save_xxx routines, not with whathever
- sys_xxx2 has in mind... */
-#define argv_alloc save_malloc
-#define argv_realloc save_realloc
-#define argv_free save_free
-/* Build argv, return argc or -1 on failure */
-static int build_argv(char *name, char ***argvp)
-{
- int argvsize = 10, argc = 0;
- char *args, *arglast = NULL, *argp;
- char **argv;
-
-#ifdef DEBUG
- fdprintf(2, "Building argv, %s =>\n", name);
-#endif
- if ((argv = (char **)argv_alloc(argvsize * sizeof(char *))) == NULL)
- return(-1);
- if ((args = argv_alloc(strlen(name) + 1)) == NULL)
- return(-1);
- strcpy(args, name);
- argp = strtok_r(args, " \t", &arglast);
- while (argp != NULL) {
- if (argc + 1 >= argvsize) {
- argvsize += 10;
- argv = (char **)argv_realloc((char *)argv, argvsize*sizeof(char *));
- if (argv == NULL) {
- argv_free(args);
- return(-1);
- }
- }
-#ifdef DEBUG
- fdprintf(2, "%s\n", argp);
-#endif
- argv[argc++] = argp;
- argp = strtok_r((char *)NULL, " \t", &arglast);
- }
- argv[argc] = NULL;
- *argvp = argv;
- return(argc);
-}
-#undef argv_alloc
-#undef argv_realloc
-#undef argv_free
-
-
-/* Lookup and return global text symbol or NULL on failure
- Symbol name is null-terminated and without the leading '_' */
-static FUNCPTR
-lookup(char *sym)
-{
- char buf[256];
- char *symname = buf;
- int len;
- FUNCPTR entry;
- SYM_TYPE type;
-
- len = strlen(sym);
- if (len > 254 && (symname = malloc(len+2)) == NULL)
- return(NULL);
-#if defined _ARCH_PPC || defined SIMSPARCSOLARIS
- /* GCC for PPC and SIMSPARC doesn't add a leading _ to symbols */
- strcpy(symname, sym);
-#else
- sprintf(symname, "_%s", sym);
-#endif
- if (symFindByNameAndType(sysSymTbl, symname, (char **)&entry,
- &type, N_EXT | N_TEXT, N_EXT | N_TEXT) != OK)
- entry = NULL;
- if (symname != buf)
- free(symname);
- return(entry);
-}
-
-/* This function is spawned to build argc, argv, lookup the symbol to call,
- connect and set up file descriptors, and make the actual call.
- N.B. 'name' was allocated by the Erlang task (through plain_malloc) and
- is freed by this port program task.
- Note: 'name' may be a path containing '/'. */
-
-static void call_proc(char *name, int ifd, int ofd, int read_write,
- int redir_stderr, int driver_index,
- int p6, int p7, int p8, int p9)
-{
- int argc;
- char **argv, *bname;
- FUNCPTR entry;
- int ret = -1;
-
- /* Must consume 'name' */
- argc = build_argv(name, &argv);
- plain_free(name);
- /* Find basename of path */
- if ((bname = strrchr(argv[0], '/')) != NULL) {
- bname++;
- } else {
- bname = argv[0];
- }
-#ifdef DEBUG
- fdprintf(2, "Port program name: %s\n", bname);
-#endif
- semTake(entry_data_sem, WAIT_FOREVER);
-
- if (argc > 0) {
- if ((entry = lookup(bname)) == NULL) {
- int fd;
- char *fn;
- /* NOTE: We don't check the return value of loadModule,
- since that was incompatibly changed from 5.0.2b to 5.1,
- but rather do a repeated lookup(). */
- if ((fd = open(argv[0], O_RDONLY)) > 0) {
- (void) loadModule(fd, GLOBAL_SYMBOLS);
- close(fd);
- entry = lookup(bname);
- }
- if (entry == NULL) {
- /* filename == func failed, try func.o */
- if ((fn = malloc(strlen(argv[0]) + 3)) != NULL) { /* ".o\0" */
- strcpy(fn, argv[0]);
- strcat(fn, ".o");
- if ((fd = open(fn, O_RDONLY)) > 0) {
- (void) loadModule(fd, GLOBAL_SYMBOLS);
- close(fd);
- entry = lookup(bname);
- }
- free(fn);
- }
- }
- }
- } else {
- entry = NULL;
- }
- semGive(entry_data_sem);
-
- if (read_write & DO_READ) { /* emulator read */
- save_fd(ofd);
- ioTaskStdSet(0, 1, ofd); /* stdout for process */
- if(redir_stderr)
- ioTaskStdSet(0, 2, ofd);/* stderr for process */
- }
- if (read_write & DO_WRITE) { /* emulator write */
- save_fd(ifd);
- ioTaskStdSet(0, 0, ifd); /* stdin for process */
- }
- if (entry != NULL) {
- ret = (*entry)(argc, argv, (char **)NULL); /* NULL for envp */
- } else {
- fdprintf(2, "Could not exec \"%s\"\n", argv[0]);
- ret = -1;
- }
- if (driver_data[driver_index].report_exit) {
- semTake(driver_data_sem, WAIT_FOREVER);
- driver_data[driver_index].exitcode = ret;
- driver_data[driver_index].exit_reported = 1;
- semGive(driver_data_sem);
- }
- /* We *don't* want to close the pipes here, but let the delete
- hook take care of it - it might want to flush stdout and there'd
- better be an open descriptor to flush to... */
- exit(ret);
-}
-
-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, int port_unused_argument)
-{
- SET_NONBLOCKING(fd);
- fd_data[fd].pending = NULL;
- fd_data[fd].buf = fd_data[fd].cpos = NULL;
- fd_data[fd].remain = fd_data[fd].sz = 0;
-}
-
-static ErlDrvData spawn_start(ErlDrvPort port_num, char *name,SysDriverOpts* opts)
-{
- int ifd[2], ofd[2], len, nl, id;
- char taskname[11], *progname, *bname;
- char *space_in_command;
- int packet_bytes = opts->packet_bytes;
- int read_write = opts->read_write;
- int use_stdio = opts->use_stdio;
- int redir_stderr = opts->redir_stderr;
- int driver_index;
-
- if (!use_stdio){
- return (ErlDrvData) -3;
- }
-
- /* Create pipes and set the Erlang task as owner of its
- * read and write ends (through save_fd()).
- */
- switch (read_write) {
- case DO_READ:
- if (pipe(ifd) < 0){
- return (ErlDrvData) -2;
- }
- if (ifd[0] >= max_files) {
- close_pipes(ifd, ofd, read_write);
- errno = ENFILE;
- return (ErlDrvData) -2;
- }
- save_fd(ifd[0]);
- break;
- case DO_WRITE:
- if (pipe(ofd) < 0) {
- return (ErlDrvData) -2;
- }
- if (ofd[1] >= max_files) {
- close_pipes(ifd, ofd, read_write);
- errno = ENFILE;
- return (ErlDrvData) -2;
- }
- save_fd(ofd[1]);
- break;
- case DO_READ|DO_WRITE:
- if (pipe(ifd) < 0){
- return (ErlDrvData) -2;
- }
- if (ifd[0] >= max_files || pipe(ofd) < 0) {
- close_pipes(ifd, ofd, DO_READ);
- errno = ENFILE;
- return (ErlDrvData) -2;
- }
- if (ofd[1] >= max_files) {
- close_pipes(ifd, ofd, read_write);
- errno = ENFILE;
- return (ErlDrvData) -2;
- }
- save_fd(ifd[0]);
- save_fd(ofd[1]);
- break;
- default:
- return (ErlDrvData) -1;
- }
-
- /* Allocate space for program name to be freed by the
- * spawned task. We use plain_malloc so that the allocated
- * space is not owned by the Erlang task.
- */
-
- if ((progname = plain_malloc(strlen(name) + 1)) == NULL) {
- close_pipes(ifd, ofd, read_write);
- errno = ENOMEM;
- return (ErlDrvData) -2;
- }
- strcpy(progname, name);
-
- /* Check if name contains a space
- * (e.g "port_test -o/home/gandalf/tornado/wind/target/erlang")
- */
- if ((space_in_command = strrchr(progname, ' ')) != NULL) {
- *space_in_command = '\0';
- }
-
- /* resulting in "port_test" */
- if ((bname = strrchr(progname, '/')) != NULL)
- bname++;
- else
- bname = progname;
-
- /* resulting in "port_test" */
- len = strlen(bname);
- nl = len > 10 ? 10 : len;
- strncpy(taskname, bname, nl);
- taskname[nl] = '\0';
- if (space_in_command != NULL)
- *space_in_command = ' ';
- driver_index = pre_set_driver_data(ifd[0], ofd[1],
- read_write, opts->exit_status);
-
- /* resetting to "port_test -o/home/gandalf/tornado/wind/target/erlang" */
- if ((id = taskSpawn(taskname, spTaskPriority, spTaskOptions,
- port_stack_size, (FUNCPTR)call_proc, (int)progname,
- ofd[0], ifd[1], read_write, redir_stderr, driver_index,
- 0,0,0,0))
- == ERROR) {
- close_pipes(ifd, ofd, read_write);
- plain_free(progname); /* only when spawn fails */
- errno = ENOMEM;
- return (ErlDrvData) -2;
- }
-#ifdef DEBUG
- fdprintf(2, "Spawned %s as %s[0x%x]\n", name, taskname, id);
-#endif
- if (read_write & DO_READ)
- init_fd_data(ifd[0], port_num);
- if (read_write & DO_WRITE)
- init_fd_data(ofd[1], port_num);
- return (ErlDrvData) (set_driver_data(port_num, ifd[0], ofd[1],
- packet_bytes,read_write,
- opts->exit_status));
-}
-
-static ErlDrvData fd_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts)
-{
- if (((opts->read_write & DO_READ) && opts->ifd >= max_files) ||
- ((opts->read_write & DO_WRITE) && opts->ofd >= max_files)) {
- return (ErlDrvData) -1;
- }
-
- 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);
- return (ErlDrvData) (set_driver_data(port_num, opts->ifd, opts->ofd,
- opts->packet_bytes, opts->read_write, 0));
-}
-
-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);
- fd_data[fd].buf = NULL;
- fd_data[fd].sz = 0;
- fd_data[fd].remain = 0;
- fd_data[fd].cpos = NULL;
-}
-
-static void nbio_stop_fd(int port_num, int fd)
-{
- Pend *p, *p1;
-
- driver_select(port_num, fd, ERL_DRV_READ|ERL_DRV_WRITE, 0);
- clear_fd_data(fd);
- p = fd_data[fd].pending;
- SET_BLOCKING(fd);
- while (p) {
- p1 = p->next;
- free(p);
- p = p1;
- }
- fd_data[fd].pending = NULL;
-}
-
-static void fd_stop(ErlDrvData drv_data)
-{
- int ofd;
- int fd = (int) drv_data;
-
- nbio_stop_fd(driver_data[fd].port_num, (int)fd);
- ofd = driver_data[fd].ofd;
- if (ofd != fd && ofd != -1)
- nbio_stop_fd(driver_data[fd].port_num, (int)ofd); /* XXX fd = ofd? */
-}
-
-static ErlDrvData
-vanilla_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts)
-{
- int flags, fd;
- struct stat statbuf;
-
- DEBUGF(("vanilla_start, name: %s [r=%1i w=%1i]\n", name,
- opts->read_write & DO_READ,
- opts->read_write & DO_WRITE));
-
- 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){
- errno = ENFILE;
- return (ErlDrvData) -2;
- }
- if (fd >= max_files) {
- close(fd);
- errno = ENFILE;
- return (ErlDrvData) -2;
- }
- if (fstat(fd, &statbuf) < 0) {
- close(fd);
- errno = ENFILE;
- return (ErlDrvData) -2;
- }
-
- /* Return error for reading regular files (doesn't work) */
- if (ISREG(statbuf) && ((opts->read_write) & DO_READ)) {
- close(fd);
- return (ErlDrvData) -3;
- }
- init_fd_data(fd, port_num);
- return (ErlDrvData) (set_driver_data(port_num, fd, fd,
- opts->packet_bytes, opts->read_write, 0));
-}
-
-/* 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 drv_data)
-{
- int port_num, ofd;
- int fd = (int) drv_data;
-
- port_num = driver_data[fd].port_num;
- nbio_stop_fd(port_num, fd);
- driver_select(port_num, fd, ERL_DRV_USE, 0); /* close(fd) */
-
- ofd = driver_data[fd].ofd;
- if (ofd != fd && ofd != -1) {
- nbio_stop_fd(port_num, ofd);
- driver_select(port_num, ofd, ERL_DRV_USE, 0); /* close(fd) */
- }
-}
-
-static int sched_write(int port_num,int fd, char *buf, int len, int pb)
-{
- Pend *p, *p2, *p3;
- int p_bytes = len;
-
- p = (Pend*) erts_alloc_fnf(ERTS_ALC_T_PEND_DATA, pb + len + sizeof(Pend));
- if (!p) {
- driver_failure(port_num, -1);
- return(-1);
- }
-
- switch(pb) {
- case 4: put_int32(len, p->buf); break;
- case 2: put_int16(len, p->buf); break;
- case 1: put_int8(len, p->buf); break;
- case 0: break; /* Handles this case too */
- }
- sys_memcpy(p->buf + pb, buf, len);
- driver_select(port_num, fd, ERL_DRV_WRITE|ERL_DRV_USE, 1);
- p->cpos = p->buf;
- p->fd = fd;
- p->next = NULL;
- p->remain = len + pb;
- p2 = fd_data[fd].pending;
- if (p2 == NULL)
- fd_data[fd].pending = p;
- else {
- p3 = p2->next;
- while(p3) {
- p_bytes += p2->remain;
- p2 = p2->next;
- p3 = p3->next;
- }
- p2->next = p;
- }
- if (p_bytes > (1 << 13)) /* More than 8 k pending */
- set_busy_port(port_num, 1);
- return(0);
-}
-
-/* Fd is the value returned as drv_data by the start func */
-static void output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)
-{
- int buf_done, port_num, wval, pb, ofd;
- byte lb[4];
- struct iovec iv[2];
- int fd = (int) drv_data;
-
- pb = driver_data[fd].packet_bytes;
- port_num = driver_data[fd].port_num;
-
- if ((ofd = driver_data[fd].ofd) == -1) {
- return;
- }
-
- if (fd_data[ofd].pending) {
- sched_write(port_num, ofd, buf, len, pb);
- return;
- }
-
- if ((pb == 2 && len > 65535) || (pb == 1 && len > 255)) {
- driver_failure_posix(port_num, EINVAL);
- return;
- }
- if (pb == 0) {
- wval = write(ofd, buf, len);
- } else {
- lb[0] = (len >> 24) & 255; /* MSB */
- lb[1] = (len >> 16) & 255;
- lb[2] = (len >> 8) & 255;
- lb[3] = len & 255; /* LSB */
- iv[0].iov_base = (char*) lb + (4 - pb);
- iv[0].iov_len = pb;
- iv[1].iov_base = buf;
- iv[1].iov_len = len;
- wval = writev(ofd, iv, 2);
- }
- if (wval == pb + len ) {
- return;
- }
- if (wval < 0) {
- if ((errno == EINTR) || (errno == ERRNO_BLOCK)) {
- if (pb) {
- sched_write(port_num, ofd, buf ,len, pb);
- } else if (pb == 0) {
- sched_write(port_num, ofd, buf ,len, 0);
- }
- return;
- }
- driver_failure_posix(driver_data[fd].port_num, EINVAL);
- return;
- }
- if (wval < pb) {
- sched_write(port_num, ofd, (lb +4 -pb) + wval, pb-wval, 0);
- sched_write(port_num, ofd, buf ,len, 0);
- return;
- }
-
- /* we now know that wval < (pb + len) */
- buf_done = wval - pb;
- sched_write(port_num, ofd, buf + buf_done, len - buf_done,0);
-}
-
-static void stop_select(ErlDrvEvent fd, void* _)
-{
- close((int)fd);
-}
-
-static int ensure_header(int fd,char *buf,int packet_size, int sofar)
-{
- int res = 0;
- int remaining = packet_size - sofar;
-
- SET_BLOCKING(fd);
- if (read_fill(fd, buf+sofar, remaining) != remaining)
- return -1;
- switch (packet_size) {
- case 1: res = get_int8(buf); break;
- case 2: res = get_int16(buf); break;
- case 4: res = get_int32(buf); break;
- }
- SET_NONBLOCKING(fd);
- return(res);
-}
-
-static int port_inp_failure(int port_num, int ready_fd, int res)
-{
- (void) driver_select(port_num, ready_fd, ERL_DRV_READ|ERL_DRV_WRITE, 0);
- clear_fd_data(ready_fd);
- if (res == 0) {
- if (driver_data[ready_fd].report_exit) {
- int tmpexit = 0;
- int reported;
- /* Lock the driver_data structure */
- semTake(driver_data_sem, WAIT_FOREVER);
- if ((reported = driver_data[ready_fd].exit_reported))
- tmpexit = driver_data[ready_fd].exitcode;
- semGive(driver_data_sem);
- if (reported) {
- erts_fprintf(stderr,"Exitcode %d reported\r\n", tmpexit);
- driver_report_exit(port_num, tmpexit);
- }
- }
- driver_failure_eof(port_num);
- } else {
- driver_failure(port_num, res);
- }
- 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 drv_data, ErlDrvEvent drv_event)
-{
- int port_num, packet_bytes, res;
- Uint h = 0;
- char *buf;
- int fd = (int) drv_data;
- int ready_fd = (int) drv_event;
-
- port_num = driver_data[fd].port_num;
- packet_bytes = driver_data[fd].packet_bytes;
-
- if (packet_bytes == 0) {
- if ((res = read(ready_fd, tmp_buf, tmp_buf_size)) > 0) {
- driver_output(port_num, (char*)tmp_buf, res);
- return;
- }
- port_inp_failure(port_num, ready_fd, res);
- return;
- }
-
- 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)) {
- ;
- } else {
- 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;
- }
- return;
- }
-
-
- if (fd_data[ready_fd].remain == 0) { /* clean fd */
- /* We make one read attempt and see what happens */
- res = read(ready_fd, tmp_buf, tmp_buf_size);
- if (res < 0) {
- if ((errno == EINTR) || (errno == ERRNO_BLOCK))
- return;
- port_inp_failure(port_num, ready_fd, res);
- return;
- }
- else if (res == 0) { /* eof */
- port_inp_failure(port_num, ready_fd, res);
- return;
- }
- else if (res < packet_bytes) { /* Ugly case... get at least */
- if ((h = ensure_header(ready_fd, tmp_buf, packet_bytes, res))==-1) {
- port_inp_failure(port_num, ready_fd, -1);
- return;
- }
- buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h);
- if (!buf) {
- port_inp_failure(port_num, ready_fd, -1);
- return;
- }
- fd_data[ready_fd].buf = buf;
- fd_data[ready_fd].sz = h;
- fd_data[ready_fd].remain = h;
- fd_data[ready_fd].cpos = buf;
- return;
- }
- else { /* if (res >= packet_bytes) */
- unsigned char* cpos = tmp_buf;
- int bytes_left = res;
- while (1) { /* driver_output as many as possible */
- if (bytes_left == 0) {
- clear_fd_data(ready_fd);
- return;
- }
- if (bytes_left < packet_bytes) { /* Yet an ugly case */
- if((h=ensure_header(ready_fd, cpos,
- packet_bytes, bytes_left))==-1) {
- port_inp_failure(port_num, ready_fd, -1);
- return;
- }
- buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h);
- if (!buf)
- port_inp_failure(port_num, ready_fd, -1);
- fd_data[ready_fd].buf = buf;
- fd_data[ready_fd].sz = h;
- fd_data[ready_fd].remain = h;
- fd_data[ready_fd].cpos = buf;
- return;
- }
- switch (packet_bytes) {
- case 1: h = get_int8(cpos); cpos += 1; break;
- case 2: h = get_int16(cpos); cpos += 2; break;
- case 4: h = get_int32(cpos); cpos += 4; break;
- }
- bytes_left -= packet_bytes;
- /* we've got the header, now check if we've got the data */
- 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 */
- buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h);
- if (!buf) {
- port_inp_failure(port_num, ready_fd, -1);
- }
- 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;
- return;
- }
- }
- return;
- }
- }
- fprintf(stderr, "remain %d \n", fd_data[ready_fd].remain);
- port_inp_failure(port_num, ready_fd, -1);
-}
-
-
-/* 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 drv_data, ErlDrvEvent drv_event)
-{
- Pend *p;
- int wval;
-
- int fd = (int) drv_data;
- int ready_fd = (int) drv_event;
-
- while(1) {
- if ((p = fd_data[ready_fd].pending) == NULL) {
- driver_select(driver_data[fd].port_num, ready_fd,
- ERL_DRV_WRITE, 0);
- return;
- }
- wval = write(p->fd, p->cpos, p->remain);
- if (wval == p->remain) {
- fd_data[ready_fd].pending = p->next;
- erts_free(ERTS_ALC_T_PEND_DATA, p);
- if (fd_data[ready_fd].pending == NULL) {
- driver_select(driver_data[fd].port_num, ready_fd,
- ERL_DRV_WRITE, 0);
- set_busy_port(driver_data[fd].port_num, 0);
- return;
- }
- else
- continue;
- }
- else if (wval < 0) {
- if (errno == ERRNO_BLOCK || errno == EINTR)
- return;
- else {
- driver_select(driver_data[fd].port_num, ready_fd,
- ERL_DRV_WRITE, 0);
- driver_failure(driver_data[fd].port_num, -1);
- return;
- }
- }
- else if (wval < p->remain) {
- p->cpos += wval;
- p->remain -= wval;
- return;
- }
- }
-}
-
-/* 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){
- int p = taskIdSelf(); /* Hmm, may be negative??? requires some GB of
- memory to make the TCB address convert to a
- negative value. */
- sprintf(buffer,"%d", p);
-}
-
-int
-erts_sys_putenv(char *buffer, int sep_ix)
-{
- return putenv(buffer);
-}
-
-int
-erts_sys_getenv(char *key, char *value, size_t *size)
-{
- char *orig_value;
- int res;
- orig_value = getenv(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;
- }
- }
- return res;
-}
-
-int
-erts_sys_getenv__(char *key, char *value, size_t *size)
-{
- return erts_sys_getenv(key, value, size);
-}
-
-void
-sys_init_io(void)
-{
- tmp_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_TMP_BUF, SYS_TMP_BUF_SIZE);
- tmp_buf_size = SYS_TMP_BUF_SIZE;
- fd_data = (struct fd_data *)
- erts_alloc(ERTS_ALC_T_FD_TAB, max_files * sizeof(struct fd_data));
-}
-
-
-/* Fill buffer, return buffer length, 0 for EOF, < 0 for error. */
-
-static int read_fill(int fd, char *buf, int len)
-{
- int i, got = 0;
- do {
- if ((i = read(fd, buf+got, len-got)) <= 0) {
- return i;
- }
- got += i;
- } while (got < len);
- return (len);
-}
-
-
-/************************** Misc... *******************************/
-
-extern const char pre_loaded_code[];
-extern char* const pre_loaded[];
-
-
-/* Float conversion */
-
-int sys_chars_to_double(char *buf, double *fp)
-{
- char *s = buf;
-
- /* The following check is incorporated from the Vee machine */
-
-#define ISDIGIT(d) ((d) >= '0' && (d) <= '9')
-
- /* Robert says that something like this is what he really wanted:
- *
- * 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++ != '.') /* Decimal part. */
- return -1;
- 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;
-
- if (sscanf(buf, "%lf", fp) != 1)
- return -1;
- return 0;
-}
-
-/*
- ** Convert a double to ascii format 0.dddde[+|-]ddd
- ** return number of characters converted
- */
-
-int sys_double_to_chars(double fp, char *buf)
-{
- (void) sprintf(buf, "%.20e", fp);
- return strlen(buf);
-}
-
-
-/* Floating point exceptions */
-
-#if (CPU == SPARC)
-jmp_buf fpe_jmp;
-
-RETSIGTYPE fpe_sig_handler(int sig)
-{
- longjmp(fpe_jmp, 1);
-}
-
-#elif (CPU == PPC603)
-static void fix_registers(void){
- FP_CONTEXT fpcontext;
- fppSave(&fpcontext);
- fpcontext.fpcsr &= ~(_PPC_FPSCR_INIT);
- fppRestore(&fpcontext);
-}
-#endif
-
-
-/* Return a pointer to a vector of names of preloaded modules */
-
-Preload* sys_preloaded(void)
-{
- return (Preload *) pre_loaded;
-}
-
-/* Return a pointer to preloaded code for module "module" */
-unsigned char* sys_preload_begin(Preload *pp)
-{
- return pp->code;
-}
-
-/* Clean up if allocated */
-void sys_preload_end(Preload *pp)
-{
- /* Nothing */
-}
-
-/* Read a key from console (?) */
-
-int sys_get_key(int fd)
-{
- int c;
- unsigned char rbuf[64];
-
- fflush(stdout); /* Flush query ??? */
-
- if ((c = read(fd,rbuf,64)) <= 0)
- return c;
- return rbuf[0];
-}
-
-
-/* A real printf that does the equivalent of fprintf(stdout, ...) */
-
-/* ARGSUSED */
-static STATUS
-stdio_write(char *buf, int nchars, int fp)
-{
- if (fwrite(buf, sizeof(char), nchars, (FILE *)fp) == 0)
- return(ERROR);
- return(OK);
-}
-
-int real_printf(const char *fmt, ...)
-{
- va_list ap;
- int err;
-
- va_start(ap, fmt);
- err = fioFormatV(fmt, ap, stdio_write, (int)stdout);
- va_end(ap);
- return(err);
-}
-
-
-/*
- * Little function to do argc, argv calls from (e.g.) VxWorks shell
- * The arguments should be in the form of a single ""-enclosed string
- * NOTE: This isn't really part of the emulator, just included here
- * so we can use the handy functions and memory reclamation.
- */
-void argcall(char *args)
-{
- int argc;
- char **argv;
- FUNCPTR entry;
-
- if (args != NULL) {
- if ((argc = build_argv(args, &argv)) > 0) {
- if ((entry = lookup(argv[0])) != NULL)
- (*entry)(argc, argv, (char **)NULL); /* NULL for envp */
- else
- fprintf(stderr, "Couldn't find %s\n", argv[0]);
- } else
- fprintf(stderr, "Failed to build argv!\n");
- } else
- fprintf(stderr, "No argument list!\n");
-}
-
-
-/* That concludes the Erlang stuff - now we just need to implement an OS...
- - Just kidding, but resource reclamation isn't the strength of VxWorks */
-#undef calloc
-#undef free
-#undef cfree
-#undef malloc
-#undef realloc
-#undef open
-#undef creat
-#undef socket
-#undef accept
-#undef close
-#undef fopen
-#undef fdopen
-#undef freopen
-#undef fclose
-
-/********************* Using elib_malloc ****************************/
-/* This gives us yet another level of malloc wrappers. The purpouse */
-/* is to be able to select between different varieties of memory */
-/* allocation without recompiling. */
-/* Maybe the performance is somewhat degraded by this, but */
-/* on the other hand, performance may be much better if the most */
-/* suiting malloc is used (not to mention the much lower */
-/* fragmentation). */
-/* /Patrik N */
-/********************************************************************/
-
-/*
- * I don't want to include the whole elib header, especially
- * as it uses char * for generic pointers. Let's fool ANSI C instead.
- */
-extern void *elib_malloc(size_t);
-extern void *elib_realloc(void *, size_t);
-extern void elib_free(void *);
-extern void elib_init(void *, int);
-extern void elib_force_init(void *, int);
-extern size_t elib_sizeof(void *);
-
-/* Flags */
-#define USING_ELIB_MALLOC 1 /* We are using the elib_malloc */
-#define WARN_MALLOC_MIX 2 /* Warn if plain malloc or save_malloc
- is mixed with sys_free2 or
- sys_realloc2 */
-#define REALLOC_MOVES 4 /* Always move on realloc
- (less fragmentation) */
-#define USER_POOL 8 /* The user supplied the memory
- pool, it was not save_alloced. */
-#define RECLAIM_USER_POOL 16 /* Use the reclaim mechanism in the
- user pool. */
-#define NEW_USER_POOL 32 /* The user pool is newly suppllied,
- any old pool should be discarded */
-
-
-#define ELIB_LOCK \
-if(alloc_flags & USING_ELIB_MALLOC) \
- semTake(elib_malloc_sem, WAIT_FOREVER)
-
-#define ELIB_UNLOCK \
-if(alloc_flags & USING_ELIB_MALLOC) \
- semGive(elib_malloc_sem)
-
-#define USER_RECLAIM() ((alloc_flags & USING_ELIB_MALLOC) && \
- (alloc_flags & USER_POOL) && \
- (alloc_flags & RECLAIM_USER_POOL))
-
-/*
- * Global state
- * The use of function pointers for the malloc/realloc/free functions
- * is actually only useful in the malloc case, we must know what kind of
- * realloc/free we are going to use, so we could call elib_xxx directly.
- * However, as the overhead is small and this construction makes it
- * fairly easy to add another malloc algorithm, the function pointers
- * are used in realloc/free to.
- */
-static MallocFunction actual_alloc = &save_malloc;
-static ReallocFunction actual_realloc = &save_realloc;
-static FreeFunction actual_free = &save_free;
-static int alloc_flags = 0;
-static int alloc_pool_size = 0;
-static void *alloc_pool_ptr = NULL;
-static SEM_ID elib_malloc_sem = NULL;
-
-/*
- * Descide if we should use the save_free instead of elib_free or,
- * in the case of the free used in a delete hook, if we should
- * use plain free instead of elib_free.
- */
-static int use_save_free(void *ptr){
- register int diff = ((char *) ptr) - ((char *) alloc_pool_ptr);
- /*
- * Hmmm... should it be save_free even if diff is exactly 0?
- * The answer is Yes if the whole area is save_alloced and No if not,
- * so reclaim_free_hook is NOT run in the case of one save_alloced area.
- */
- return (!(alloc_flags & USING_ELIB_MALLOC) ||
- (diff < 0 || diff >= alloc_pool_size));
-}
-
-/*
- * A free function used by the task deletion hook for the save_xxx functions.
- * Set with the set_reclaim_free_function function.
- */
-static void reclaim_free_hook(void *ptr){
- if(use_save_free(ptr)){
- free(ptr);
- } else {
- ELIB_LOCK;
- (*actual_free)(ptr);
- ELIB_UNLOCK;
- }
-}
-
-
-/*
- * Initialize, sets the values of pointers based on
- * either nothing (the default) or what's set previously by the
- * erl_set_memory_block function.
- */
-static void initialize_allocation(void){
- set_reclaim_free_function(NULL);
- if(alloc_pool_size == 0){
- actual_alloc = (void *(*)(size_t))&save_malloc;
- actual_realloc = (void *(*)(void *, size_t))&save_realloc;
- actual_free = &save_free;
- alloc_flags &= ~(USING_ELIB_MALLOC | USER_POOL | RECLAIM_USER_POOL);
- } else {
- if(elib_malloc_sem == NULL)
- elib_malloc_sem = semMCreate
- (SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
- if(elib_malloc_sem == NULL)
- erl_exit(1,"Could not create mutex semaphore for elib_malloc");
- if(!(alloc_flags & USER_POOL)){
- if((alloc_pool_ptr = save_malloc(alloc_pool_size)) == NULL)
- erl_exit(1,"Erlang set to allocate a %d byte block initially;"
- " not enough memory available.", alloc_pool_size);
- elib_force_init(alloc_pool_ptr, alloc_pool_size);
- } else if(alloc_flags & NEW_USER_POOL){
- elib_force_init(alloc_pool_ptr, alloc_pool_size);
- }
- actual_alloc=&elib_malloc;
- actual_realloc=&elib_realloc;
- actual_free=&elib_free;
- alloc_flags |= USING_ELIB_MALLOC;
- /* We MUST see to that the right free function is used
- otherwise we'll get a very nasty crash! */
- if(USER_RECLAIM())
- set_reclaim_free_function(&reclaim_free_hook);
- }
- alloc_flags &= ~(NEW_USER_POOL); /* It's never new after initialization*/
-}
-
-/* This does not exist on other platforms, we just use it in sys.c
- and the BSD resolver */
-void *sys_calloc2(Uint nelem, Uint elsize){
- void *ptr = erts_alloc_fnf(ERTS_ALC_T_UNDEF, nelem*elsize);
- if(ptr != NULL)
- memset(ptr,0,nelem*elsize);
- return ptr;
-}
-
-/*
- * The malloc wrapper
- */
-void *
-erts_sys_alloc(ErtsAlcType_t type, void *extra, Uint size)
-{
- register void *ret;
- ELIB_LOCK;
- if(USER_RECLAIM())
- ret = save_malloc2((size_t)size,actual_alloc);
- else
- ret = (*actual_alloc)((size_t)size);
- ELIB_UNLOCK;
- return ret;
-}
-
-/*
- * The realloc wrapper, may respond to the "realloc-always-moves" flag
- * if the area is initially allocated with elib_malloc.
- */
-void *
-erts_sys_realloc(ErtsAlcType_t type, void *extra, void *ptr, Uint size)
-{
- register void *ret;
- if(use_save_free(ptr)){
- if((alloc_flags & WARN_MALLOC_MIX) &&
- (alloc_flags & USING_ELIB_MALLOC))
- erts_fprintf(stderr,"Warning, save_malloced data realloced "
- "by sys_realloc2\n");
- return save_realloc(ptr, (size_t) size);
- } else {
- ELIB_LOCK;
- if((alloc_flags & REALLOC_MOVES) &&
- (alloc_flags & USING_ELIB_MALLOC)){
- size_t osz = elib_sizeof(ptr);
- if(USER_RECLAIM())
- ret = save_malloc2((size_t) size, actual_alloc);
- else
- ret = (*actual_alloc)((size_t) size);
- if(ret != NULL){
- memcpy(ret,ptr,(((size_t)size) < osz) ? ((size_t)size) : osz);
- if(USER_RECLAIM())
- save_free2(ptr,actual_free);
- else
- (*actual_free)(ptr);
- }
- } else {
- if(USER_RECLAIM())
- ret = save_realloc2(ptr,(size_t)size,actual_realloc);
- else
- ret = (*actual_realloc)(ptr,(size_t)size);
- }
- ELIB_UNLOCK;
- return ret;
- }
-}
-
-/*
- * Wrapped free().
- */
-void
-erts_sys_free(ErtsAlcType_t type, void *extra, void *ptr)
-{
- if(use_save_free(ptr)){
- /*
- * This might happen when linked in drivers use save_malloc etc
- * directly.
- */
- if((alloc_flags & WARN_MALLOC_MIX) &&
- (alloc_flags & USING_ELIB_MALLOC))
- erts_fprintf(stderr,"Warning, save_malloced data freed by "
- "sys_free2\n");
- save_free(ptr);
- } else {
- ELIB_LOCK;
- if(USER_RECLAIM())
- save_free2(ptr,actual_free);
- else
- (*actual_free)(ptr);
- ELIB_UNLOCK;
- }
-}
-
-/*
- * External interface to be called before erlang is started
- * Parameters:
- * isize: The size of the memory block where erlang should malloc().
- * iptr: (optional) A pointer to a user supplied memory block of
- * size isize.
- * warn_save: Instructs sys_free2 and sys_realloc2 to warn if
- * memory allocation/reallocation/freeing is mixed between
- * pure malloc/save_malloc/sys_alloc2 routines (only
- * warns if elib is actually used in the sys_alloc2 routines).
- * realloc_moves: Always allocate a fresh memory block on reallocation
- * (less fragmentation).
- * reclaim_in_supplied: Use memory reclaim mechanisms inside the user
- * supplied area, this makes one area reusable between
- * starts of erlang and might be nice for drivers etc.
- */
-
-int erl_set_memory_block(int isize, int iptr, int warn_save,
- int realloc_moves, int reclaim_in_supplied, int p5,
- int p6, int p7, int p8, int p9){
- if(erlang_id != 0){
- erts_fprintf(stderr,"Error, cannot set erlang memory block while an "
- "erlang task is running!\n");
- return 1;
- }
- if(isize < 8 * 1024 *1024)
- erts_fprintf(stderr,
- "Warning, the memory pool of %dMb may be to small to "
- "run erlang in!\n", isize / (1024 * 1024));
- alloc_pool_size = (size_t) isize;
- alloc_pool_ptr = (void *) iptr;
- alloc_flags = 0;
- /* USING_ELIB_MALLOC gets set by the initialization routine */
- if((void *)iptr != NULL)
- alloc_flags |= (USER_POOL | NEW_USER_POOL);
- if(realloc_moves)
- alloc_flags |= REALLOC_MOVES;
- if(warn_save)
- alloc_flags |= WARN_MALLOC_MIX;
- if((void *)iptr != NULL && reclaim_in_supplied)
- alloc_flags |= RECLAIM_USER_POOL;
- return 0;
-}
-
-/* External statistics interface */
-int erl_memory_show(int p0, int p1, int p2, int p3, int p4, int p5,
- int p6, int p7, int p8, int p9){
- struct elib_stat statistics;
- if(!(alloc_flags & USING_ELIB_MALLOC) && erlang_id != 0){
- erts_printf("Using plain save_alloc, use memShow instead.\n");
- return 1;
- }
- if(erlang_id == 0 && !((alloc_flags & USER_POOL) &&
- !(alloc_flags & NEW_USER_POOL))){
- erts_printf("Sorry, no allocation statistics until erlang "
- "is started.\n");
- return 1;
- }
- erts_printf("Allocation settings:\n");
- erts_printf("Using elib_malloc with memory pool size of %lu bytes.\n",
- (unsigned long) alloc_pool_size);
- erts_printf("Realloc-always-moves is %s\n",
- (alloc_flags & REALLOC_MOVES) ? "on" : "off");
- erts_printf("Warnings about mixed malloc/free's are %s\n",
- (alloc_flags & WARN_MALLOC_MIX) ? "on" : "off");
- if(alloc_flags & USER_POOL){
- erts_printf("The memory block used by elib is user supplied "
- "at 0x%08x.\n", (unsigned int) alloc_pool_ptr);
- if(alloc_flags & RECLAIM_USER_POOL)
- erts_printf("Allocated memory within the user supplied pool\n"
- " will be automatically reclaimed at task exit.\n");
- } else {
- erts_printf("The memory block used by elib is save_malloc'ed "
- "at 0x%08x.\n", (unsigned int) alloc_pool_ptr);
- }
- erts_printf("Statistics from elib_malloc:\n");
- ELIB_LOCK;
-
- elib_stat(&statistics);
- ELIB_UNLOCK;
- erts_printf("Type Size (bytes) Number of blocks\n");
- erts_printf("============= ============ ================\n");
- erts_printf("Total: %12lu %16lu\n",
- (unsigned long) statistics.mem_total*4,
- (unsigned long) statistics.mem_blocks);
- erts_printf("Allocated: %12lu %16lu\n",
- (unsigned long) statistics.mem_alloc*4,
- (unsigned long) statistics.mem_blocks-statistics.free_blocks);
- erts_printf("Free: %12lu %16lu\n",
- (unsigned long) statistics.mem_free*4,
- (unsigned long) statistics.free_blocks);
- erts_printf("Largest free: %12lu -\n\n",
- (unsigned long) statistics.max_free*4);
- return 0;
-}
-
-
-/*
-** More programmer friendly (as opposed to user friendly ;-) interface
-** to the memory statistics. Resembles the VxWorks memPartInfoGet but
-** does not take a partition id as parameter...
-*/
-int erl_mem_info_get(MEM_PART_STATS *stats){
- struct elib_stat statistics;
- if(!(alloc_flags & USING_ELIB_MALLOC))
- return -1;
- ELIB_LOCK;
- elib_stat(&statistics);
- ELIB_UNLOCK;
- stats->numBytesFree = statistics.mem_free*4;
- stats->numBlocksFree = statistics.free_blocks;
- stats->maxBlockSizeFree = statistics.max_free*4;
- stats->numBytesAlloc = statistics.mem_alloc*4;
- stats->numBlocksAlloc = statistics.mem_blocks-statistics.free_blocks;
- return 0;
-}
-
-/********************* Pipe driver **********************************/
-/*
- * Purpose: Pipe driver with Unix (unnamed) pipe semantics.
- * Author: Peter Hogfeldt ([email protected]) from an outline
- * by Per Hedeland ([email protected]).
- *
- * Note: This driver must *not* use the reclaim facilities, hence it
- * is placed here. (after the #undef's of open,malloc etc)
- *
- * This driver supports select() and non-blocking I/O via
- * ioctl(fd, FIONBIO, val).
- *
- * 1997-03-21 Peter Hogfeldt
- * Added non-blocking I/O.
- *
- */
-
-/*
- * SEMAPHORES
- *
- * Each end of a pipe has two semaphores: semExcl for serialising access to
- * the pipe end, and semBlock for blocking I/O.
- *
- * reader->semBlock is available (full) if and only if the pipe is
- * not empty, or the write end is closed. Otherwise
- * it is unavailable (empty). It is initially
- * unavailable.
- *
- * writer->semBlock is available (full) if and only if the pipe is
- * not full, or if the reader end is closed.
- * Otherwise it is unavailable. It is initially
- * available.
- */
-
-#define UXPIPE_SIZE 4096
-
-/* Forward declaration */
-typedef struct uxPipeDev UXPIPE_DEV;
-
-/*
- * Pipe descriptor (one for each open pipe).
- */
-typedef struct {
- int drvNum;
- UXPIPE_DEV *reader, *writer;
- RING_ID ringId;
-} UXPIPE;
-
-/*
- * Device descriptor (one for each of the read and write
- * ends of an open pipe).
- */
-struct uxPipeDev {
- UXPIPE *pipe;
- int blocking;
- SEL_WAKEUP_LIST wakeupList;
- SEM_ID semExcl;
- SEM_ID semBlock;
-};
-
-int uxPipeDrvNum = 0; /* driver number of pipe driver */
-
-#define PIPE_NAME "/uxpipe" /* only used internally */
-#define PIPE_READ "/r" /* ditto */
-#define PIPE_WRITE "/w" /* ditto */
-
-LOCAL char pipeRead[64], pipeWrite[64];
-LOCAL DEV_HDR devHdr;
-LOCAL UXPIPE *newPipe; /* communicate btwn open()s in pipe() */
-LOCAL SEM_ID pipeSem; /* mutual exclusion in pipe() */
-
-/* forward declarations */
-LOCAL int uxPipeOpen(DEV_HDR *pDv, char *name, int mode);
-LOCAL int uxPipeClose(UXPIPE_DEV *pDev);
-LOCAL int uxPipeRead(UXPIPE_DEV *pDev, char *buffer, int maxbytes);
-LOCAL int uxPipeWrite(UXPIPE_DEV *pDev, char *buffer, int nbytes);
-LOCAL STATUS uxPipeIoctl(FAST UXPIPE_DEV *pDev, FAST int function, int arg);
-
-
-/***************************************************************************
- *
- * uxPipeDrv - install Unix pipe driver
- *
- * This routine initializes the Unix pipe driver. It must be called
- * before any other routine in this driver.
- *
- * RETURNS:
- * OK, or ERROR if I/O system is unable to install driver.
- */
-
-STATUS
-uxPipeDrv(void)
-{
- if (uxPipeDrvNum > 0)
- return (OK); /* driver already installed */
- if ((uxPipeDrvNum = iosDrvInstall((FUNCPTR) NULL, (FUNCPTR) NULL,
- uxPipeOpen, uxPipeClose, uxPipeRead,
- uxPipeWrite, uxPipeIoctl)) == ERROR)
- return (ERROR);
- if (iosDevAdd(&devHdr, PIPE_NAME, uxPipeDrvNum) == ERROR)
- return (ERROR);
- strcpy(pipeRead, PIPE_NAME);
- strcat(pipeRead, PIPE_READ);
- strcpy(pipeWrite, PIPE_NAME);
- strcat(pipeWrite, PIPE_WRITE);
- if ((pipeSem = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE)) == NULL)
- return (ERROR);
- return (OK);
-}
-
-/***************************************************************************
- *
- * uxPipeOpen - open a pipe
- *
- * RETURNS: Pointer to device descriptor, or ERROR if memory cannot be
- * allocated (errno = ENOMEM), or invalid argument (errno = EINVAL).
- */
-
-/*
- * DEV_HDR *pDv; pointer to device header (dummy)
- * char *name; name of pipe to open ("/r" or "/w")
- * int mode; access mode (O_RDONLY or O_WRONLY)
- */
-LOCAL int
-uxPipeOpen(DEV_HDR *pDv, char *name, int mode)
-{
- UXPIPE_DEV *reader, *writer;
-
- if (mode == O_RDONLY && strcmp(name, PIPE_READ) == 0) {
- /* reader open */
- if ((newPipe = (UXPIPE *) malloc(sizeof(UXPIPE))) != NULL) {
- if ((newPipe->ringId = rngCreate(UXPIPE_SIZE)) != NULL) {
- if ((reader = (UXPIPE_DEV *) malloc(sizeof(UXPIPE_DEV))) != NULL) {
- if ((reader->semExcl = semBCreate(SEM_Q_FIFO, SEM_FULL)) != NULL) {
- if ((reader->semBlock = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) != NULL) {
- reader->pipe = newPipe;
- reader->blocking = 1;
- selWakeupListInit(&reader->wakeupList);
- newPipe->reader = reader;
- newPipe->writer = NULL;
- newPipe->drvNum = uxPipeDrvNum;
- return ((int) reader);
- }
- semDelete(reader->semExcl);
- }
- free(reader);
- }
- rngDelete(newPipe->ringId);
- }
- free(newPipe);
- newPipe = NULL;
- errno = ENOMEM;
- }
- } else if (mode == O_WRONLY && strcmp(name, PIPE_WRITE) == 0) {
- /* writer open */
- if (newPipe != NULL &&
- (writer = (UXPIPE_DEV *) malloc(sizeof(UXPIPE_DEV))) != NULL) {
- if ((writer->semExcl = semBCreate(SEM_Q_FIFO, SEM_FULL)) != NULL) {
- if ((writer->semBlock = semBCreate(SEM_Q_FIFO, SEM_FULL)) != NULL) {
- writer->blocking = 1;
- writer->pipe = newPipe;
- selWakeupListInit(&writer->wakeupList);
- newPipe->writer = writer;
- newPipe = NULL;
- return ((int) writer);
- }
- semDelete(writer->semExcl);
- }
- free(writer);
- }
- if (newPipe != NULL)
- free(newPipe);
- newPipe = NULL;
- errno = ENOMEM;
- } else {
- errno = EINVAL;
- }
- return (ERROR);
-}
-
-/***************************************************************************
- *
- * uxPipeClose - close read or write end of a pipe.
- *
- * RETURNS:
- * OK, or ERROR if device descriptor does not refer to an open read or
- write end of a pipe (errno = EBADF).
- */
-
-LOCAL int
-uxPipeClose(UXPIPE_DEV *pDev)
-{
- UXPIPE *pajp = pDev->pipe;
-
- taskLock();
- if (pDev == pajp->reader) {
- /* Close this end */
- semDelete(pDev->semExcl);
- semDelete(pDev->semBlock);
- free(pDev);
- pajp->reader = NULL;
- /* Inform the other end */
- if (pajp->writer != NULL) {
- selWakeupAll(&pajp->writer->wakeupList, SELWRITE);
- semGive(pajp->writer->semBlock);
- }
- } else if (pDev == pajp->writer) {
- /* Close this end */
- semDelete(pDev->semExcl);
- semDelete(pDev->semBlock);
- free(pDev);
- pajp->writer = NULL;
- /* Inform the other end */
- if (pajp->reader != NULL) {
- selWakeupAll(&pajp->reader->wakeupList, SELREAD);
- semGive(pajp->reader->semBlock);
- }
- } else {
- errno = EBADF;
- taskUnlock();
- return (ERROR);
- }
- if (pajp->reader == NULL && pajp->writer == NULL) {
- rngDelete(pajp->ringId);
- pajp->drvNum = 0;
- free(pajp);
- }
- taskUnlock();
- return (OK);
-}
-/***************************************************************************
- *
- * uxPipeRead - read from a pipe.
- *
- * Reads at most maxbytes bytes from the pipe. Blocks if blocking mode is
- * set and the pipe is empty.
- *
- * RETURNS:
- * number of bytes read, 0 on EOF, or ERROR if device descriptor does
- * not refer to an open read end of a pipe (errno = EBADF), or if
- * non-blocking mode is set and the pipe is empty (errno = EWOULDBLOCK).
- */
-
-LOCAL int
-uxPipeRead(UXPIPE_DEV *pDev, char *buffer, int maxbytes)
-{
- UXPIPE *pajp = pDev->pipe;
- int nbytes = 0;
-
- if (pDev != pajp->reader) {
- errno = EBADF;
- return (ERROR);
- }
- if (maxbytes == 0)
- return (0);
- semTake(pDev->semExcl, WAIT_FOREVER);
- /*
- * Note that semBlock may be full, although there is nothing to read.
- * This happens e.g. after the following sequence of operations: a
- * reader task blocks, a writer task writes two times (the first
- * write unblocks the reader task, the second write makes semBlock
- * full).
- */
- while (nbytes == 0) {
- if (pDev->blocking)
- semTake(pDev->semBlock, WAIT_FOREVER);
- /*
- * Reading and updating of the write end must not be interleaved
- * with a write from another task - hence we lock this task.
- */
- taskLock();
- nbytes = rngBufGet(pajp->ringId, buffer, maxbytes);
- if (nbytes > 0) {
- /* Give own semaphore if bytes remain or if write end is closed */
- if ((!rngIsEmpty(pajp->ringId) || pajp->writer == NULL) &&
- pDev->blocking)
- semGive(pDev->semBlock);
- /* Inform write end */
- if (pajp->writer != NULL) {
- if (pajp->writer->blocking)
- semGive(pajp->writer->semBlock);
- selWakeupAll(&pajp->writer->wakeupList, SELWRITE);
- }
- } else if (pajp->writer == NULL) {
- nbytes = 0; /* EOF */
- /* Give semaphore when write end is closed */
- if (pDev->blocking)
- semGive(pDev->semBlock);
- taskUnlock();
- semGive(pDev->semExcl);
- return (nbytes);
- } else if (!pDev->blocking) {
- taskUnlock();
- semGive(pDev->semExcl);
- errno = EWOULDBLOCK;
- return (ERROR);
- }
- taskUnlock();
- }
- semGive(pDev->semExcl);
- return (nbytes);
-}
-
-/***************************************************************************
- *
- * uxPipeWrite - write to a pipe.
- *
- * Writes nbytes bytes to the pipe. Blocks if blocking mode is set, and if
- * the pipe is full.
- *
- * RETURNS:
- * number of bytes written, or ERROR if the device descriptor does not
- * refer to an open write end of a pipe (errno = EBADF); or if the read end
- * of the pipe is closed (errno = EPIPE); or if non-blocking mode is set
- * and the pipe is full (errno = EWOULDBLOCK).
- *
- */
-
-LOCAL int
-uxPipeWrite(UXPIPE_DEV *pDev, char *buffer, int nbytes)
-{
-
- UXPIPE *pajp = pDev->pipe;
- int sofar = 0, written;
-
- if (pDev != pajp->writer) {
- errno = EBADF;
- return (ERROR);
- }
- if (pajp->reader == NULL) {
- errno = EPIPE;
- return (ERROR);
- }
- if (nbytes == 0)
- return (0);
- semTake(pDev->semExcl, WAIT_FOREVER);
- while (sofar < nbytes) {
- if (pDev->blocking)
- semTake(pDev->semBlock, WAIT_FOREVER);
- if (pajp->reader == NULL) {
- errno = EPIPE;
- semGive(pDev->semBlock);
- semGive(pDev->semExcl);
- return (ERROR);
- }
- /* Writing and updating of the read end must not be interleaved
- * with a read from another task - hence we lock this task.
- */
- taskLock();
- written = rngBufPut(pajp->ringId, buffer + sofar, nbytes - sofar);
- sofar += written;
- /* Inform the read end if we really wrote something */
- if (written > 0 && pajp->reader != NULL) {
- selWakeupAll(&pajp->reader->wakeupList, SELREAD);
- if (pajp->reader->blocking)
- semGive(pajp->reader->semBlock);
- }
- taskUnlock();
- if (!pDev->blocking) {
- if (sofar == 0) {
- errno = EWOULDBLOCK;
- sofar = ERROR;
- }
- break;
- }
- }
- /* Give own semaphore if space remains */
- if (!rngIsFull(pajp->ringId) && pDev->blocking)
- semGive(pDev->semBlock);
- semGive(pDev->semExcl);
- return (sofar);
-}
-
-/***************************************************************************
- *
- * uxPipeIoctl - do device specific I/O control
- *
- * RETURNS:
- * OK or ERROR.
- */
-
-LOCAL STATUS
-uxPipeIoctl(FAST UXPIPE_DEV *pDev, FAST int function, int arg)
-
-{
- UXPIPE *pajp = pDev->pipe;
- int status = OK;
-
- switch (function) {
- case FIONBIO:
- pDev->blocking = (*(int *)arg) ? 0 : 1;
- break;
- case FIOSELECT:
- taskLock();
- selNodeAdd(&pDev->wakeupList, (SEL_WAKEUP_NODE *) arg);
- if (selWakeupType((SEL_WAKEUP_NODE *) arg) == SELREAD &&
- pDev == pajp->reader &&
- (!rngIsEmpty(pajp->ringId) || pajp->writer == NULL))
- selWakeup((SEL_WAKEUP_NODE *) arg);
- if (selWakeupType((SEL_WAKEUP_NODE *) arg) == SELWRITE &&
- pDev == pajp->writer &&
- (!rngIsFull(pajp->ringId) || pajp->reader == NULL))
- selWakeup((SEL_WAKEUP_NODE *) arg);
- taskUnlock();
- break;
- case FIOUNSELECT:
- selNodeDelete(&pDev->wakeupList, (SEL_WAKEUP_NODE *) arg);
- break;
- default:
- status = ERROR;
- break;
- }
- return (status);
-}
-
-/***************************************************************************
- *
- * pipe - create an intertask channel
- *
- * Creates a pipe. fd[0] (fd[1]) is the read (write) file descriptor.
- *
- * RETURNS:
- * OK or ERROR, if the pipe could not be created.
- */
-
-STATUS
-pipe(int fd[2])
-{
- semTake(pipeSem, WAIT_FOREVER);
- if ((fd[0] = open(pipeRead, O_RDONLY, 0)) != ERROR) {
- if ((fd[1] = open(pipeWrite, O_WRONLY, 0)) != ERROR) {
- semGive(pipeSem);
- return (OK);
- }
- (void) close(fd[0]);
- }
- errno &= 0xFFFF;
- if((errno & 0xFFFF) == EINTR) /* Why on earth EINTR??? */
- errno = ENFILE; /* It means we are out of file descriptors...*/
- semGive(pipeSem);
- return (ERROR);
-}
-
-/***************************************************************************
- *
- * uxPipeShow - display pipe information
- *
- * RETURNS:
- * N/A.
- */
-
-void
-uxPipeShow(int fd)
-{
- UXPIPE_DEV *pDev;
- UXPIPE *pajp;
- int drvValue;
-
- if ((drvValue = iosFdValue(fd)) == ERROR) {
- erts_fprintf(stderr, "Error: file descriptor invalid\n");
- return;
- }
- pDev = (UXPIPE_DEV *)drvValue;
- pajp = pDev->pipe;
- if (pajp->drvNum != uxPipeDrvNum) {
- erts_fprintf(stderr, "Error: Not a ux pipe device\n");
- return;
- }
- erts_fprintf(stderr, "Device : 0x%x\n", (int) pDev);
- erts_fprintf(stderr, "Buffer size : %d\n", UXPIPE_SIZE);
- erts_fprintf(stderr, "Bytes in buffer : %d\n\n", rngNBytes(pajp->ringId));
- erts_fprintf(stderr, "READ END\n\n");
- if (pajp->reader != NULL) {
- erts_fprintf(stderr, "Mode : ");
- erts_fprintf(stderr, "%s\n",
- (pajp->reader->blocking) ? "blocking" : "non-blocking");
- }
- erts_fprintf(stderr, "Status : ");
- if (pajp->reader != NULL) {
- erts_fprintf(stderr, "OPEN\n");
- erts_fprintf(stderr, "Wake-up list : %d\n\n",
- selWakeupListLen(&pajp->reader->wakeupList));
- erts_fprintf(stderr, "Exclusion Semaphore\n");
- semShow(pajp->reader->semExcl, 1);
- erts_fprintf(stderr, "Blocking Semaphore\n");
- semShow(pajp->reader->semBlock, 1);
- } else
- erts_fprintf(stderr, "CLOSED\n\n");
- erts_fprintf(stderr, "WRITE END\n\n");
- if (pajp->writer != NULL) {
- erts_fprintf(stderr, "Mode : ");
- erts_fprintf(stderr, "%s\n",
- (pajp->writer->blocking) ? "blocking" : "non-blocking");
- }
- erts_fprintf(stderr, "Status : ");
- if (pajp->writer != NULL) {
- erts_fprintf(stderr, "OPEN\n");
- erts_fprintf(stderr, "Wake-up list : %d\n\n",
- selWakeupListLen(&pajp->writer->wakeupList));
- erts_fprintf(stderr, "Exclusion Semaphore\n");
- semShow(pajp->writer->semExcl, 1);
- erts_fprintf(stderr, "Blocking Semaphore\n");
- semShow(pajp->writer->semBlock, 1);
- } else
- erts_fprintf(stderr, "CLOSED\n\n");
-}
-
-#ifdef DEBUG
-void
-erl_assert_error(char* expr, char* file, int line)
-{
- fflush(stdout);
- fprintf(stderr, "Assertion failed: %s in %s, line %d\n",
- expr, file, line);
- fflush(stderr);
- erl_crash_dump(file, line, "Assertion failed: %s\n", expr);
- abort();
-}
-void
-erl_debug(char* fmt, ...)
-{
- char sbuf[1024]; /* Temporary buffer. */
- va_list va;
-
- va_start(va, fmt);
- vsprintf(sbuf, fmt, va);
- va_end(va);
- fprintf(stderr, "%s\n", sbuf);
-}
-#endif
diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h
index 03298a6c54..5ce1a61303 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-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2012. 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
@@ -90,6 +90,11 @@
#define strncasecmp _strnicmp
+#ifndef __GNUC__
+# undef ERTS_I64_LITERAL
+# define ERTS_I64_LITERAL(X) X##i64
+#endif
+
/*
* Practial Windows specific macros.
*/
@@ -171,8 +176,8 @@ extern clock_t sys_times(SysTimes *buffer);
extern char *win_build_environment(char *);
typedef struct {
- char *environment_strings;
- char *next_string;
+ WCHAR *environment_strings;
+ WCHAR *next_string;
} GETENV_STATE;
void erts_sys_env_init(void);
diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c
index acbbfc2ce9..6c69fecbf3 100755
--- a/erts/emulator/sys/win32/sys.c
+++ b/erts/emulator/sys/win32/sys.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2012. 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
@@ -420,6 +420,8 @@ typedef struct async_io {
HANDLE ioAllowed; /* The thread will wait for this event
* before starting a new read or write.
*/
+ HANDLE flushEvent; /* Used to signal that a flush should be done. */
+ HANDLE flushReplyEvent; /* Used to signal that a flush has been done. */
DWORD pendingError; /* Used to delay presentating an error to Erlang
* until the check_io function is entered.
*/
@@ -878,6 +880,8 @@ init_async_io(AsyncIo* aio, int use_threads)
aio->ov.Offset = 0L;
aio->ov.OffsetHigh = 0L;
aio->ioAllowed = NULL;
+ aio->flushEvent = NULL;
+ aio->flushReplyEvent = NULL;
aio->pendingError = 0;
aio->bytesTransferred = 0;
#ifdef ERTS_SMP
@@ -890,6 +894,12 @@ init_async_io(AsyncIo* aio, int use_threads)
aio->ioAllowed = CreateAutoEvent(FALSE);
if (aio->ioAllowed == NULL)
return -1;
+ aio->flushEvent = CreateAutoEvent(FALSE);
+ if (aio->flushEvent == NULL)
+ return -1;
+ aio->flushReplyEvent = CreateAutoEvent(FALSE);
+ if (aio->flushReplyEvent == NULL)
+ return -1;
}
return 0;
}
@@ -923,6 +933,14 @@ release_async_io(AsyncIo* aio, ErlDrvPort port_num)
if (aio->ioAllowed != NULL)
CloseHandle(aio->ioAllowed);
aio->ioAllowed = NULL;
+
+ if (aio->flushEvent != NULL)
+ CloseHandle(aio->flushEvent);
+ aio->flushEvent = NULL;
+
+ if (aio->flushReplyEvent != NULL)
+ CloseHandle(aio->flushReplyEvent);
+ aio->flushReplyEvent = NULL;
}
/* ----------------------------------------------------------------------
@@ -1204,9 +1222,7 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
*/
DEBUGF(("Spawning \"%s\"\n", name));
- envir = win_build_environment(envir); /* Still an ansi environment, could be
- converted to unicode for spawn_executable, but
- that is not done (yet) */
+ envir = win_build_environment(envir); /* Always a unicode environment */
ok = create_child_process(name,
hChildStdin,
hChildStdout,
@@ -1485,7 +1501,8 @@ create_child_process
NULL,
NULL,
TRUE,
- createFlags | staticCreateFlags,
+ createFlags | staticCreateFlags |
+ CREATE_UNICODE_ENVIRONMENT,
env,
wd,
&siStartInfo,
@@ -1613,7 +1630,8 @@ create_child_process
NULL,
NULL,
TRUE,
- createFlags | staticCreateFlags,
+ createFlags | staticCreateFlags |
+ CREATE_UNICODE_ENVIRONMENT,
env,
(WCHAR *) wd,
&siStartInfo,
@@ -2083,16 +2101,26 @@ threaded_writer(LPVOID param)
AsyncIo* aio = (AsyncIo *) param;
HANDLE thread = GetCurrentThread();
char* buf;
- DWORD numToWrite;
+ DWORD numToWrite, handle;
int ok;
+ HANDLE handles[2];
+ handles[0] = aio->ioAllowed;
+ handles[1] = aio->flushEvent;
for (;;) {
- WaitForSingleObject(aio->ioAllowed, INFINITE);
+ handle = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
if (aio->flags & DF_EXIT_THREAD)
break;
+
buf = OV_BUFFER_PTR(aio);
numToWrite = OV_NUM_TO_READ(aio);
aio->pendingError = 0;
+
+ if (handle == (WAIT_OBJECT_0 + 1) && numToWrite == 0) {
+ SetEvent(aio->flushReplyEvent);
+ continue;
+ }
+
ok = WriteFile(aio->fd, buf, numToWrite, &aio->bytesTransferred, NULL);
if (!ok) {
aio->pendingError = GetLastError();
@@ -2127,7 +2155,11 @@ threaded_writer(LPVOID param)
}
}
}
- SetEvent(aio->ov.hEvent);
+ OV_NUM_TO_READ(aio) = 0;
+ if (handle == (WAIT_OBJECT_0 + 1))
+ SetEvent(aio->flushReplyEvent);
+ else
+ SetEvent(aio->ov.hEvent);
if (aio->pendingError != NO_ERROR || aio->bytesTransferred == 0)
break;
if (aio->flags & DF_EXIT_THREAD)
@@ -2193,6 +2225,43 @@ fd_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
if ((dp = new_driver_data(port_num, opts->packet_bytes, 2, TRUE)) == NULL)
return ERL_DRV_ERROR_GENERAL;
+ /**
+ * Here is a brief description about how the fd driver works on windows.
+ *
+ * fd_init:
+ * For each in/out fd pair a threaded_reader and threaded_writer thread is
+ * created. Within the DriverData struct each of the threads have an AsyncIO
+ * sctruct associated with it. Within AsyncIO there are two important HANDLEs,
+ * ioAllowed and ov.hEvent. ioAllowed is used to signal the threaded_* threads
+ * should read/write some data, and ov.hEvent is driver_select'ed to be used to
+ * signal that the thread is done reading/writing.
+ *
+ * The reason for the driver being threaded like this is because once the FD is open
+ * on windows, it is not possible to set the it in overlapped mode. So we have to
+ * simulate this using threads.
+ *
+ * output:
+ * When an output occurs the data to be outputted is copied to AsyncIO.ov. Then
+ * the ioAllowed HANDLE is set, ov.hEvent is cleared and the port is marked as busy.
+ * The threaded_writer thread is lying in WaitForMultipleObjects on ioAllowed, and
+ * when signalled it writes all data in AsyncIO.ov and then sets ov.hEvent so that
+ * ready_output gets triggered and (potentially) sends the reply to the port and
+ * marks the port an non-busy.
+ *
+ * input:
+ * The threaded_reader is lying waiting in ReadFile on the in fd and when a new
+ * line is written it sets ov.hEvent that new data is available and then goes
+ * and waits for ioAllowed to be set. ready_input is run when ov.hEvent is set and
+ * delivers the data to the port. Then ioAllowed is signalled again and threaded_reader
+ * goes back to ReadFile.
+ *
+ * shutdown:
+ * In order to guarantee that all io is outputted before the driver is stopped,
+ * fd_stop uses flushEvent and flushReplyEvent to make sure that there is no data
+ * in ov which needs writing before returning from fd_stop.
+ *
+ **/
+
if (!create_file_thread(&dp->in, DO_READ)) {
dp->port_num = PORT_FREE;
return ERL_DRV_ERROR_GENERAL;
@@ -2241,6 +2310,8 @@ static void fd_stop(ErlDrvData d)
(void) driver_select(dp->port_num,
(ErlDrvEvent)dp->out.ov.hEvent,
ERL_DRV_WRITE, 0);
+ SetEvent(dp->out.flushEvent);
+ WaitForSingleObject(dp->out.flushReplyEvent, INFINITE);
}
}
@@ -2764,10 +2835,10 @@ static void stop_select(ErlDrvEvent e, void* _)
** no interpretation 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){
+void sys_get_pid(char *buffer, size_t buffer_size){
DWORD p = GetCurrentProcessId();
/* The pid is scalar and is an unsigned long. */
- sprintf(buffer,"%lu",(unsigned long) p);
+ erts_snprintf(buffer, buffer_size, "%lu",(unsigned long) p);
}
void
@@ -3107,7 +3178,8 @@ erl_assert_error(char* expr, char* file, int line)
{
char message[1024];
- sprintf(message, "File %hs, line %d: %hs", file, line, expr);
+ erts_snprintf(message, sizeof(message),
+ "File %hs, line %d: %hs", file, line, expr);
MessageBox(GetActiveWindow(), message, "Assertion failed",
MB_OK | MB_ICONERROR);
#if 0
diff --git a/erts/emulator/sys/win32/sys_env.c b/erts/emulator/sys/win32/sys_env.c
index 064745d418..754f4c6e4c 100644
--- a/erts/emulator/sys/win32/sys_env.c
+++ b/erts/emulator/sys/win32/sys_env.c
@@ -1,271 +1,299 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2002-2011. 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
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "sys.h"
-#include "erl_sys_driver.h"
-#include "erl_alloc.h"
-
-static char* merge_environment(char *current, char *add);
-static char* arg_to_env(char **arg);
-static char** env_to_arg(char *env);
-static char** find_arg(char **arg, char *str);
-static int compare(const void *a, const void *b);
-
-static erts_smp_rwmtx_t environ_rwmtx;
-
-void
-erts_sys_env_init(void)
-{
- erts_smp_rwmtx_init(&environ_rwmtx, "environ");
-}
-
-int
-erts_sys_putenv(char *key_value, int sep_ix)
-{
- int res;
- char sep = key_value[sep_ix];
- ASSERT(sep == '=');
- key_value[sep_ix] = '\0';
- erts_smp_rwmtx_rwlock(&environ_rwmtx);
- res = (SetEnvironmentVariable((LPCTSTR) key_value,
- (LPCTSTR) &key_value[sep_ix+1]) ? 0 : 1);
- erts_smp_rwmtx_rwunlock(&environ_rwmtx);
- key_value[sep_ix] = sep;
- return res;
-}
-
-int
-erts_sys_getenv__(char *key, char *value, size_t *size)
-{
- size_t req_size = 0;
- int res = 0;
- DWORD new_size;
-
- SetLastError(0);
- new_size = GetEnvironmentVariable((LPCTSTR) key,
- (LPTSTR) value,
- (DWORD) *size);
- res = !new_size && GetLastError() == ERROR_ENVVAR_NOT_FOUND ? -1 : 0;
- if (res < 0)
- return res;
- res = new_size > *size ? 1 : 0;
- *size = new_size;
- return res;
-}
-
-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;
-}
-
-struct win32_getenv_state {
- char *env;
- char *next;
-};
-
-
-void init_getenv_state(GETENV_STATE *state)
-{
- erts_smp_rwmtx_rlock(&environ_rwmtx);
- state->environment_strings = (char *) GetEnvironmentStrings();
- state->next_string = state->environment_strings;
-}
-
-char *getenv_string(GETENV_STATE *state)
-{
- ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx));
- if (state->next_string[0] == '\0')
- return NULL;
- else {
- char *res = state->next_string;
- state->next_string += sys_strlen(res) + 1;
- return res;
- }
-}
-
-void fini_getenv_state(GETENV_STATE *state)
-{
- FreeEnvironmentStrings(state->environment_strings);
- state->environment_strings = state->next_string = NULL;
- erts_smp_rwmtx_runlock(&environ_rwmtx);
-}
-
-char*
-win_build_environment(char* new_env)
-{
- if (new_env == NULL) {
- return NULL;
- } else {
- char *tmp, *merged;
-
- erts_smp_rwmtx_rlock(&environ_rwmtx);
- tmp = GetEnvironmentStrings();
- merged = merge_environment(tmp, new_env);
-
- FreeEnvironmentStrings(tmp);
- erts_smp_rwmtx_runlock(&environ_rwmtx);
- return merged;
- }
-}
-
-static char*
-merge_environment(char *old, char *add)
-{
- char **a_arg = env_to_arg(add);
- char **c_arg = env_to_arg(old);
- char *ret;
- int i, j;
-
- for(i = 0; c_arg[i] != NULL; ++i)
- ;
-
- for(j = 0; a_arg[j] != NULL; ++j)
- ;
-
- c_arg = erts_realloc(ERTS_ALC_T_TMP,
- c_arg, (i+j+1) * sizeof(char *));
-
- for(j = 0; a_arg[j] != NULL; ++j){
- char **tmp;
- char *current = a_arg[j];
- char *eq_p = strchr(current,'=');
- int unset = (eq_p!=NULL && eq_p[1]=='\0');
-
- if ((tmp = find_arg(c_arg, current)) != NULL) {
- if (!unset) {
- *tmp = current;
- } else {
- *tmp = c_arg[--i];
- c_arg[i] = NULL;
- }
- } else if (!unset) {
- c_arg[i++] = current;
- c_arg[i] = NULL;
- }
- }
- ret = arg_to_env(c_arg);
- erts_free(ERTS_ALC_T_TMP, c_arg);
- erts_free(ERTS_ALC_T_TMP, a_arg);
- return ret;
-}
-
-static char**
-find_arg(char **arg, char *str)
-{
- char *tmp;
- int len;
-
- if ((tmp = strchr(str, '=')) != NULL) {
- tmp++;
- len = tmp - str;
- while (*arg != NULL){
- if (_strnicmp(*arg, str, len) == 0){
- return arg;
- }
- ++arg;
- }
- }
- return NULL;
-}
-
-static int
-compare(const void *a, const void *b)
-{
- char *s1 = *((char **) a);
- char *s2 = *((char **) b);
- char *e1 = strchr(s1,'=');
- char *e2 = strchr(s2,'=');
- int ret;
- int len;
-
- if(!e1)
- e1 = s1 + strlen(s1);
- if(!e2)
- e2 = s2 + strlen(s2);
-
- if((e1 - s1) > (e2 - s2))
- len = (e2 - s2);
- else
- len = (e1 - s1);
-
- ret = _strnicmp(s1,s2,len);
- if (ret == 0)
- return ((e1 - s1) - (e2 - s2));
- else
- return ret;
-}
-
-static char**
-env_to_arg(char *env)
-{
- char **ret;
- char *tmp;
- int i;
- int num_strings = 0;
-
- for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1) {
- ++num_strings;
- }
- ret = erts_alloc(ERTS_ALC_T_TMP, sizeof(char *) * (num_strings + 1));
- i = 0;
- for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1){
- ret[i++] = tmp;
- }
- ret[i] = NULL;
- return ret;
-}
-
-static char*
-arg_to_env(char **arg)
-{
- char *block;
- char *ptr;
- int i;
- int totlen = 1; /* extra '\0' */
-
- for(i = 0; arg[i] != NULL; ++i) {
- totlen += strlen(arg[i])+1;
- }
-
- /* sort the environment vector */
- qsort(arg, i, sizeof(char *), &compare);
-
- if (totlen == 1){
- block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, 2);
- block[0] = block[1] = '\0';
- } else {
- block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, totlen);
- ptr = block;
- for(i=0; arg[i] != NULL; ++i){
- strcpy(ptr, arg[i]);
- ptr += strlen(ptr)+1;
- }
- *ptr = '\0';
- }
- return block;
-}
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2002-2012. 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
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_sys_driver.h"
+#include "erl_alloc.h"
+
+static WCHAR *merge_environment(WCHAR *current, WCHAR *add);
+static WCHAR *arg_to_env(WCHAR **arg);
+static WCHAR **env_to_arg(WCHAR *env);
+static WCHAR **find_arg(WCHAR **arg, WCHAR *str);
+static int compare(const void *a, const void *b);
+
+static erts_smp_rwmtx_t environ_rwmtx;
+
+void
+erts_sys_env_init(void)
+{
+ erts_smp_rwmtx_init(&environ_rwmtx, "environ");
+}
+
+int
+erts_sys_putenv_raw(char *key, char *value)
+{
+ int res;
+ erts_smp_rwmtx_rwlock(&environ_rwmtx);
+ res = (SetEnvironmentVariable((LPCTSTR) key,
+ (LPCTSTR) value) ? 0 : 1);
+ erts_smp_rwmtx_rwunlock(&environ_rwmtx);
+ return res;
+}
+
+int
+erts_sys_putenv(char *key, char *value)
+{
+ int res;
+ WCHAR *wkey = (WCHAR *) key;
+ WCHAR *wvalue = (WCHAR *) value;
+ erts_smp_rwmtx_rwlock(&environ_rwmtx);
+ res = (SetEnvironmentVariableW(wkey,
+ wvalue) ? 0 : 1);
+ erts_smp_rwmtx_rwunlock(&environ_rwmtx);
+ return res;
+}
+
+int
+erts_sys_getenv(char *key, char *value, size_t *size)
+{
+ size_t req_size = 0;
+ int res = 0;
+ DWORD new_size;
+ WCHAR *wkey = (WCHAR *) key;
+ WCHAR *wvalue = (WCHAR *) value;
+ DWORD wsize = *size / (sizeof(WCHAR) / sizeof(char));
+
+ SetLastError(0);
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ new_size = GetEnvironmentVariableW(wkey,
+ wvalue,
+ (DWORD) wsize);
+ res = !new_size && GetLastError() == ERROR_ENVVAR_NOT_FOUND ? -1 : 0;
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ if (res < 0)
+ return res;
+ res = new_size > wsize ? 1 : 0;
+ *size = new_size * (sizeof(WCHAR) / sizeof(char));
+ return res;
+}
+int
+erts_sys_getenv__(char *key, char *value, size_t *size)
+{
+ size_t req_size = 0;
+ int res = 0;
+ DWORD new_size;
+
+ SetLastError(0);
+ new_size = GetEnvironmentVariable((LPCTSTR) key,
+ (LPTSTR) value,
+ (DWORD) *size);
+ res = !new_size && GetLastError() == ERROR_ENVVAR_NOT_FOUND ? -1 : 0;
+ if (res < 0)
+ return res;
+ res = new_size > *size ? 1 : 0;
+ *size = new_size;
+ return res;
+}
+
+int
+erts_sys_getenv_raw(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 init_getenv_state(GETENV_STATE *state)
+{
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ state->environment_strings = GetEnvironmentStringsW();
+ state->next_string = state->environment_strings;
+}
+
+char *getenv_string(GETENV_STATE *state)
+{
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx));
+ if (state->next_string[0] == L'\0') {
+ return NULL;
+ } else {
+ WCHAR *res = state->next_string;
+ state->next_string += wcslen(res) + 1;
+ return (char *) res;
+ }
+}
+
+void fini_getenv_state(GETENV_STATE *state)
+{
+ FreeEnvironmentStringsW(state->environment_strings);
+ state->environment_strings = state->next_string = NULL;
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+}
+
+char*
+win_build_environment(char* new_env)
+{
+ if (new_env == NULL) {
+ return NULL;
+ } else {
+ WCHAR *tmp, *merged, *tmp_new;
+
+ tmp_new = (WCHAR *) new_env;
+
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ tmp = GetEnvironmentStringsW();
+ merged = merge_environment(tmp, tmp_new);
+
+ FreeEnvironmentStringsW(tmp);
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ return (char *) merged;
+ }
+}
+
+static WCHAR *
+merge_environment(WCHAR *old, WCHAR *add)
+{
+ WCHAR **a_arg = env_to_arg(add);
+ WCHAR **c_arg = env_to_arg(old);
+ WCHAR *ret;
+ int i, j;
+
+ for(i = 0; c_arg[i] != NULL; ++i)
+ ;
+
+ for(j = 0; a_arg[j] != NULL; ++j)
+ ;
+
+ c_arg = erts_realloc(ERTS_ALC_T_TMP,
+ c_arg, (i+j+1) * sizeof(WCHAR *));
+
+ for(j = 0; a_arg[j] != NULL; ++j){
+ WCHAR **tmp;
+ WCHAR *current = a_arg[j];
+ WCHAR *eq_p = wcschr(current,L'=');
+ int unset = (eq_p!=NULL && eq_p[1]==L'\0');
+
+ if ((tmp = find_arg(c_arg, current)) != NULL) {
+ if (!unset) {
+ *tmp = current;
+ } else {
+ *tmp = c_arg[--i];
+ c_arg[i] = NULL;
+ }
+ } else if (!unset) {
+ c_arg[i++] = current;
+ c_arg[i] = NULL;
+ }
+ }
+ ret = arg_to_env(c_arg);
+ erts_free(ERTS_ALC_T_TMP, c_arg);
+ erts_free(ERTS_ALC_T_TMP, a_arg);
+ return ret;
+}
+
+static WCHAR**
+find_arg(WCHAR **arg, WCHAR *str)
+{
+ WCHAR *tmp;
+ int len;
+
+ if ((tmp = wcschr(str, L'=')) != NULL) {
+ tmp++;
+ len = tmp - str;
+ while (*arg != NULL){
+ if (_wcsnicmp(*arg, str, len) == 0){
+ return arg;
+ }
+ ++arg;
+ }
+ }
+ return NULL;
+}
+
+static int
+compare(const void *a, const void *b)
+{
+ WCHAR *s1 = *((WCHAR **) a);
+ WCHAR *s2 = *((WCHAR **) b);
+ WCHAR *e1 = wcschr(s1,L'=');
+ WCHAR *e2 = wcschr(s2,L'=');
+ int ret;
+ int len;
+
+ if(!e1)
+ e1 = s1 + wcslen(s1);
+ if(!e2)
+ e2 = s2 + wcslen(s2);
+
+ if((e1 - s1) > (e2 - s2))
+ len = (e2 - s2);
+ else
+ len = (e1 - s1);
+
+ ret = _wcsnicmp(s1,s2,len);
+ if (ret == 0)
+ return ((e1 - s1) - (e2 - s2));
+ else
+ return ret;
+}
+
+static WCHAR**
+env_to_arg(WCHAR *env)
+{
+ WCHAR **ret;
+ WCHAR *tmp;
+ int i;
+ int num_strings = 0;
+
+ for(tmp = env; *tmp != '\0'; tmp += wcslen(tmp)+1) {
+ ++num_strings;
+ }
+ ret = erts_alloc(ERTS_ALC_T_TMP, sizeof(WCHAR *) * (num_strings + 1));
+ i = 0;
+ for(tmp = env; *tmp != '\0'; tmp += wcslen(tmp)+1){
+ ret[i++] = tmp;
+ }
+ ret[i] = NULL;
+ return ret;
+}
+
+static WCHAR *
+arg_to_env(WCHAR **arg)
+{
+ WCHAR *block;
+ WCHAR *ptr;
+ int i;
+ int totlen = 1; /* extra '\0' */
+
+ for(i = 0; arg[i] != NULL; ++i) {
+ totlen += wcslen(arg[i])+1;
+ }
+
+ /* sort the environment vector */
+ qsort(arg, i, sizeof(WCHAR *), &compare);
+
+ if (totlen == 1){
+ block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, 2 * sizeof(WCHAR));
+ block[0] = block[1] = '\0';
+ } else {
+ block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, totlen * sizeof(WCHAR));
+ ptr = block;
+ for(i=0; arg[i] != NULL; ++i){
+ wcscpy(ptr, arg[i]);
+ ptr += wcslen(ptr)+1;
+ }
+ *ptr = '\0';
+ }
+ return block;
+}
diff --git a/erts/emulator/sys/win32/sys_float.c b/erts/emulator/sys/win32/sys_float.c
index 6558ad2d99..09dad89140 100644
--- a/erts/emulator/sys/win32/sys_float.c
+++ b/erts/emulator/sys/win32/sys_float.c
@@ -118,18 +118,18 @@ sys_chars_to_double(char *buf, double *fp)
*/
int
-sys_double_to_chars(double fp, char *buf)
+sys_double_to_chars(double fp, char *buffer, size_t buffer_size)
{
- char *s = buf;
+ char *s = buffer;
- (void) sprintf(buf, "%.20e", fp);
+ (void) erts_snprintf(buffer, buffer_size, "%.20e", fp);
/* 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-buf; /* i.e strlen(buf) */
+ return s-buffer; /* i.e strlen(buffer) */
}
int
diff --git a/erts/emulator/sys/win32/sys_interrupt.c b/erts/emulator/sys/win32/sys_interrupt.c
index 347c31053b..36c43e93da 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-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2012. 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
@@ -75,11 +75,11 @@ BOOL WINAPI ctrl_handler_ignore_break(DWORD dwCtrlType)
return TRUE;
break;
case CTRL_LOGOFF_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
if (nohup)
return TRUE;
/* else pour through... */
case CTRL_CLOSE_EVENT:
- case CTRL_SHUTDOWN_EVENT:
erl_exit(0, "");
break;
}
@@ -127,11 +127,11 @@ BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
SetEvent(erts_sys_break_event);
break;
case CTRL_LOGOFF_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
if (nohup)
return TRUE;
/* else pour through... */
case CTRL_CLOSE_EVENT:
- case CTRL_SHUTDOWN_EVENT:
erl_exit(0, "");
break;
}
diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c
index b5123dc45d..2f2dfc8197 100644
--- a/erts/emulator/sys/win32/sys_time.c
+++ b/erts/emulator/sys/win32/sys_time.c
@@ -26,11 +26,7 @@
#include "sys.h"
#include "assert.h"
-#ifdef __GNUC__
-#define LL_LITERAL(X) X##LL
-#else
-#define LL_LITERAL(X) X##i64
-#endif
+#define LL_LITERAL(X) ERTS_I64_LITERAL(X)
/******************* Routines for time measurement *********************/
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index 03311423f0..9594ab48b1 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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
@@ -47,6 +47,7 @@ MODULES= \
busy_port_SUITE \
call_trace_SUITE \
code_SUITE \
+ code_parallel_load_SUITE \
crypto_SUITE \
ddll_SUITE \
decode_packet_SUITE \
@@ -137,10 +138,10 @@ TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
EMAKEFILE=Emakefile
-TEST_SPEC_FILES = emulator.spec \
- emulator.spec.win \
- emulator.spec.vxworks \
- emulator.spec.ose
+TEST_SPEC_FILES= emulator.spec \
+ emulator.spec.win \
+ emulator_bench.spec
+
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl
index 99ed8f1748..e2442861c7 100644
--- a/erts/emulator/test/bif_SUITE.erl
+++ b/erts/emulator/test/bif_SUITE.erl
@@ -25,7 +25,9 @@
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
display/1, display_huge/0,
- types/1,
+ 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]).
@@ -33,7 +35,9 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [types, t_list_to_existing_atom, os_env, otp_7526,
+ [erl_bif_types, guard_bifs_in_erl_bif_types, shadow_comments,
+ specs, improper_bif_stubs, auto_imports,
+ t_list_to_existing_atom, os_env, otp_7526,
display,
atom_to_binary, binary_to_atom, binary_to_existing_atom,
min_max, erlang_halt].
@@ -86,33 +90,20 @@ deeep(N,Acc) ->
deeep(N) ->
deeep(N,[hello]).
+erl_bif_types(Config) when is_list(Config) ->
+ ensure_erl_bif_types_compiled(),
-types(Config) when is_list(Config) ->
- c:l(erl_bif_types),
- case erlang:function_exported(erl_bif_types, module_info, 0) of
- false ->
- %% Fail cleanly.
- ?line ?t:fail("erl_bif_types not compiled");
- true ->
- types_1()
- end.
-
-types_1() ->
- ?line List0 = erlang:system_info(snifs),
+ List0 = erlang:system_info(snifs),
%% Ignore missing type information for hipe BIFs.
- ?line List = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs],
+ List = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs],
- case [MFA || MFA <- List, not known_types(MFA)] of
- [] ->
- types_2(List);
- BadTypes ->
- io:put_chars("No type information:\n"),
- io:format("~p\n", [lists:sort(BadTypes)]),
- ?line ?t:fail({length(BadTypes),bifs_without_types})
- end.
+ KnownTypes = [MFA || MFA <- List, known_types(MFA)],
+ io:format("There are ~p BIFs with type information in erl_bif_types.",
+ [length(KnownTypes)]),
+ erl_bif_types_2(KnownTypes).
-types_2(List) ->
+erl_bif_types_2(List) ->
BadArity = [MFA || {M,F,A}=MFA <- List,
begin
Types = erl_bif_types:arg_types(M, F, A),
@@ -120,14 +111,14 @@ types_2(List) ->
end],
case BadArity of
[] ->
- types_3(List);
+ erl_bif_types_3(List);
[_|_] ->
io:put_chars("Bifs with bad arity\n"),
io:format("~p\n", [BadArity]),
?line ?t:fail({length(BadArity),bad_arity})
end.
-types_3(List) ->
+erl_bif_types_3(List) ->
BadSmokeTest = [MFA || {M,F,A}=MFA <- List,
begin
try erl_bif_types:type(M, F, A) of
@@ -151,9 +142,220 @@ types_3(List) ->
?line ?t:fail({length(BadSmokeTest),bad_smoke_test})
end.
+guard_bifs_in_erl_bif_types(_Config) ->
+ ensure_erl_bif_types_compiled(),
+
+ List0 = erlang:system_info(snifs),
+ List = [{F,A} || {erlang,F,A} <- List0,
+ erl_internal:guard_bif(F, A)],
+ Not = [FA || {F,A}=FA <- List,
+ not erl_bif_types:is_known(erlang, F, A)],
+ case Not of
+ [] ->
+ ok;
+ [_|_] ->
+ io:put_chars(
+ ["Dialyzer requires that all guard BIFs "
+ "have type information in erl_bif_types.\n\n"
+ "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()
+ end.
+
+shadow_comments(_Config) ->
+ ensure_erl_bif_types_compiled(),
+
+ List0 = erlang:system_info(snifs),
+ List1 = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs],
+ List = [MFA || MFA <- List1, not is_operator(MFA)],
+ HasTypes = [MFA || {M,F,A}=MFA <- List,
+ erl_bif_types:is_known(M, F, A)],
+ Path = get_code_path(),
+ BifRel = sofs:relation(HasTypes, [{m,f,a}]),
+ BifModules = sofs:to_external(sofs:projection(1, BifRel)),
+ AbstrByModule = [extract_abstract(Mod, Path) || Mod <- BifModules],
+ Specs0 = [extract_specs(Mod, Abstr) ||
+ {Mod,Abstr} <- AbstrByModule],
+ Specs = lists:append(Specs0),
+ SpecFuns0 = [F || {F,_} <- Specs],
+ SpecFuns = sofs:relation(SpecFuns0, [{m,f,a}]),
+ HasTypesAndSpecs = sofs:intersection(BifRel, SpecFuns),
+ Commented0 = lists:append([extract_comments(Mod, Path) ||
+ Mod <- BifModules]),
+ Commented = sofs:relation(Commented0, [{m,f,a}]),
+ {NoComments0,_,NoBifSpecs0} =
+ sofs:symmetric_partition(HasTypesAndSpecs, Commented),
+ NoComments = sofs:to_external(NoComments0),
+ NoBifSpecs = sofs:to_external(NoBifSpecs0),
+
+ case NoComments of
+ [] ->
+ ok;
+ [_|_] ->
+ io:put_chars(
+ ["If a BIF stub has both a spec and has type information in "
+ "erl_bif_types, there *must*\n"
+ "be a comment in the source file to make that immediately "
+ "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()
+ end,
+
+ case NoBifSpecs of
+ [] ->
+ ok;
+ [_|_] ->
+ io:put_chars(
+ ["The following functions have \"shadowed\" comments "
+ "claiming that there is type information in erl_bif_types,\n"
+ "but actually there is no such type information.\n\n"
+ "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()
+ end.
+
+extract_comments(Mod, Path) ->
+ Beam = which(Mod, Path),
+ SrcDir = filename:join(filename:dirname(filename:dirname(Beam)), "src"),
+ Src = filename:join(SrcDir, atom_to_list(Mod) ++ ".erl"),
+ {ok,Bin} = file:read_file(Src),
+ Lines0 = binary:split(Bin, <<"\n">>, [global]),
+ Lines1 = [T || <<"%% Shadowed by erl_bif_types: ",T/binary>> <- Lines0],
+ {ok,ReMFA} = re:compile("([^:]*):([^/]*)/(\\d*)"),
+ Lines = [L || L <- Lines1, re:run(L, ReMFA, [{capture,[]}]) =:= match],
+ [begin
+ {match,[M,F,A]} = re:run(L, ReMFA, [{capture,all_but_first,list}]),
+ {list_to_atom(M),list_to_atom(F),list_to_integer(A)}
+ end || L <- Lines].
+
+ensure_erl_bif_types_compiled() ->
+ c:l(erl_bif_types),
+ case erlang:function_exported(erl_bif_types, module_info, 0) of
+ false ->
+ %% Fail cleanly.
+ ?t:fail("erl_bif_types not compiled");
+ true ->
+ ok
+ end.
+
known_types({M,F,A}) ->
erl_bif_types:is_known(M, F, A).
+specs(_) ->
+ List0 = erlang:system_info(snifs),
+
+ %% Ignore missing type information for hipe BIFs.
+ List1 = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs],
+
+ %% Ignore all operators.
+ List = [MFA || MFA <- List1, not is_operator(MFA)],
+
+ %% Extract specs from the abstract code for all BIFs.
+ Path = get_code_path(),
+ BifRel = sofs:relation(List, [{m,f,a}]),
+ BifModules = sofs:to_external(sofs:projection(1, BifRel)),
+ AbstrByModule = [extract_abstract(Mod, Path) || Mod <- BifModules],
+ Specs0 = [extract_specs(Mod, Abstr) ||
+ {Mod,Abstr} <- AbstrByModule],
+ Specs = lists:append(Specs0),
+ BifSet = sofs:set(List, [function]),
+ SpecRel0 = sofs:relation(Specs, [{function,spec}]),
+ SpecRel = sofs:restriction(SpecRel0, BifSet),
+
+ %% Find BIFs without specs.
+ NoSpecs0 = sofs:difference(BifSet, sofs:domain(SpecRel)),
+ NoSpecs = sofs:to_external(NoSpecs0),
+ case NoSpecs of
+ [] ->
+ ok;
+ [_|_] ->
+ io:put_chars("The following BIFs don't have specs:\n"),
+ [print_mfa(MFA) || MFA <- NoSpecs],
+ ?t:fail()
+ end.
+
+is_operator({erlang,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.
+
+extract_specs(M, Abstr) ->
+ [{make_mfa(M, Name),Spec} || {attribute,_,spec,{Name,Spec}} <- Abstr].
+
+make_mfa(M, {F,A}) -> {M,F,A};
+make_mfa(M, {M,_,_}=MFA) -> MFA.
+
+improper_bif_stubs(_) ->
+ Bifs0 = erlang:system_info(snifs),
+ Bifs = [MFA || {M,_,_}=MFA <- Bifs0, M =/= hipe_bifs],
+ Path = get_code_path(),
+ BifRel = sofs:relation(Bifs, [{m,f,a}]),
+ BifModules = sofs:to_external(sofs:projection(1, BifRel)),
+ AbstrByModule = [extract_abstract(Mod, Path) || Mod <- BifModules],
+ Funcs0 = [extract_functions(Mod, Abstr) ||
+ {Mod,Abstr} <- AbstrByModule],
+ Funcs = lists:append(Funcs0),
+ BifSet = sofs:set(Bifs, [function]),
+ FuncRel0 = sofs:relation(Funcs, [{function,code}]),
+ FuncRel = sofs:restriction(FuncRel0, BifSet),
+ [check_stub(MFA, Body) || {MFA,Body} <- sofs:to_external(FuncRel)],
+ ok.
+
+auto_imports(_Config) ->
+ Path = get_code_path(),
+ {erlang,Abstr} = extract_abstract(erlang, Path),
+ SpecFuns = [Name || {attribute,_,spec,{Name,_}} <- Abstr],
+ auto_imports(SpecFuns, 0).
+
+auto_imports([{F,A}|T], Errors) ->
+ case erl_internal:bif(F, A) of
+ false ->
+ io:format("~p/~p: not auto-imported, but spec claims it "
+ "is auto-imported", [F,A]),
+ auto_imports(T, Errors+1);
+ true ->
+ auto_imports(T, Errors)
+ end;
+auto_imports([{erlang,F,A}|T], Errors) ->
+ case erl_internal:bif(F, A) of
+ false ->
+ auto_imports(T, Errors);
+ true ->
+ io:format("~p/~p: auto-imported, but "
+ "spec claims it is *not* auto-imported", [F,A]),
+ auto_imports(T, Errors+1)
+ end;
+auto_imports([], 0) ->
+ ok;
+auto_imports([], Errors) ->
+ ?t:fail({Errors,inconsistencies}).
+
+extract_functions(M, Abstr) ->
+ [{{M,F,A},Body} || {function,_,F,A,Body} <- Abstr].
+
+check_stub({erlang,apply,3}, _) ->
+ ok;
+check_stub({_,F,A}, B) ->
+ try
+ [{clause,_,Args,[],Body}] = B,
+ A = length(Args),
+ [{call,_,{remote,_,{atom,_,erlang},{atom,_,nif_error}},[_]}] = Body
+ catch
+ _:_ ->
+ io:put_chars("Invalid body for the following BIF stub:\n"),
+ Func = {function,0,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()
+ 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),
@@ -483,6 +685,35 @@ erlang_halt(Config) when is_list(Config) ->
id(I) -> I.
+%% Get code path, including the path for the erts application.
+get_code_path() ->
+ case code:lib_dir(erts) of
+ {error,bad_name} ->
+ Erts = filename:join([code:root_dir(),"erts","preloaded","ebin"]),
+ [Erts|code:get_path()];
+ _ ->
+ code:get_path()
+ end.
+
+which(Mod, Path) ->
+ which_1(atom_to_list(Mod) ++ ".beam", Path).
+
+which_1(Base, [D|Ds]) ->
+ Path = filename:join(D, Base),
+ case filelib:is_regular(Path) of
+ true -> Path;
+ false -> which_1(Base, Ds)
+ end.
+print_mfa({M,F,A}) ->
+ io:format("~p:~p/~p", [M,F,A]).
+
+extract_abstract(Mod, Path) ->
+ Beam = which(Mod, Path),
+ {ok,{Mod,[{abstract_code,{raw_abstract_v1,Abstr}}]}} =
+ beam_lib:chunks(Beam, [abstract_code]),
+ {Mod,Abstr}.
+
+
hostname() ->
hostname(atom_to_list(node())).
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index c9f43bb00b..58e0cb4096 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl
index dea02b1f4b..8b5c82d968 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. 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/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl
index 9d80b01748..eaecd32f95 100644
--- a/erts/emulator/test/call_trace_SUITE.erl
+++ b/erts/emulator/test/call_trace_SUITE.erl
@@ -25,6 +25,7 @@
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.
@@ -46,6 +47,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
Common = [errors, on_load],
NotHipe = [process_specs, basic, flags, pam, change_pam,
+ upgrade,
return_trace, exception_trace, deep_exception,
exception_nocatch, bit_syntax],
Hipe = [hipe],
@@ -76,7 +78,13 @@ init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
end_per_testcase(_Func, Config) ->
Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
+ ?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.
+
+ c:l(?MODULE).
hipe(Config) when is_list(Config) ->
?line 0 = erlang:trace_pattern({?MODULE,worker_foo,1}, true),
@@ -185,8 +193,13 @@ basic() ->
%% Trace some functions...
?line 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),
@@ -267,6 +280,118 @@ foo() -> foo0.
foo(X) -> X+1.
foo(X, Y) -> X+Y.
+
+%% Note that the semantics that this test case verifies
+%% are not explicitly specified in the docs (what I could find in R15B).
+%% 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";
+upgrade(Config) when is_list(Config) ->
+ V1 = compile_version(my_upgrade_test, 1, Config),
+ V2 = compile_version(my_upgrade_test, 2, Config),
+ start_tracer(),
+ upgrade_do(V1, V2, false),
+ upgrade_do(V1, V2, true).
+
+upgrade_do(V1, V2, TraceLocalVersion) ->
+ {module,my_upgrade_test} = erlang:load_module(my_upgrade_test, V1),
+
+
+ %% Test that trace is cleared after load_module
+
+ trace_func({my_upgrade_test,'_','_'}, [], [global]),
+ case TraceLocalVersion of
+ true -> trace_func({my_upgrade_test,local_version,0}, [], [local]);
+ _ -> ok
+ end,
+ 1 = my_upgrade_test:version(),
+ 1 = my_upgrade_test:do_local(),
+ 1 = my_upgrade_test:do_real_local(),
+ put('F1_exp', my_upgrade_test:make_fun_exp()),
+ put('F1_loc', my_upgrade_test:make_fun_local()),
+ 1 = (get('F1_exp'))(),
+ 1 = (get('F1_loc'))(),
+
+ Self = self(),
+ expect({trace,Self,call,{my_upgrade_test,version,[]}}),
+ 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
+ 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
+ end,
+
+ {module,my_upgrade_test} = erlang:load_module(my_upgrade_test, V2),
+ 2 = my_upgrade_test:version(),
+ put('F2_exp', my_upgrade_test:make_fun_exp()),
+ put('F2_loc', my_upgrade_test:make_fun_local()),
+ 2 = (get('F1_exp'))(),
+ 1 = (get('F1_loc'))(),
+ 2 = (get('F2_exp'))(),
+ 2 = (get('F2_loc'))(),
+ expect(),
+
+ put('F1_exp', undefined),
+ put('F1_loc', undefined),
+ erlang:garbage_collect(),
+ erlang:purge_module(my_upgrade_test),
+
+ % Test that trace is cleared after delete_module
+
+ trace_func({my_upgrade_test,'_','_'}, [], [global]),
+ case TraceLocalVersion of
+ true -> trace_func({my_upgrade_test,local_version,0}, [], [local]);
+ _ -> ok
+ end,
+ 2 = my_upgrade_test:version(),
+ 2 = my_upgrade_test:do_local(),
+ 2 = my_upgrade_test:do_real_local(),
+ 2 = (get('F2_exp'))(),
+ 2 = (get('F2_loc'))(),
+
+ expect({trace,Self,call,{my_upgrade_test,version,[]}}),
+ 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
+ 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
+ end,
+
+ true = erlang:delete_module(my_upgrade_test),
+ {'EXIT',{undef,_}} = (catch my_upgrade_test:version()),
+ {'EXIT',{undef,_}} = (catch ((get('F2_exp'))())),
+ 2 = (get('F2_loc'))(),
+ expect(),
+
+ put('F2_exp', undefined),
+ put('F2_loc', undefined),
+ erlang:garbage_collect(),
+ erlang:purge_module(my_upgrade_test),
+ ok.
+
+compile_version(Module, Version, Config) ->
+ Data = ?config(data_dir, Config),
+ File = filename:join(Data, atom_to_list(Module)),
+ {ok,Module,Bin} = compile:file(File, [{d,'VERSION',Version},
+ binary,report]),
+ Bin.
+
+
+
%% Test flags (arity, timestamp) for call_trace/3.
%% Also, test the '{tracer,Pid}' option.
flags(_Config) ->
@@ -1151,11 +1276,13 @@ trace_info(What, Key) ->
Res.
trace_func(MFA, MatchSpec) ->
- get(tracer) ! {apply,self(),{erlang,trace_pattern,[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,
- ok = io:format("trace_pattern(~p, ~p) -> ~p", [MFA,MatchSpec,Res]),
+ ok = io:format("trace_pattern(~p, ~p, ~p) -> ~p", [MFA,MatchSpec,Flags,Res]),
Res.
trace_pid(Pid, On, Flags) ->
diff --git a/erts/emulator/test/call_trace_SUITE_data/my_upgrade_test.erl b/erts/emulator/test/call_trace_SUITE_data/my_upgrade_test.erl
new file mode 100644
index 0000000000..11b8a95209
--- /dev/null
+++ b/erts/emulator/test/call_trace_SUITE_data/my_upgrade_test.erl
@@ -0,0 +1,26 @@
+-module(my_upgrade_test).
+
+-export([version/0]).
+-export([do_local/0]).
+-export([do_real_local/0]).
+-export([make_fun_exp/0]).
+-export([make_fun_local/0]).
+
+
+version() ->
+ ?VERSION.
+
+do_local() ->
+ version().
+
+do_real_local() ->
+ local_version().
+
+local_version() ->
+ ?VERSION.
+
+make_fun_exp() ->
+ fun() -> ?MODULE:version() end.
+
+make_fun_local() ->
+ fun() -> local_version() end.
diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl
index 74ce5e397a..b0408cabe1 100644
--- a/erts/emulator/test/code_SUITE.erl
+++ b/erts/emulator/test/code_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. 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/code_parallel_load_SUITE.erl b/erts/emulator/test/code_parallel_load_SUITE.erl
new file mode 100644
index 0000000000..aa9e4c96c6
--- /dev/null
+++ b/erts/emulator/test/code_parallel_load_SUITE.erl
@@ -0,0 +1,198 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%% Author: Björn-Egil Dahlberg
+
+-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
+ ]).
+
+-define(model, code_parallel_load_SUITE_model).
+-define(interval, 50).
+-define(number_of_processes, 160).
+-define(passes, 4).
+
+
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ multiple_load_check_purge_repeat,
+ many_load_distributed_only_once
+ ].
+
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+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) ->
+ SConf = ?config(save_config, Config),
+ Pids = proplists:get_value(purge_pids, SConf),
+
+ case check_old_code(?model) of
+ true -> check_and_purge_processes_code(Pids, ?model);
+ _ -> ok
+ end,
+ case erlang:delete_module(?model) of
+ true -> check_and_purge_processes_code(Pids, ?model);
+ _ -> ok
+ end,
+ Dog=?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog).
+
+
+multiple_load_check_purge_repeat(_Conf) ->
+ Ts = [v1,v2,v3,v4,v5,v6],
+
+ %% generate code that receives a token, code switches to new code
+ %% then matches this token against a literal code token
+ %% should be identical
+ %% (smoke test for parallel code loading
+ Codes = [{T, generate(?model, [], [
+ format("check(T) -> receive {_Pid, change, T1} -> "
+ " ~w:check(T1)\n"
+ " after 0 -> T = f(), check(T) end.\n", [?model]),
+ format("f() -> ~w.~n", [T])
+ ])} || T <- Ts],
+
+ Pids = setup_code_changer(Codes),
+ {save_config, [{purge_pids,Pids}]}.
+
+setup_code_changer([{Token,Code}|Cs] = Codes) ->
+ {module, ?model} = erlang:load_module(?model,Code),
+ Pids = setup_checkers(Token,?number_of_processes),
+ code_changer(Cs, Codes, ?interval,Pids,?passes),
+ Pids.
+
+code_changer(_, _, _, Pids, 0) ->
+ [unlink(Pid) || Pid <- Pids],
+ [exit(Pid, die) || Pid <- Pids],
+ io:format("done~n"),
+ ok;
+code_changer([], Codes, T, Pids, Ps) ->
+ code_changer(Codes, Codes, T, Pids, Ps - 1);
+code_changer([{Token,Code}|Cs], Codes, T, Pids, Ps) ->
+ receive after T ->
+ io:format("load code with token ~4w : pass ~4w~n", [Token, Ps]),
+ {module, ?model} = erlang:load_module(?model, Code),
+ % this is second time we call load_module for this module
+ % so it should have old code
+ [Pid ! {self(), change, Token} || Pid <- Pids],
+ % should we wait a moment or just blantantly try to check and purge repeatadly?
+ receive after 1 -> ok end,
+ ok = check_and_purge_processes_code(Pids, ?model),
+ code_changer(Cs, Codes, T, Pids, Ps)
+ end.
+
+
+
+many_load_distributed_only_once(_Conf) ->
+ Ts = [<<"first version">>, <<"second version">>],
+
+ [{Token1,Code1},{Token2, Code2}] = [{T, generate(?model, [], [
+ "check({<<\"second version\">> = V, Pid}) -> V = f(), Pid ! {self(), completed, V}, ok;\n" ++
+ format("check(T) -> receive {Pid, change, T1, B} -> "
+ " Res = erlang:load_module(~w, B), Pid ! {self(), change, Res},\n"
+ " ~w:check({T1, Pid})\n"
+ " after 0 -> T = f(), check(T) end.\n", [?model, ?model]),
+ format("f() -> ~w.~n", [T])
+ ])} || T <- Ts],
+
+
+ {module, ?model} = erlang:load_module(?model, Code1),
+ Pids = setup_checkers(Token1,?number_of_processes),
+
+ receive after 1000 -> ok end, % give 'em some time to spin up
+ [Pid ! {self(), change, Token2, Code2} || Pid <- Pids],
+ Loads = [receive {Pid, change, Res} -> Res end || Pid <- Pids],
+ [receive {Pid, completed, Token2} -> ok end || Pid <- Pids],
+
+ ok = ensure_only_one_load(Loads, 0),
+ {save_config, [{purge_pids,Pids}]}.
+
+ensure_only_one_load([], 1) -> ok;
+ensure_only_one_load([], _) -> too_many_loads;
+ensure_only_one_load([{module, ?model}|Loads], N) ->
+ ensure_only_one_load(Loads, N + 1);
+ensure_only_one_load([{error, not_purged}|Loads], N) ->
+ ensure_only_one_load(Loads, N).
+% no other return values are allowed from load_module
+
+
+%% aux
+
+setup_checkers(_,0) -> [];
+setup_checkers(T,N) -> [spawn_link(fun() -> ?model:check(T) end) | setup_checkers(T, N-1)].
+
+check_and_purge_processes_code(Pids, M) ->
+ check_and_purge_processes_code(Pids, M, []).
+check_and_purge_processes_code([], M, []) ->
+ erlang:purge_module(M),
+ ok;
+check_and_purge_processes_code([], M, Pending) ->
+ io:format("Processes ~w are still executing old code - retrying.~n", [Pending]),
+ check_and_purge_processes_code(Pending, M, []);
+check_and_purge_processes_code([Pid|Pids], M, Pending) ->
+ case erlang:check_process_code(Pid, M) of
+ false ->
+ check_and_purge_processes_code(Pids, M, Pending);
+ true ->
+ check_and_purge_processes_code(Pids, M, [Pid|Pending])
+ end.
+
+
+generate(Module, Attributes, FunStrings) ->
+ FunForms = function_forms(FunStrings),
+ Forms = [
+ {attribute,1,module,Module},
+ {attribute,2,export,[FA || {FA,_} <- FunForms]}
+ ] ++ [{attribute, 3, A, V}|| {A, V} <- Attributes] ++
+ [ 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)].
+
+format(F,Ts) -> lists:flatten(io_lib:format(F, Ts)).
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index 2ddb4d76d9..f3a177faf2 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -98,19 +98,6 @@ end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
Dog=?config(watchdog, Config),
?t:timetrap_cancel(Dog).
-%%% Don't be too hard on vxworks, the cross server gets nodedown
-%%% cause the card is too busy if we don't sleep a little between pings.
-sleep() ->
- case os:type() of
- vxworks ->
- receive
- after 10 ->
- ok
- end;
- _ ->
- ok
- end.
-
ping(doc) ->
["Tests pinging a node in different ways."];
ping(Config) when is_list(Config) ->
@@ -122,23 +109,21 @@ ping(Config) when is_list(Config) ->
?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),
- sleep()
+ ?line 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),sleep() end),
+ ?line test_server:do_times(Times, fun() -> pong = net_adm:ping(OtherNode) end),
?line 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),sleep() end),
+ ?line test_server:do_times(Times, fun() -> pong = net_adm:ping(Node) end),
ok.
diff --git a/erts/emulator/test/driver_SUITE_data/chkio_drv.c b/erts/emulator/test/driver_SUITE_data/chkio_drv.c
index 40f1ad4fea..faf1040276 100644
--- a/erts/emulator/test/driver_SUITE_data/chkio_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/chkio_drv.c
@@ -17,7 +17,7 @@
*/
#ifndef UNIX
-#if !defined(__WIN32__) && !defined(VXWORKS)
+#if !defined(__WIN32__)
#define UNIX 1
#endif
#endif
diff --git a/erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c b/erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c
index e6a3edcd74..1e107309df 100644
--- a/erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c
@@ -17,7 +17,7 @@
*/
#ifndef UNIX
-#if !defined(__WIN32__) && !defined(VXWORKS)
+#if !defined(__WIN32__)
#define UNIX 1
#endif
#endif
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 b2cc1e785a..d174771629 100644
--- a/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c
@@ -29,7 +29,7 @@
*/
#ifndef UNIX
-#if !defined(__WIN32__) && !defined(VXWORKS)
+#if !defined(__WIN32__)
#define UNIX 1
#endif
#endif
diff --git a/erts/emulator/test/driver_SUITE_data/missing_callback_drv.c b/erts/emulator/test/driver_SUITE_data/missing_callback_drv.c
index e7d9a294fa..851f2c745b 100644
--- a/erts/emulator/test/driver_SUITE_data/missing_callback_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/missing_callback_drv.c
@@ -17,7 +17,7 @@
*/
#ifndef UNIX
-#if !defined(__WIN32__) && !defined(VXWORKS)
+#if !defined(__WIN32__)
#define UNIX 1
#endif
#endif
diff --git a/erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c b/erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c
index 8e203f74ec..0c86a26604 100644
--- a/erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c
@@ -28,7 +28,7 @@
*/
#ifndef UNIX
-#if !defined(__WIN32__) && !defined(VXWORKS)
+#if !defined(__WIN32__)
#define UNIX 1
#endif
#endif
diff --git a/erts/emulator/test/driver_SUITE_data/timer_drv.c b/erts/emulator/test/driver_SUITE_data/timer_drv.c
index 8c3f203a64..57538e0d57 100644
--- a/erts/emulator/test/driver_SUITE_data/timer_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/timer_drv.c
@@ -1,11 +1,3 @@
-#ifdef VXWORKS
-#include <vxWorks.h>
-#include <taskVarLib.h>
-#include <taskLib.h>
-#include <sysLib.h>
-#include <string.h>
-#include <ioLib.h>
-#endif
#include <stdio.h>
#include "erl_driver.h"
@@ -84,12 +76,8 @@ static void timer_read(ErlDrvData p, char *buf, ErlDrvSizeT len)
driver_output(port, reply, 1);
} else if (buf[0] == DELAY_START_TIMER) {
#ifndef __WIN32__
-#ifdef VXWORKS
- taskDelay(sysClkRateGet());
-#else
sleep(1);
#endif
-#endif
driver_set_timer(port, get_int32(buf + 1));
}
}
diff --git a/erts/emulator/test/emulator.spec.vxworks b/erts/emulator/test/emulator.spec.vxworks
deleted file mode 100644
index 55675bdc29..0000000000
--- a/erts/emulator/test/emulator.spec.vxworks
+++ /dev/null
@@ -1,26 +0,0 @@
-{topcase, {dir, "../emulator_test"}}.
-
-% Added since R11
-{skip,{distribution_SUITE,link_to_dead_new_node,"Does not work in distributed test environments"}}.
-{skip,{binary_SUITE,terms_float,"Floats, VxWorks, PPC = Floating points never equal..."}}.
-{skip,{system_info_SUITE,process_count,"Fix-allocs starving VxWorks cards"}}.
-{skip,{monitor_SUITE,mixer,"Fix-allocs starving VxWorks cards"}}.
-
-{skip,{node_container_SUITE,"Too memory consuming..."}}.
-
-{skip,{trace_SUITE,system_monitor_long_gc_1,"Too memory consuming..."}}.
-{skip,{trace_SUITE,system_monitor_long_gc_2,"Too memory consuming..."}}.
-{skip,{trace_SUITE,system_monitor_large_heap_1,"Too memory consuming..."}}.
-{skip,{trace_SUITE,system_monitor_large_heap_2,"Too memory consuming..."}}.
-% End added since R11
-
-{skip, {distribution_SUITE,stop_dist,"Not written to work on VxWorks."}}.
-{skip, {distribution_SUITE,dist_auto_connect_never,
- "Not written to work on VxWorks."}}.
-{skip, {distribution_SUITE,dist_auto_connect_once,
- "Not written to work on VxWorks."}}.
-{skip, {trace_SUITE,system_monitor_long_gc,
- "Too memory consuming for VxWorks cards."}}.
-{skip, {trace_meta_SUITE,stack_grow,
- "Too memory consuming for VxWorks cards."}}.
-{skip, {obsolete_SUITE, "Not on vxworks"}}.
diff --git a/erts/emulator/test/emulator_bench.spec b/erts/emulator/test/emulator_bench.spec
new file mode 100644
index 0000000000..f709d913b7
--- /dev/null
+++ b/erts/emulator/test/emulator_bench.spec
@@ -0,0 +1 @@
+{groups,"../emulator_test",estone_SUITE,[estone_bench]}.
diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl
index 2417d4bcfe..21834bfa62 100644
--- a/erts/emulator/test/estone_SUITE.erl
+++ b/erts/emulator/test/estone_SUITE.erl
@@ -19,7 +19,7 @@
-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]).
+ init_per_group/2,end_per_group/2,estone/1,estone_bench/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
%% Internal exports for EStone tests
@@ -46,6 +46,7 @@
-include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct_event.hrl").
%% Test suite defines
-define(default_timeout, ?t:minutes(10)).
@@ -80,7 +81,7 @@ all() ->
[estone].
groups() ->
- [].
+ [{estone_bench, [{repeat,50}],[estone_bench]}].
init_per_suite(Config) ->
Config.
@@ -108,6 +109,17 @@ estone(Config) when is_list(Config) ->
?line {comment,Mhz ++ " MHz, " ++
integer_to_list(Stones) ++ " ESTONES"}.
+estone_bench(Config) ->
+ DataDir = ?config(data_dir,Config),
+ L = ?MODULE:macro(?MODULE:micros(),DataDir),
+ [ct_event:notify(
+ #event{name = benchmark_data,
+ data = [{name,proplists:get_value(title,Mark)},
+ {value,proplists:get_value(estones,Mark)}]})
+ || Mark <- L],
+ L.
+
+
%%
%% Calculate CPU speed
%%
diff --git a/erts/emulator/test/estone_SUITE_data/estone_cat.c b/erts/emulator/test/estone_SUITE_data/estone_cat.c
index 8ed9f8375b..a34bda4384 100644
--- a/erts/emulator/test/estone_SUITE_data/estone_cat.c
+++ b/erts/emulator/test/estone_SUITE_data/estone_cat.c
@@ -12,11 +12,7 @@
#include <fcntl.h>
#include <errno.h>
-#ifdef VXWORKS
-estone_cat(argc, argv)
-#else
main(argc, argv)
-#endif
int argc;
char *argv[];
{
diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl
index 8e6923ce9f..4a45afa9e9 100644
--- a/erts/emulator/test/float_SUITE.erl
+++ b/erts/emulator/test/float_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -25,7 +25,7 @@
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,cmp_zero/1, cmp_integer/1, cmp_bignum/1]).
+ bad_float_unpack/1, write/1, cmp_zero/1, cmp_integer/1, cmp_bignum/1]).
-export([otp_7178/1]).
-export([hidden_inf/1]).
@@ -42,7 +42,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[fpe, fp_drv, fp_drv_thread, otp_7178, denormalized,
- match, bad_float_unpack, {group, comparison}
+ match, bad_float_unpack, write, {group, comparison}
,hidden_inf
].
@@ -190,6 +190,11 @@ bad_float_unpack(Config) when is_list(Config) ->
bad_float_unpack_match(<<F:64/float>>) -> F;
bad_float_unpack_match(<<I:64/integer-signed>>) -> I.
+%% Exposes endianness issues.
+
+write(Config) when is_list(Config) ->
+ "1.0" = io_lib:write(1.0).
+
cmp_zero(_Config) ->
cmp(0.5e-323,0).
diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl
index 839ad6a4f4..ef06845cf2 100644
--- a/erts/emulator/test/fun_SUITE.erl
+++ b/erts/emulator/test/fun_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. 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/gc_SUITE.erl b/erts/emulator/test/gc_SUITE.erl
index 771d2c9a7a..19fa433a53 100644
--- a/erts/emulator/test/gc_SUITE.erl
+++ b/erts/emulator/test/gc_SUITE.erl
@@ -54,17 +54,12 @@ grow_heap(doc) -> ["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) ->
- ?line Dog=test_server:timetrap(test_server:minutes(40)),
- ?line ok=grow_heap1(256),
- case os:type() of
- vxworks ->
- stop_here;
- _ ->
- ?line ok=grow_heap1(512),
- ?line ok=grow_heap1(1024),
- ?line ok=grow_heap1(2048)
- end,
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server: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) ->
@@ -82,10 +77,10 @@ grow_heap1(List, MaxLen, CurLen, up) ->
grow_heap1([], _MaxLen, _, down) ->
ok;
grow_heap1([_|List], MaxLen, CurLen, down) ->
- ?line {_,_,C}=erlang:now(),
- ?line Num=C rem (length(List))+1,
- ?line Elem=lists:nth(Num, List),
- ?line NewList=lists:delete(Elem, List),
+ {_,_,C} = erlang:now(),
+ Num = C rem (length(List))+1,
+ Elem = lists:nth(Num, List),
+ NewList = lists:delete(Elem, List),
grow_heap1(NewList, MaxLen, CurLen-1, down).
@@ -93,16 +88,11 @@ grow_heap1([_|List], MaxLen, CurLen, down) ->
grow_stack(doc) -> ["Increase and decrease stack size, and ",
"drop off some garbage from time to time."];
grow_stack(Config) when is_list(Config) ->
- ?line Dog=test_server:timetrap(test_server:minutes(80)),
+ Dog = test_server:timetrap(test_server:minutes(80)),
show_heap("before:"),
- case os:type() of
- vxworks ->
- ?line grow_stack1(25, 0);
- _ ->
- ?line grow_stack1(200, 0)
- end,
+ grow_stack1(200, 0),
show_heap("after:"),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
grow_stack1(0, _) ->
@@ -123,16 +113,11 @@ grow_stack_heap(doc) -> ["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) ->
- case os:type() of
- vxworks ->
- {comment, "Takes too long to run on VxWorks/cpu32"};
- _ ->
- ?line Dog=test_server:timetrap(test_server:minutes(40)),
- ?line grow_stack_heap1(16),
- ?line grow_stack_heap1(32),
- ?line test_server:timetrap_cancel(Dog),
- ok
- end.
+ Dog = test_server:timetrap(test_server:minutes(40)),
+ grow_stack_heap1(16),
+ grow_stack_heap1(32),
+ test_server:timetrap_cancel(Dog),
+ ok.
grow_stack_heap1(MaxLen) ->
io:format("~ngrow_stack_heap with ~p items.",[MaxLen]),
@@ -151,10 +136,10 @@ grow_stack_heap1(List, MaxLen, CurLen, up) ->
grow_stack_heap1([], _MaxLen, _, down) -> ok;
grow_stack_heap1([_|List], MaxLen, CurLen, down) ->
grow_stack1(CurLen*2,0),
- ?line {_,_,C}=erlang:now(),
- ?line Num=C rem (length(List))+1,
- ?line Elem=lists:nth(Num, List),
- ?line NewList=lists:delete(Elem, List),
+ {_,_,C}=erlang:now(),
+ Num=C rem (length(List))+1,
+ Elem=lists:nth(Num, List),
+ NewList=lists:delete(Elem, List),
grow_stack_heap1(NewList, MaxLen, CurLen-1, down),
ok.
diff --git a/erts/emulator/test/hibernate_SUITE.erl b/erts/emulator/test/hibernate_SUITE.erl
index 68bc3434d4..31938e6c65 100644
--- a/erts/emulator/test/hibernate_SUITE.erl
+++ b/erts/emulator/test/hibernate_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. 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/mtx_SUITE_data/Makefile.src b/erts/emulator/test/mtx_SUITE_data/Makefile.src
index b6c843269c..37eb1daa72 100644
--- a/erts/emulator/test/mtx_SUITE_data/Makefile.src
+++ b/erts/emulator/test/mtx_SUITE_data/Makefile.src
@@ -27,4 +27,11 @@ LIBS = @ERTS_LIBS@
all: $(NIF_LIBS)
+mtx_SUITE.c: force_rebuild
+ touch mtx_SUITE.c
+
+force_rebuild:
+ echo "Force rebuild to compensate for emulator type dependencies"
+
+
@SHLIB_RULES@
diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl
index 0bf2c03233..3f9b339ed2 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2012. 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/nofrag_SUITE.erl b/erts/emulator/test/nofrag_SUITE.erl
index 6b6ac28e2e..71567ed0cb 100644
--- a/erts/emulator/test/nofrag_SUITE.erl
+++ b/erts/emulator/test/nofrag_SUITE.erl
@@ -26,7 +26,6 @@
init_per_testcase/2,end_per_testcase/2,
error_handler/1,error_handler_apply/1,
error_handler_fixed_apply/1,error_handler_fun/1,
- error_handler_tuple_fun/1,
debug_breakpoint/1]).
%% Exported functions for an error_handler module.
@@ -37,7 +36,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[error_handler, error_handler_apply,
error_handler_fixed_apply, error_handler_fun,
- error_handler_tuple_fun, debug_breakpoint].
+ debug_breakpoint].
groups() ->
[].
@@ -178,29 +177,6 @@ collect_fun(N, Fun) ->
undefined_lambda(foobarblurf, Fun, Args) when is_function(Fun) ->
Args.
-error_handler_tuple_fun(Config) when is_list(Config) ->
- ?line process_flag(error_handler, ?MODULE),
- ?line Term = collect_tuple_fun(1024, {?MODULE,very_undefined_function}),
- ?line Term = binary_to_term(term_to_binary(Term)),
- ?line 1024 = length(Term),
- ?line [[{foo,bar},42.0,[e,f,g]]] = lists:usort(Term),
- ok.
-
-collect_tuple_fun(0, _) ->
- [];
-collect_tuple_fun(N, Fun) ->
- %% The next line calls the error handle function, which is
- %% ?MODULE:undefined_function/3 (it simply returns the list
- %% of args).
- C = Fun({foo,id(bar)}, 42.0, [e,f,id(g)]),
-
- %% The variable C will be saved onto the stack frame; if C
- %% points into a heap fragment the garbage collector will reach
- %% it and the emulator will crash sooner or later (sooner if
- %% the emulator is debug-compiled).
- Res = collect_tuple_fun(N-1, Fun),
- [C|Res].
-
debug_breakpoint(Config) when is_list(Config) ->
?line process_flag(error_handler, ?MODULE),
?line erts_debug:breakpoint({?MODULE,foobar,5}, true),
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index 0b99b3438a..873601ddd1 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -157,16 +157,16 @@ win_massive(Config) when is_list(Config) ->
end.
do_win_massive() ->
- ?line Dog = test_server:timetrap(test_server:seconds(360)),
- ?line SuiteDir = filename:dirname(code:which(?MODULE)),
- ?line Env = " -env ERL_MAX_PORTS 8192",
- ?line {ok, Node} =
+ Dog = test_server:timetrap(test_server:seconds(360)),
+ SuiteDir = filename:dirname(code:which(?MODULE)),
+ Env = " -env ERL_MAX_PORTS 8192",
+ {ok, Node} =
test_server:start_node(win_massive,
slave,
[{args, " -pa " ++ SuiteDir ++ Env}]),
- ?line ok = rpc:call(Node,?MODULE,win_massive_client,[3000]),
- ?line test_server:stop_node(Node),
- ?line test_server:timetrap_cancel(Dog),
+ ok = rpc:call(Node,?MODULE,win_massive_client,[3000]),
+ test_server:stop_node(Node),
+ test_server:timetrap_cancel(Dog),
ok.
win_massive_client(N) ->
@@ -208,11 +208,11 @@ win_massive_loop(P,N) ->
%% We will send only a small amount of data, to avoid deadlock.
stream_small(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line stream_ping(Config, 512, "", []),
- ?line stream_ping(Config, 1777, "", []),
- ?line stream_ping(Config, 1777, "-s512", []),
- ?line test_server:timetrap_cancel(Dog),
+ 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).
@@ -220,30 +220,22 @@ stream_small(Config) when is_list(Config) ->
%% non-blocking reads and writes.
stream_big(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(180)),
- case os:type() of
- vxworks ->
- %% Don't stress VxWorks too much
- ?line stream_ping(Config, 43755, "", []),
- ?line stream_ping(Config, 51255, "", []),
- ?line stream_ping(Config, 52345, " -s40000", []);
- _ ->
- ?line stream_ping(Config, 43755, "", []),
- ?line stream_ping(Config, 100000, "", []),
- ?line stream_ping(Config, 77777, " -s40000", [])
- end,
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server: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) ->
- ?line Dog = test_server:timetrap(test_server:seconds(120)),
- ?line ping(Config, sizes(1), 1, "", []),
- ?line ping(Config, sizes(2), 2, "", []),
- ?line ping(Config, sizes(4), 4, "", []),
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(120)),
+ 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
@@ -251,30 +243,29 @@ basic_ping(Config) when is_list(Config) ->
%% small chunks rather than all at once.
slow_writes(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(20)),
- ?line ping(Config, [8], 4, "-s1", []),
- ?line ping(Config, [10], 2, "-s2", []),
- ?line test_server:timetrap_cancel(Dog),
+ 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."];
bad_packet(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line PortTest = port_test(Config),
- ?line process_flag(trap_exit, true),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ PortTest = port_test(Config),
+ process_flag(trap_exit, true),
- ?line bad_packet(PortTest, 1, 256),
- ?line bad_packet(PortTest, 1, 257),
- ?line bad_packet(PortTest, 2, 65536),
- ?line bad_packet(PortTest, 2, 65537),
+ bad_packet(PortTest, 1, 256),
+ bad_packet(PortTest, 1, 257),
+ bad_packet(PortTest, 2, 65536),
+ bad_packet(PortTest, 2, 65537),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
bad_packet(PortTest, HeaderSize, PacketSize) ->
- %% Intentionally no ?line macros.
P = open_port({spawn, PortTest}, [{packet, HeaderSize}]),
P ! {self(), {command, make_zero_packet(PacketSize)}},
receive
@@ -292,16 +283,16 @@ make_zero_packet(N) ->
%% Test sending bad messages to a port.
bad_port_messages(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line PortTest = port_test(Config),
- ?line process_flag(trap_exit, true),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ PortTest = port_test(Config),
+ process_flag(trap_exit, true),
- ?line bad_message(PortTest, {a,b}),
- ?line bad_message(PortTest, {a}),
- ?line bad_message(PortTest, {self(),{command,bad_command}}),
- ?line bad_message(PortTest, {self(),{connect,no_pid}}),
+ bad_message(PortTest, {a,b}),
+ bad_message(PortTest, {a}),
+ bad_message(PortTest, {self(),{command,bad_command}}),
+ bad_message(PortTest, {self(),{connect,no_pid}}),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
bad_message(PortTest, Message) ->
@@ -319,108 +310,97 @@ bad_message(PortTest, Message) ->
%% Tests the 'binary' option for a port.
t_binary(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(300)),
+ Dog = test_server:timetrap(test_server:seconds(300)),
%% Packet mode.
- ?line ping(Config, sizes(1), 1, "", [binary]),
- ?line ping(Config, sizes(2), 2, "", [binary]),
- ?line ping(Config, sizes(4), 4, "", [binary]),
+ ping(Config, sizes(1), 1, "", [binary]),
+ ping(Config, sizes(2), 2, "", [binary]),
+ ping(Config, sizes(4), 4, "", [binary]),
%% Stream mode.
- case os:type() of
- vxworks ->
- %% don't stress VxWorks too much
- ?line stream_ping(Config, 435, "", [binary]),
- ?line stream_ping(Config, 43755, "", [binary]),
- ?line stream_ping(Config, 50000, "", [binary]);
- _ ->
- ?line stream_ping(Config, 435, "", [binary]),
- ?line stream_ping(Config, 43755, "", [binary]),
- ?line stream_ping(Config, 100000, "", [binary])
- end,
+ stream_ping(Config, 435, "", [binary]),
+ stream_ping(Config, 43755, "", [binary]),
+ stream_ping(Config, 100000, "", [binary]),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
name1(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(100)),
- ?line PortTest = port_test(Config),
- ?line Command = lists:concat([PortTest, " "]),
- ?line P = open_port({spawn, Command}, []),
- ?line register(myport, P),
- ?line P = whereis(myport),
+ Dog = test_server:timetrap(test_server:seconds(100)),
+ PortTest = port_test(Config),
+ Command = lists:concat([PortTest, " "]),
+ P = open_port({spawn, Command}, []),
+ register(myport, P),
+ P = whereis(myport),
Text = "hej",
- ?line myport ! {self(), {command, Text}},
- ?line receive
- {P, {data, Text}} ->
- ok
- end,
- ?line myport ! {self(), close},
- ?line receive
- {P, closed} -> ok
- end,
- ?line undefined = whereis(myport),
- ?line test_server:timetrap_cancel(Dog),
+ myport ! {self(), {command, Text}},
+ receive
+ {P, {data, Text}} ->
+ ok
+ end,
+ myport ! {self(), close},
+ receive
+ {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) ->
- ?line Dog = test_server:timetrap(test_server:seconds(100)),
- ?line PortTest = port_test(Config),
- ?line Command = lists:concat([PortTest, " -h0 -q"]),
- ?line P = open_port({spawn, Command}, [eof]),
- ?line receive
- {P, eof} ->
- ok
- end,
- ?line P ! {self(), close},
- ?line receive
- {P, closed} -> ok
- end,
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(100)),
+ PortTest = port_test(Config),
+ Command = lists:concat([PortTest, " -h0 -q"]),
+ P = open_port({spawn, Command}, [eof]),
+ receive
+ {P, eof} ->
+ ok
+ end,
+ P ! {self(), close},
+ 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) ->
- ?line Dog = test_server:timetrap(test_server:seconds(300)),
- ?line expect_input(Config, [0, 1, 10, 13, 127, 128, 255], 1, "", [in]),
- ?line expect_input(Config, [0, 1, 255, 2048], 2, "", [in]),
- ?line expect_input(Config, [0, 1, 255, 2048], 4, "", [in]),
- ?line expect_input(Config, [0, 1, 10, 13, 127, 128, 255],
- 1, "", [in, binary]),
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server: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) ->
- ?line Dog = test_server:timetrap(test_server:seconds(100)),
- ?line Dir = ?config(priv_dir, Config),
- ?line Filename = filename:join(Dir, "output_only_stream"),
- ?line output_and_verify(Config, Filename, "-h0",
- random_packet(35777, "echo")),
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(100)),
+ Dir = ?config(priv_dir, Config),
+ Filename = filename:join(Dir, "output_only_stream"),
+ output_and_verify(Config, Filename, "-h0",
+ random_packet(35777, "echo")),
+ test_server:timetrap_cancel(Dog),
ok.
output_and_verify(Config, Filename, Options, Data) ->
- ?line PortTest = port_test(Config),
- ?line Command = lists:concat([PortTest, " ",
- Options, " -o", Filename]),
- ?line Port = open_port({spawn, Command}, [out]),
- ?line Port ! {self(), {command, Data}},
- ?line Port ! {self(), close},
- ?line receive
- {Port, closed} -> ok
- end,
- Wait_time = case os:type() of
- vxworks -> 5000;
- _ -> 500
- end,
- ?line test_server:sleep(Wait_time),
- ?line {ok, Written} = file:read_file(Filename),
- ?line Data = binary_to_list(Written),
+ PortTest = port_test(Config),
+ Command = lists:concat([PortTest, " ",
+ Options, " -o", Filename]),
+ 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.
%% Test that receiving several packages written in the same
@@ -430,19 +410,11 @@ 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) ->
- ?line Dog = test_server:timetrap(test_server:seconds(600)),
- case os:type() of
- vxworks ->
- %% don't stress vxworks too much
- ?line expect_input(Config, [0, 1, 255, 10, 13], 1, "", []),
- ?line expect_input(Config, [0, 10, 13, 1600, 8191, 16383], 2, "", []),
- ?line expect_input(Config, [10, 35000], 4, "", []);
- _ ->
- ?line expect_input(Config, [0, 1, 255, 10, 13], 1, "", []),
- ?line expect_input(Config, [0, 10, 13, 1600, 32767, 65535], 2, "", []),
- ?line expect_input(Config, [10, 70000], 4, "", [])
- end,
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(600)),
+ 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
@@ -451,9 +423,9 @@ mul_basic(Config) when is_list(Config) ->
%% delays in between.)
mul_slow_writes(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(250)),
- ?line expect_input(Config, [0, 20, 255, 10, 1], 1, "-s64", []),
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(250)),
+ 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
@@ -461,27 +433,27 @@ mul_slow_writes(Config) when is_list(Config) ->
%% should also finish in about 5 seconds.
parallell(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(300)),
- ?line Testers =
- [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() -> 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],
- ?line process_flag(trap_exit, true),
- ?line Pids = lists:map(fun fun_spawn/1, Testers),
- ?line wait_for(Pids),
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(300)),
+ Testers = [
+ 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() -> 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],
+ process_flag(trap_exit, true),
+ Pids = lists:map(fun fun_spawn/1, Testers),
+ wait_for(Pids),
+ test_server:timetrap_cancel(Dog),
ok.
wait_for([]) ->
@@ -500,29 +472,29 @@ wait_for(Pids) ->
dying_port(suite) -> [];
dying_port(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(150)),
- ?line process_flag(trap_exit, true),
+ Dog = test_server:timetrap(test_server:seconds(150)),
+ process_flag(trap_exit, true),
- ?line P1 = make_dying_port(Config),
- ?line P2 = make_dying_port(Config),
- ?line P3 = make_dying_port(Config),
- ?line P4 = make_dying_port(Config),
- ?line P5 = make_dying_port(Config),
+ P1 = make_dying_port(Config),
+ P2 = make_dying_port(Config),
+ P3 = make_dying_port(Config),
+ P4 = make_dying_port(Config),
+ P5 = make_dying_port(Config),
%% This should be big enough to be sure to block in the write.
- ?line Garbage = random_packet(16384),
+ Garbage = random_packet(16384),
- ?line P1 ! {self(), {command, Garbage}},
- ?line P3 ! {self(), {command, Garbage}},
- ?line P5 ! {self(), {command, Garbage}},
+ P1 ! {self(), {command, Garbage}},
+ P3 ! {self(), {command, Garbage}},
+ P5 ! {self(), {command, Garbage}},
- ?line wait_for_port_exit(P1),
- ?line wait_for_port_exit(P2),
- ?line wait_for_port_exit(P3),
- ?line wait_for_port_exit(P4),
- ?line wait_for_port_exit(P5),
+ wait_for_port_exit(P1),
+ wait_for_port_exit(P2),
+ wait_for_port_exit(P3),
+ wait_for_port_exit(P4),
+ wait_for_port_exit(P5),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
wait_for_port_exit(Port) ->
@@ -549,9 +521,9 @@ make_dying_port(Config) when is_list(Config) ->
port_program_with_path(suite) -> [];
port_program_with_path(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(100)),
- ?line DataDir = ?config(data_dir, Config),
- ?line PrivDir = ?config(priv_dir, Config),
+ Dog = test_server:timetrap(test_server:seconds(100)),
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(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'.
@@ -560,36 +532,31 @@ port_program_with_path(Config) when is_list(Config) ->
%% (On Unix, there will be a single file created, which will be
%% a copy of the port program.)
- ?line PortTest = os:find_executable("port_test", DataDir),
+ PortTest = os:find_executable("port_test", DataDir),
io:format("os:find_executable(~p, ~p) returned ~p",
["port_test", DataDir, PortTest]),
- ?line {ok, PortTestPgm} = file:read_file(PortTest),
- ?line NewName = filename:join(PrivDir, filename:basename(PortTest)),
- ?line RedHerring = filename:rootname(NewName),
- ?line ok = file:write_file(RedHerring, "I'm just here to confuse.\n"),
- ?line ok = file:write_file(NewName, PortTestPgm),
- ?line ok = file:write_file_info(NewName, #file_info{mode=8#111}),
- ?line PgmWithPathAndNoExt = filename:rootname(NewName),
+ {ok, PortTestPgm} = file:read_file(PortTest),
+ NewName = filename:join(PrivDir, filename:basename(PortTest)),
+ RedHerring = filename:rootname(NewName),
+ ok = file:write_file(RedHerring, "I'm just here to confuse.\n"),
+ 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.
- ?line Command = lists:concat([PgmWithPathAndNoExt, " -h2"]),
- %% allow VxWorks time to write file
- case os:type() of
- vxworks -> test_server:sleep(2500);
- _ -> time
+ Command = lists:concat([PgmWithPathAndNoExt, " -h2"]),
+ P = open_port({spawn, Command}, [{packet, 2}]),
+ Message = "echo back to me",
+ P ! {self(), {command, Message}},
+ receive
+ {P, {data, Message}} ->
+ ok
end,
- ?line P = open_port({spawn, Command}, [{packet, 2}]),
- ?line Message = "echo back to me",
- ?line P ! {self(), {command, Message}},
- ?line receive
- {P, {data, Message}} ->
- ok
- end,
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
@@ -597,56 +564,46 @@ port_program_with_path(Config) when is_list(Config) ->
%% This used to fail on Windows.
open_input_file_port(suite) -> [];
open_input_file_port(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line PrivDir = ?config(priv_dir, Config),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ PrivDir = ?config(priv_dir, Config),
%% Create a file with the file driver and read it back using
%% open_port/2.
- ?line MyFile1 = filename:join(PrivDir, "my_input_file"),
- ?line FileData1 = "An input file",
- ?line ok = file:write_file(MyFile1, FileData1),
- case os:type() of
- vxworks ->
- %% Can't open input file with vanilla driver on VxWorks
- ?line process_flag(trap_exit, true),
- ?line case catch open_port(MyFile1, [in]) of
- {'EXIT', {badarg, _}} ->
- ok
- end;
- _ ->
- ?line case open_port(MyFile1, [in]) of
- InputPort when is_port(InputPort) ->
- ?line receive
- {InputPort, {data, FileData1}} ->
- ok
- end
- end
+ MyFile1 = filename:join(PrivDir, "my_input_file"),
+ 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
end,
- ?line test_server:timetrap_cancel(Dog),
+ 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) ->
- ?line Dog = test_server:timetrap(test_server:seconds(100)),
- ?line PrivDir = ?config(priv_dir, Config),
+ Dog = test_server:timetrap(test_server:seconds(100)),
+ PrivDir = ?config(priv_dir, Config),
%% Create a file with open_port/2 and read it back with
%% the file driver.
- ?line MyFile2 = filename:join(PrivDir, "my_output_file"),
- ?line FileData2_0 = "A file created ",
- ?line FileData2_1 = "with open_port/2.\n",
- ?line FileData2 = FileData2_0 ++ FileData2_1,
- ?line OutputPort = open_port(MyFile2, [out]),
- ?line OutputPort ! {self(), {command, FileData2_0}},
- ?line OutputPort ! {self(), {command, FileData2_1}},
- ?line OutputPort ! {self(), close},
- ?line {ok, Bin} = file:read_file(MyFile2),
- ?line FileData2 = binary_to_list(Bin),
-
- ?line test_server:timetrap_cancel(Dog),
+ MyFile2 = filename:join(PrivDir, "my_output_file"),
+ FileData2_0 = "A file created ",
+ FileData2_1 = "with open_port/2.\n",
+ FileData2 = FileData2_0 ++ FileData2_1,
+ OutputPort = open_port(MyFile2, [out]),
+ OutputPort ! {self(), {command, FileData2_0}},
+ OutputPort ! {self(), {command, FileData2_1}},
+ OutputPort ! {self(), close},
+ {ok, Bin} = file:read_file(MyFile2),
+ FileData2 = binary_to_list(Bin),
+
+ test_server:timetrap_cancel(Dog),
ok.
%%
@@ -670,17 +627,17 @@ iter_max_ports(Config) when is_list(Config) ->
iter_max_ports_test(Config) ->
- ?line Dog = test_server:timetrap(test_server:minutes(20)),
- ?line PortTest = port_test(Config),
- ?line Command = lists:concat([PortTest, " -h0 -q"]),
- ?line Iters = case os:type() of
+ Dog = test_server:timetrap(test_server:minutes(20)),
+ PortTest = port_test(Config),
+ Command = lists:concat([PortTest, " -h0 -q"]),
+ Iters = case os:type() of
{win32,_} -> 4;
_ -> 10
end,
- ?line L = do_iter_max_ports(Iters, Command),
+ L = do_iter_max_ports(Iters, Command),
io:format("Result: ~p",[L]),
- ?line all_equal(L),
- ?line test_server:timetrap_cancel(Dog),
+ 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 ->
@@ -695,9 +652,9 @@ all_equal([]) -> ok.
max_ports(Command) ->
test_server:sleep(500),
- ?line Ps = open_ports({spawn, Command}, [eof]),
- ?line N = length(Ps),
- ?line close_ports(Ps),
+ Ps = open_ports({spawn, Command}, [eof]),
+ N = length(Ps),
+ close_ports(Ps),
io:format("Got ~p ports\n",[N]),
N.
@@ -727,105 +684,105 @@ open_ports(Name, Settings) ->
enomem ->
[];
Other ->
- ?line test_server:fail({open_ports, Other})
+ test_server:fail({open_ports, Other})
end;
Other ->
- ?line test_server:fail({open_ports, Other})
+ test_server: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) ->
- ?line process_flag(trap_exit, true),
- ?line Pid = fun_spawn(fun suicide_port/1, [Config]),
- ?line receive
- {'EXIT', Pid, die} ->
- ok;
- Other ->
- test_server:fail({bad_message, Other})
- end.
+ 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})
+ end.
suicide_port(Config) when is_list(Config) ->
- ?line Port = port_expect(Config, [], 0, "", []),
- ?line exit(Port, die),
- ?line receive after infinity -> ok end.
+ Port = port_expect(Config, [], 0, "", []),
+ exit(Port, die),
+ receive after infinity -> ok end.
tps_16_bytes(doc) -> "";
tps_16_bytes(suite) -> [];
tps_16_bytes(Config) when is_list(Config) ->
- ?line tps(16, Config).
+ tps(16, Config).
tps_1K(doc) -> "";
tps_1K(suite) -> [];
tps_1K(Config) when is_list(Config) ->
- ?line tps(1024, Config).
+ tps(1024, Config).
tps(Size, Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(300)),
- ?line PortTest = port_test(Config),
- ?line Packet = list_to_binary(random_packet(Size, "e")),
- ?line Port = open_port({spawn, PortTest}, [binary, {packet, 2}]),
- ?line Transactions = 10000,
- ?line {Elapsed, ok} = test_server:timecall(?MODULE, tps,
+ Dog = test_server:timetrap(test_server:seconds(300)),
+ 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]),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
{comment, integer_to_list(trunc(Transactions/Elapsed+0.5)) ++ " transactions/s"}.
tps(_Port, _Packet, 0) -> ok;
tps(Port, Packet, N) ->
- ?line port_command(Port, Packet),
- ?line receive
- {Port, {data, Packet}} ->
- ?line tps(Port, Packet, N-1);
- Other ->
- ?line test_server:fail({bad_message, Other})
- end.
+ port_command(Port, Packet),
+ receive
+ {Port, {data, Packet}} ->
+ tps(Port, Packet, N-1);
+ Other ->
+ test_server:fail({bad_message, Other})
+ end.
%% Line I/O test
line(Config) when is_list(Config) ->
- ?line Siz = 110,
- ?line Dog = test_server:timetrap(test_server:seconds(300)),
- ?line Packet1 = random_packet(Siz),
- ?line Packet2 = random_packet(Siz div 2),
+ 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
- ?line port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2,
+ port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2,
io_lib:nl()]),
[{eol, Packet1}, {eol, Packet2}]}],
0, "", [{line,Siz}]),
%% Test the same for binaries
- ?line port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2,
+ port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2,
io_lib:nl()]),
[{eol, Packet1}, {eol, Packet2}]}],
0, "", [{line,Siz},binary]),
%% Test that too long lines get split
- ?line port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet1,
+ port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet1,
Packet2, io_lib:nl()]),
[{eol, Packet1}, {noeol, Packet1},
{eol, Packet2}]}], 0, "", [{line,Siz}]),
%% Test that last output from closing port program gets received.
- ?line L1 = lists:append([Packet1, io_lib:nl(), Packet2]),
- ?line S1 = lists:flatten(io_lib:format("-l~w", [length(L1)])),
+ 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]),
- ?line port_expect(Config,[{L1,
+ port_expect(Config,[{L1,
[{eol, Packet1}, {noeol, Packet2}, eof]}], 0,
S1, [{line,Siz},eof]),
%% Test that lonely <CR> Don't get treated as newlines
- ?line port_expect(Config,[{lists:append([Packet1, [13], Packet2,
+ port_expect(Config,[{lists:append([Packet1, [13], Packet2,
io_lib:nl()]),
[{noeol, Packet1}, {eol, [13 |Packet2]}]}],
0, "", [{line,Siz}]),
%% Test that packets get built up to lines (delayed output from
%% port program)
- ?line port_expect(Config,[{Packet2,[]},
+ 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}]),
%% Test that we get badarg if trying both packet and line
- ?line bad_argument(Config, [{packet, 5}, {line, 5}]),
- ?line test_server:timetrap_cancel(Dog),
+ bad_argument(Config, [{packet, 5}, {line, 5}]),
+ test_server:timetrap_cancel(Dog),
ok.
%%% Redirection of stderr test
@@ -834,15 +791,15 @@ stderr_to_stdout(suite) ->
stderr_to_stdout(doc) ->
"Test that redirection of standard error to standard output works.";
stderr_to_stdout(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(60)),
+ Dog = test_server:timetrap(test_server:seconds(60)),
%% See that it works
- ?line Packet = random_packet(10),
- ?line port_expect(Config,[{Packet,[Packet]}], 0, "-e -l10",
+ Packet = random_packet(10),
+ port_expect(Config,[{Packet,[Packet]}], 0, "-e -l10",
[stderr_to_stdout]),
- %% ?line stream_ping(Config, 10, "-e", [stderr_to_stdout]),
+ %% stream_ping(Config, 10, "-e", [stderr_to_stdout]),
%% See that it doesn't always happen (will generate garbage on stderr)
- ?line port_expect(Config,[{Packet,[eof]}], 0, "-e -l10", [line,eof]),
- ?line test_server:timetrap_cancel(Dog),
+ port_expect(Config,[{Packet,[eof]}], 0, "-e -l10", [line,eof]),
+ test_server:timetrap_cancel(Dog),
ok.
@@ -862,47 +819,39 @@ env(suite) ->
env(doc) ->
["Test that the 'env' option works"];
env(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skipped,"Environments not implemented on VxWorks (could be...)"};
- _ ->
- env2(Config)
- end.
-
-env2(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(60)),
- ?line Priv = ?config(priv_dir, Config),
- ?line Temp = filename:join(Priv, "env_fun.bin"),
+ Dog = test_server:timetrap(test_server:seconds(60)),
+ Priv = ?config(priv_dir, Config),
+ Temp = filename:join(Priv, "env_fun.bin"),
PluppVal = "dirty monkey",
- ?line env_slave(Temp, [{"plupp",PluppVal}]),
+ env_slave(Temp, [{"plupp",PluppVal}]),
Long = "LongAndBoringEnvName",
- ?line os:putenv(Long, "nisse"),
-
- ?line env_slave(Temp, [{"plupp",PluppVal},
- {"DIR_PLUPP","###glurfrik"}],
- fun() ->
- PluppVal = os:getenv("plupp"),
- "###glurfrik" = os:getenv("DIR_PLUPP"),
- "nisse" = os:getenv(Long)
- end),
+ 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),
-
- ?line env_slave(Temp, [{"must_define_something","some_value"},
- {"certainly_not_existing",false},
- {"ends_with_equal", "value="},
- {Long,false},
- {"glurf","a glorfy string"}]),
+
+ env_slave(Temp, [{"must_define_something","some_value"},
+ {"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)],
- ?line env_slave(Temp, lists:sort(ExistingList ++ NotExistingList)),
+ env_slave(Temp, lists:sort(ExistingList ++ NotExistingList)),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
env_slave(File, Env) ->
@@ -946,23 +895,15 @@ env_slave_main([File]) ->
%% 'env' option
%% Test bad environments.
bad_env(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skipped,"Environments not implemented on VxWorks"};
- _ ->
- bad_env_1()
- end.
-
-bad_env_1() ->
- ?line try_bad_env([abbb]),
- ?line try_bad_env([{"key","value"}|{"another","value"}]),
- ?line try_bad_env([{"key","value","value2"}]),
- ?line try_bad_env([{"key",[a,b,c]}]),
- ?line try_bad_env([{"key",value}]),
- ?line try_bad_env({a,tuple}),
- ?line try_bad_env(42),
- ?line try_bad_env([a|b]),
- ?line try_bad_env(self()),
+ try_bad_env([abbb]),
+ try_bad_env([{"key","value"}|{"another","value"}]),
+ try_bad_env([{"key","value","value2"}]),
+ try_bad_env([{"key",[a,b,c]}]),
+ try_bad_env([{"key",value}]),
+ try_bad_env({a,tuple}),
+ try_bad_env(42),
+ try_bad_env([a|b]),
+ try_bad_env(self()),
ok.
try_bad_env(Env) ->
@@ -979,36 +920,29 @@ cd(suite) ->
cd(doc) ->
["Test that the 'cd' option works"];
cd(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skipped,"Task specific directories does not exist on VxWorks"};
- _ ->
- cd2(Config)
- end.
-cd2(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(60)),
-
- ?line Program = atom_to_list(lib:progname()),
- ?line DataDir = ?config(data_dir, Config),
- ?line TestDir = filename:join(DataDir, "dir"),
- ?line Cmd = Program ++ " -pz " ++ DataDir ++
- " -noshell -s port_test pwd -s erlang halt",
- ?line _ = open_port({spawn, Cmd},
- [{cd, TestDir},
- {line, 256}]),
- ?line receive
- {_, {data, {eol, String}}} ->
- case filename_equal(String, TestDir) of
- true ->
- ok;
- false ->
- ?line test_server:fail({cd, String})
- end;
- Other2 ->
- ?line test_server:fail({env, Other2})
- end,
+ Dog = test_server:timetrap(test_server:seconds(60)),
- ?line test_server:timetrap_cancel(Dog),
+ Program = atom_to_list(lib:progname()),
+ DataDir = ?config(data_dir, Config),
+ TestDir = filename:join(DataDir, "dir"),
+ Cmd = Program ++ " -pz " ++ DataDir ++
+ " -noshell -s port_test pwd -s erlang halt",
+ _ = open_port({spawn, Cmd},
+ [{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})
+ end,
+
+ test_server:timetrap_cancel(Dog),
ok.
filename_equal(A, B) ->
@@ -1059,8 +993,8 @@ otp_3906(Config) when is_list(Config) ->
-define(OTP_3906_MAX_CONC_OSP, 50).
otp_3906(Config, OSName) ->
- ?line DataDir = filename:dirname(proplists:get_value(data_dir,Config)),
- ?line {ok, Variables} = file:consult(
+ DataDir = filename:dirname(proplists:get_value(data_dir,Config)),
+ {ok, Variables} = file:consult(
filename:join([DataDir,"..","..",
"test_server","variables"])),
case lists:keysearch('CC', 1, Variables) of
@@ -1105,7 +1039,7 @@ otp_3906(Config, OSName) ->
succeded ->
ok;
_ ->
- ?line test_server:fail(Result)
+ test_server:fail(Result)
end;
_ ->
{skipped, "No C compiler found"}
@@ -1246,15 +1180,15 @@ otp_4389(doc) -> [];
otp_4389(Config) when is_list(Config) ->
case os:type() of
{unix, _} ->
- ?line Dog = test_server:timetrap(test_server:seconds(240)),
- ?line TCR = self(),
+ Dog = test_server:timetrap(test_server:seconds(240)),
+ TCR = self(),
case get_true_cmd() of
True when is_list(True) ->
- ?line lists:foreach(
+ lists:foreach(
fun (P) ->
- ?line receive
- {P, ok} -> ?line ok;
- {P, Err} -> ?line ?t:fail(Err)
+ receive
+ {P, ok} -> ok;
+ {P, Err} -> ?t:fail(Err)
end
end,
lists:map(
@@ -1283,14 +1217,14 @@ otp_4389(Config) when is_list(Config) ->
end)
end,
lists:duplicate(1000,[]))),
- ?line test_server:timetrap_cancel(Dog),
+ 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."};
_ ->
- ?line {skipped, "\"true\" command not found"}
+ {skipped, "\"true\" command not found"}
end;
_ ->
{skip,"Only run on Unix"}
@@ -1320,11 +1254,11 @@ exit_status(suite) ->
exit_status(doc) ->
["Test that the 'exit_status' option works"];
exit_status(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(60)),
- ?line port_expect(Config,[{"x",
+ Dog = test_server:timetrap(test_server:seconds(60)),
+ port_expect(Config,[{"x",
[{exit_status, 5}]}],
1, "", [exit_status]),
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
spawn_driver(suite) ->
@@ -1332,36 +1266,36 @@ spawn_driver(suite) ->
spawn_driver(doc) ->
["Test spawning a driver specifically"];
spawn_driver(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"}, []),
- ?line Port ! {self(), {command, "Hello port!"}},
- ?line receive
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ Path = ?config(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,
- ?line Port ! {self(), close},
- ?line receive {Port, closed} -> ok end,
+ Port ! {self(), close},
+ receive {Port, closed} -> ok end,
- ?line Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"},
+ Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"},
[]),
- ?line receive
+ 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 {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, "ls"}, [])),
- ?line {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, "cmd"}, [])),
- ?line {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, os:find_executable("erl")}, [])),
- ?line test_server:timetrap_cancel(Dog),
+ 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.
spawn_executable(suite) ->
@@ -1369,48 +1303,48 @@ spawn_executable(suite) ->
spawn_executable(doc) ->
["Test spawning an executable specifically"];
spawn_executable(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line DataDir = ?config(data_dir, Config),
- ?line EchoArgs1 = filename:join([DataDir,"echo_args"]),
- ?line ExactFile1 = filename:nativename(os:find_executable(EchoArgs1)),
- ?line [ExactFile1] = run_echo_args(DataDir,[]),
- ?line ["echo_args"] = run_echo_args(DataDir,["echo_args"]),
- ?line ["echo_arguments"] = run_echo_args(DataDir,["echo_arguments"]),
- ?line [ExactFile1,"hello world","dlrow olleh"] =
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ DataDir = ?config(data_dir, Config),
+ EchoArgs1 = filename:join([DataDir,"echo_args"]),
+ ExactFile1 = filename:nativename(os:find_executable(EchoArgs1)),
+ [ExactFile1] = run_echo_args(DataDir,[]),
+ ["echo_args"] = run_echo_args(DataDir,["echo_args"]),
+ ["echo_arguments"] = run_echo_args(DataDir,["echo_arguments"]),
+ [ExactFile1,"hello world","dlrow olleh"] =
run_echo_args(DataDir,[ExactFile1,"hello world","dlrow olleh"]),
- ?line [ExactFile1] = run_echo_args(DataDir,[default]),
- ?line [ExactFile1,"hello world","dlrow olleh"] =
+ [ExactFile1] = run_echo_args(DataDir,[default]),
+ [ExactFile1,"hello world","dlrow olleh"] =
run_echo_args(DataDir,[switch_order,ExactFile1,"hello world",
"dlrow olleh"]),
- ?line [ExactFile1,"hello world","dlrow olleh"] =
+ [ExactFile1,"hello world","dlrow olleh"] =
run_echo_args(DataDir,[default,"hello world","dlrow olleh"]),
- ?line [ExactFile1,"hello world","dlrow olleh"] =
+ [ExactFile1,"hello world","dlrow olleh"] =
run_echo_args_2("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\""),
- ?line PrivDir = ?config(priv_dir, Config),
- ?line SpaceDir =filename:join([PrivDir,"With Spaces"]),
- ?line file:make_dir(SpaceDir),
- ?line Executable = filename:basename(ExactFile1),
- ?line file:copy(ExactFile1,filename:join([SpaceDir,Executable])),
- ?line ExactFile2 = filename:nativename(filename:join([SpaceDir,Executable])),
- ?line chmodplusx(ExactFile2),
+ PrivDir = ?config(priv_dir, Config),
+ SpaceDir =filename:join([PrivDir,"With Spaces"]),
+ file:make_dir(SpaceDir),
+ Executable = filename:basename(ExactFile1),
+ file:copy(ExactFile1,filename:join([SpaceDir,Executable])),
+ ExactFile2 = filename:nativename(filename:join([SpaceDir,Executable])),
+ chmodplusx(ExactFile2),
io:format("|~s|~n",[ExactFile2]),
- ?line [ExactFile2] = run_echo_args(SpaceDir,[]),
- ?line ["echo_args"] = run_echo_args(SpaceDir,["echo_args"]),
- ?line ["echo_arguments"] = run_echo_args(SpaceDir,["echo_arguments"]),
- ?line [ExactFile2,"hello world","dlrow olleh"] =
+ [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"]),
- ?line [ExactFile2] = run_echo_args(SpaceDir,[default]),
- ?line [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"]),
- ?line [ExactFile2,"hello world","dlrow olleh"] =
+ [ExactFile2,"hello world","dlrow olleh"] =
run_echo_args(SpaceDir,[default,"hello world","dlrow olleh"]),
- ?line [ExactFile2,"hello world","dlrow olleh"] =
+ [ExactFile2,"hello world","dlrow olleh"] =
run_echo_args_2("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\""),
- ?line ExeExt =
+ ExeExt =
case string:to_lower(lists:last(string:tokens(ExactFile2,"."))) of
"exe" ->
".exe";
@@ -1418,47 +1352,47 @@ spawn_executable(Config) when is_list(Config) ->
""
end,
Executable2 = "spoky name"++ExeExt,
- ?line file:copy(ExactFile1,filename:join([SpaceDir,Executable2])),
- ?line ExactFile3 = filename:nativename(filename:join([SpaceDir,Executable2])),
- ?line chmodplusx(ExactFile3),
- ?line [ExactFile3] = run_echo_args(SpaceDir,Executable2,[]),
- ?line ["echo_args"] = run_echo_args(SpaceDir,Executable2,["echo_args"]),
- ?line ["echo_arguments"] = run_echo_args(SpaceDir,Executable2,["echo_arguments"]),
- ?line [ExactFile3,"hello world","dlrow olleh"] =
+ file:copy(ExactFile1,filename:join([SpaceDir,Executable2])),
+ ExactFile3 = filename:nativename(filename:join([SpaceDir,Executable2])),
+ chmodplusx(ExactFile3),
+ [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"]),
- ?line [ExactFile3] = run_echo_args(SpaceDir,Executable2,[default]),
- ?line [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"]),
- ?line [ExactFile3,"hello world","dlrow olleh"] =
+ [ExactFile3,"hello world","dlrow olleh"] =
run_echo_args(SpaceDir,Executable2,
[default,"hello world","dlrow olleh"]),
- ?line [ExactFile3,"hello world","dlrow olleh"] =
+ [ExactFile3,"hello world","dlrow olleh"] =
run_echo_args_2("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\""),
- ?line {'EXIT',{enoent,_}} = (catch run_echo_args(SpaceDir,"fnurflmonfi",
+ {'EXIT',{enoent,_}} = (catch run_echo_args(SpaceDir,"fnurflmonfi",
[default,"hello world",
"dlrow olleh"])),
NonExec = "kronxfrt"++ExeExt,
- ?line file:write_file(filename:join([SpaceDir,NonExec]),
+ file:write_file(filename:join([SpaceDir,NonExec]),
<<"Not an executable">>),
- ?line {'EXIT',{eacces,_}} = (catch run_echo_args(SpaceDir,NonExec,
+ {'EXIT',{eacces,_}} = (catch run_echo_args(SpaceDir,NonExec,
[default,"hello world",
"dlrow olleh"])),
- ?line {'EXIT',{enoent,_}} = (catch open_port({spawn_executable,"cmd"},[])),
- ?line {'EXIT',{enoent,_}} = (catch open_port({spawn_executable,"sh"},[])),
+ {'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)
end,
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
unregister_name(Config) when is_list(Config) ->
- ?line true = register(crash, open_port({spawn, "sleep 100"}, [])),
- ?line true = unregister(crash).
+ true = register(crash, open_port({spawn, "sleep 100"}, [])),
+ true = unregister(crash).
test_bat_file(Dir) ->
FN = "tf.bat",
@@ -1478,16 +1412,16 @@ test_bat_file(Dir) ->
<<"\r\n">>,
<<":done\r\n">>,
<<"\r\n">>],
- ?line file:write_file(Full,list_to_binary(D)),
- ?line EF = filename:basename(FN),
- ?line [DN,"hello","world"] =
+ file:write_file(Full,list_to_binary(D)),
+ EF = filename:basename(FN),
+ [DN,"hello","world"] =
run_echo_args(Dir,FN,
[default,"hello","world"]),
%% The arg0 argumant should be ignored when running batch files
- ?line [DN,"hello","world"] =
+ [DN,"hello","world"] =
run_echo_args(Dir,FN,
["knaskurt","hello","world"]),
- ?line EF = filename:basename(DN),
+ EF = filename:basename(DN),
ok.
test_sh_file(Dir) ->
@@ -1501,20 +1435,20 @@ test_sh_file(Dir) ->
<<" shift\n">>,
<<" i=`expr $i + 1`\n">>,
<<"done\n">>],
- ?line file:write_file(Full,list_to_binary(D)),
- ?line chmodplusx(Full),
- ?line [Full,"hello","world"] =
+ file:write_file(Full,list_to_binary(D)),
+ chmodplusx(Full),
+ [Full,"hello","world"] =
run_echo_args(Dir,FN,
[default,"hello","world"]),
- ?line [Full,"hello","world of spaces"] =
+ [Full,"hello","world of spaces"] =
run_echo_args(Dir,FN,
[default,"hello","world of spaces"]),
- ?line file:write_file(filename:join([Dir,"testfile1"]),<<"testdata1">>),
- ?line file:write_file(filename:join([Dir,"testfile2"]),<<"testdata2">>),
- ?line Pattern = filename:join([Dir,"testfile*"]),
- ?line L = filelib:wildcard(Pattern),
- ?line 2 = length(L),
- ?line [Full,"hello",Pattern] =
+ 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.
@@ -1574,25 +1508,25 @@ mix_up_ports(suite) ->
mix_up_ports(doc) ->
["Test that the emulator does not mix up ports when the port table wraps"];
mix_up_ports(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, "echo_drv"}, []),
- ?line Port ! {self(), {command, "Hello port!"}},
- ?line receive
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ Path = ?config(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,
- ?line Port ! {self(), close},
- ?line receive {Port, closed} -> ok end,
- ?line loop(start, done,
+ Port ! {self(), close},
+ receive {Port, closed} -> ok end,
+ loop(start, done,
fun(P) ->
- ?line Q =
+ Q =
(catch erlang:open_port({spawn, "echo_drv"}, [])),
-%% ?line io:format("~p ", [Q]),
+%% io:format("~p ", [Q]),
if is_port(Q) ->
Q;
true ->
@@ -1600,14 +1534,14 @@ mix_up_ports(Config) when is_list(Config) ->
done
end
end),
- ?line Port ! {self(), {command, "Hello again port!"}},
- ?line receive
+ Port ! {self(), {command, "Hello again port!"}},
+ receive
Msg2 ->
test_server:fail({unexpected, Msg2})
after 1000 ->
ok
end,
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
loop(Stop, Stop, Fun) when is_function(Fun) ->
@@ -1622,45 +1556,45 @@ otp_5112(doc) ->
["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) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Path = ?config(data_dir, Config),
- ?line ok = load_driver(Path, "exit_drv"),
- ?line Port = otp_5112_get_wrapped_port(),
- ?line ?t:format("Max ports: ~p~n",[max_ports()]),
- ?line ?t:format("Port: ~p~n",[Port]),
- ?line {links, Links1} = process_info(self(),links),
- ?line ?t:format("Links1: ~p~n",[Links1]),
- ?line true = lists:member(Port, Links1),
- ?line Port ! {self(), {command, ""}},
- ?line {links, Links2} = process_info(self(),links),
- ?line ?t:format("Links2: ~p~n",[Links2]),
- ?line false = lists:member(Port, Links2), %% This used to fail
- ?line test_server:timetrap_cancel(Dog),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ Path = ?config(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]),
+ {links, Links1} = process_info(self(),links),
+ ?t:format("Links1: ~p~n",[Links1]),
+ true = lists:member(Port, Links1),
+ Port ! {self(), {command, ""}},
+ {links, Links2} = process_info(self(),links),
+ ?t: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() ->
- ?line P1 = erlang:open_port({spawn, "exit_drv"}, []),
- ?line case port_ix(P1) < max_ports() of
+ P1 = erlang:open_port({spawn, "exit_drv"}, []),
+ case port_ix(P1) < max_ports() of
true ->
- ?line ?t:format("Need to wrap port index (~p)~n", [P1]),
- ?line otp_5112_wrap_port_ix([P1]),
- ?line P2 = erlang:open_port({spawn, "exit_drv"}, []),
- ?line false = port_ix(P2) < max_ports(),
- ?line P2;
+ ?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 ->
- ?line ?t:format("Port index already wrapped (~p)~n", [P1]),
- ?line P1
+ ?t:format("Port index already wrapped (~p)~n", [P1]),
+ P1
end.
otp_5112_wrap_port_ix(Ports) ->
- ?line case (catch erlang:open_port({spawn, "exit_drv"}, [])) of
+ case (catch erlang:open_port({spawn, "exit_drv"}, [])) of
Port when is_port(Port) ->
- ?line otp_5112_wrap_port_ix([Port|Ports]);
+ otp_5112_wrap_port_ix([Port|Ports]);
_ ->
%% Port table now full; empty port table
- ?line lists:foreach(fun (P) -> P ! {self(), close} end,
+ lists:foreach(fun (P) -> P ! {self(), close} end,
Ports),
- ?line ok
+ ok
end.
@@ -1669,106 +1603,105 @@ otp_5119(suite) ->
otp_5119(doc) ->
["Test that port index is not unnecessarily wrapped"];
otp_5119(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, "exit_drv"),
- ?line PI1 = port_ix(otp_5119_fill_empty_port_tab([])),
- ?line PI2 = port_ix(erlang:open_port({spawn, "exit_drv"}, [])),
- ?line {PortIx1, PortIx2}
- = case PI2 > PI1 of
- true ->
- ?line {PI1, PI2};
- false ->
- ?line {port_ix(otp_5119_fill_empty_port_tab([PI2])),
- port_ix(erlang:open_port({spawn, "exit_drv"}, []))}
- end,
- ?line MaxPorts = max_ports(),
- ?line ?t:format("PortIx1 = ~p ~p~n", [PI1, PortIx1]),
- ?line ?t:format("PortIx2 = ~p ~p~n", [PI2, PortIx2]),
- ?line ?t:format("MaxPorts = ~p~n", [MaxPorts]),
- ?line true = PortIx2 > PortIx1,
- ?line true = PortIx2 =< PortIx1 + MaxPorts,
- ?line test_server:timetrap_cancel(Dog),
- ?line ok.
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ Path = ?config(data_dir, Config),
+ ok = load_driver(Path, "exit_drv"),
+ PI1 = port_ix(otp_5119_fill_empty_port_tab([])),
+ PI2 = port_ix(erlang:open_port({spawn, "exit_drv"}, [])),
+ {PortIx1, PortIx2} = case PI2 > PI1 of
+ true ->
+ {PI1, PI2};
+ false ->
+ {port_ix(otp_5119_fill_empty_port_tab([PI2])),
+ 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]),
+ true = PortIx2 > PortIx1,
+ true = PortIx2 =< PortIx1 + MaxPorts,
+ test_server:timetrap_cancel(Dog),
+ ok.
otp_5119_fill_empty_port_tab(Ports) ->
- ?line case (catch erlang:open_port({spawn, "exit_drv"}, [])) of
+ case (catch erlang:open_port({spawn, "exit_drv"}, [])) of
Port when is_port(Port) ->
- ?line otp_5119_fill_empty_port_tab([Port|Ports]);
+ otp_5119_fill_empty_port_tab([Port|Ports]);
_ ->
%% Port table now full; empty port table
- ?line lists:foreach(fun (P) -> P ! {self(), close} end,
+ lists:foreach(fun (P) -> P ! {self(), close} end,
Ports),
- ?line [LastPort|_] = Ports,
- ?line LastPort
+ [LastPort|_] = Ports,
+ LastPort
end.
-define(DEF_MAX_PORTS, 1024).
max_ports_env() ->
- ?line case os:getenv("ERL_MAX_PORTS") of
+ case os:getenv("ERL_MAX_PORTS") of
EMP when is_list(EMP) ->
case catch list_to_integer(EMP) of
- Int when is_integer(Int) -> ?line Int;
- _ -> ?line false
+ Int when is_integer(Int) -> Int;
+ _ -> false
end;
- _ -> ?line false
+ _ -> false
end.
max_ports() ->
- ?line PreMaxPorts
+ PreMaxPorts
= case max_ports_env() of
- Env when is_integer(Env) -> ?line Env;
+ Env when is_integer(Env) -> Env;
_ ->
- ?line case os:type() of
+ case os:type() of
{unix, _} ->
- ?line UlimStr = string:strip(os:cmd("ulimit -n")
+ UlimStr = string:strip(os:cmd("ulimit -n")
-- "\n"),
- ?line case catch list_to_integer(UlimStr) of
- Ulim when is_integer(Ulim) -> ?line Ulim;
- _ -> ?line ?DEF_MAX_PORTS
+ case catch list_to_integer(UlimStr) of
+ Ulim when is_integer(Ulim) -> Ulim;
+ _ -> ?DEF_MAX_PORTS
end;
- _ -> ?line ?DEF_MAX_PORTS
+ _ -> ?DEF_MAX_PORTS
end
end,
- ?line case PreMaxPorts > ?DEF_MAX_PORTS of
- true -> ?line PreMaxPorts;
- false -> ?line ?DEF_MAX_PORTS
+ case PreMaxPorts > ?DEF_MAX_PORTS of
+ true -> PreMaxPorts;
+ false -> ?DEF_MAX_PORTS
end.
port_ix(Port) when is_port(Port) ->
- ?line ["#Port",_,PortIxStr] = string:tokens(erlang:port_to_list(Port),
+ ["#Port",_,PortIxStr] = string:tokens(erlang:port_to_list(Port),
"<.>"),
- ?line list_to_integer(PortIxStr).
+ list_to_integer(PortIxStr).
otp_6224(doc) -> ["Check that port command failure doesn't crash the emulator"];
otp_6224(suite) -> [];
otp_6224(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, "failure_drv"),
- ?line Go = make_ref(),
- ?line Failer = spawn(fun () ->
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ Path = ?config(data_dir, Config),
+ ok = load_driver(Path, "failure_drv"),
+ Go = make_ref(),
+ Failer = spawn(fun () ->
receive Go -> ok end,
- ?line Port = open_port({spawn, "failure_drv"},
+ Port = open_port({spawn, "failure_drv"},
[]),
Port ! {self(), {command, "Fail, please!"}},
otp_6224_loop()
end),
- ?line Mon = erlang:monitor(process, Failer),
- ?line Failer ! Go,
- ?line receive
+ Mon = erlang:monitor(process, Failer),
+ Failer ! Go,
+ receive
{'DOWN', Mon, process, Failer, Reason} ->
- ?line case Reason of
- {driver_failed, _} -> ?line ok;
- driver_failed -> ?line ok;
- _ -> ?line ?t:fail({unexpected_exit_reason,
+ case Reason of
+ {driver_failed, _} -> ok;
+ driver_failed -> ok;
+ _ -> ?t:fail({unexpected_exit_reason,
Reason})
end
end,
- ?line test_server:timetrap_cancel(Dog),
- ?line ok.
+ test_server:timetrap_cancel(Dog),
+ ok.
otp_6224_loop() ->
receive _ -> ok after 0 -> ok end,
@@ -1781,11 +1714,11 @@ otp_6224_loop() ->
exit_status_multi_scheduling_block(doc) -> [];
exit_status_multi_scheduling_block(suite) -> [];
exit_status_multi_scheduling_block(Config) when is_list(Config) ->
- ?line Repeat = 3,
- ?line case ?t:os_type() of
+ Repeat = 3,
+ case ?t:os_type() of
{unix, _} ->
- ?line Dog = ?t:timetrap(test_server:minutes(2*Repeat)),
- ?line SleepSecs = 6,
+ Dog = ?t:timetrap(test_server:minutes(2*Repeat)),
+ SleepSecs = 6,
try
lists:foreach(fun (_) ->
exit_status_msb_test(Config,
@@ -1799,7 +1732,7 @@ exit_status_multi_scheduling_block(Config) when is_list(Config) ->
?t:timetrap_cancel(Dog),
receive after SleepSecs+500 -> ok end
end;
- _ -> ?line {skip, "Not implemented for this OS"}
+ _ -> {skip, "Not implemented for this OS"}
end.
exit_status_msb_test(Config, SleepSecs) when is_list(Config) ->
@@ -1808,22 +1741,22 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) ->
%% and we want these port programs to terminate while multi-scheduling
%% is blocked.
%%
- ?line NoSchedsOnln = erlang:system_info(schedulers_online),
- ?line Parent = self(),
- ?line ?t:format("SleepSecs = ~p~n", [SleepSecs]),
- ?line PortProg = "sleep " ++ integer_to_list(SleepSecs),
- ?line Start = now(),
- ?line NoProcs = case NoSchedsOnln of
+ NoSchedsOnln = erlang:system_info(schedulers_online),
+ Parent = self(),
+ ?t:format("SleepSecs = ~p~n", [SleepSecs]),
+ PortProg = "sleep " ++ integer_to_list(SleepSecs),
+ Start = now(),
+ NoProcs = case NoSchedsOnln of
NProcs when NProcs < ?EXIT_STATUS_MSB_MAX_PROCS ->
NProcs;
_ ->
?EXIT_STATUS_MSB_MAX_PROCS
end,
- ?line NoPortsPerProc = case 20*NoProcs of
+ NoPortsPerProc = case 20*NoProcs of
TNPorts when TNPorts < ?EXIT_STATUS_MSB_MAX_PORTS -> 20;
_ -> ?EXIT_STATUS_MSB_MAX_PORTS div NoProcs
end,
- ?line ?t:format("NoProcs = ~p~nNoPortsPerProc = ~p~n",
+ ?t:format("NoProcs = ~p~nNoPortsPerProc = ~p~n",
[NoProcs, NoPortsPerProc]),
ProcFun
= fun () ->
@@ -1873,32 +1806,32 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) ->
PrtSIds),
Parent ! {self(), done}
end,
- ?line Procs = lists:map(fun (N) ->
+ Procs = lists:map(fun (N) ->
spawn_opt(ProcFun,
[link,
{scheduler,
(N rem NoSchedsOnln)+1}])
end,
lists:seq(1, NoProcs)),
- ?line SIds = lists:map(fun (P) ->
+ SIds = lists:map(fun (P) ->
receive {P, started, SIds} -> SIds end
end,
Procs),
- ?line StartedTime = timer:now_diff(now(), Start)/1000000,
- ?line ?t:format("StartedTime = ~p~n", [StartedTime]),
- ?line true = StartedTime < SleepSecs,
- ?line erlang:system_flag(multi_scheduling, block),
- ?line lists:foreach(fun (P) -> receive {P, done} -> ok end end, Procs),
- ?line DoneTime = timer:now_diff(now(), Start)/1000000,
- ?line ?t:format("DoneTime = ~p~n", [DoneTime]),
- ?line true = DoneTime > SleepSecs,
- ?line ok = verify_multi_scheduling_blocked(),
- ?line erlang:system_flag(multi_scheduling, unblock),
- ?line case {length(lists:usort(lists:flatten(SIds))), NoSchedsOnln} of
+ StartedTime = timer:now_diff(now(), Start)/1000000,
+ ?t: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 = timer:now_diff(now(), Start)/1000000,
+ ?t: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} ->
- ?line ok;
+ ok;
{N, M} ->
- ?line ?t:fail("Failed to create ports on all"
+ ?t:fail("Failed to create ports on all"
++ integer_to_list(M) ++ " available"
"schedulers. Only created ports on "
++ integer_to_list(N) ++ " schedulers.")
@@ -1921,18 +1854,18 @@ sid_proc(SIds) ->
end.
verify_multi_scheduling_blocked() ->
- ?line Procs = lists:map(fun (_) ->
+ Procs = lists:map(fun (_) ->
spawn_link(fun () -> sid_proc([]) end)
end,
lists:seq(1, 3*erlang:system_info(schedulers_online))),
- ?line receive after 1000 -> ok end,
- ?line SIds = lists:map(fun (P) ->
+ receive after 1000 -> ok end,
+ SIds = lists:map(fun (P) ->
P ! {self(), want_sids},
receive {P, sids, PSIds} -> PSIds end
end,
Procs),
- ?line 1 = length(lists:usort(lists:flatten(SIds))),
- ?line ok.
+ 1 = length(lists:usort(lists:flatten(SIds))),
+ ok.
%%% Pinging functions.
@@ -1993,30 +1926,30 @@ build_cmd_line(FixedCmdLine, [], Result) ->
port_expect(Config, Actions, HSize, CmdLine, Options0) ->
% io:format("port_expect(~p, ~p, ~p, ~p)",
% [Actions, HSize, CmdLine, Options0]),
- ?line PortTest = port_test(Config),
- ?line Cmd = lists:concat([PortTest, " -h", HSize, " ", CmdLine]),
- ?line PortType =
+ PortTest = port_test(Config),
+ Cmd = lists:concat([PortTest, " -h", HSize, " ", CmdLine]),
+ PortType =
case HSize of
0 -> stream;
_ -> {packet, HSize}
end,
- ?line Options = [PortType|Options0],
- ?line io:format("open_port({spawn, ~p}, ~p)", [Cmd, Options]),
- ?line Port = open_port({spawn, Cmd}, Options),
- ?line port_expect(Port, Actions, Options),
+ Options = [PortType|Options0],
+ io:format("open_port({spawn, ~p}, ~p)", [Cmd, Options]),
+ Port = open_port({spawn, Cmd}, Options),
+ port_expect(Port, Actions, Options),
Port.
port_expect(Port, [{Send, Expects}|Rest], Options) when is_list(Expects) ->
- ?line port_send(Port, Send),
- ?line IsBinaryPort = lists:member(binary, Options),
- ?line Receiver =
+ 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,
- ?line Receiver(Port, maybe_to_binary(Expects, IsBinaryPort)),
- ?line port_expect(Port, Rest, Options);
+ Receiver(Port, maybe_to_binary(Expects, IsBinaryPort)),
+ port_expect(Port, Rest, Options);
port_expect(_, [], _) ->
ok.
@@ -2068,7 +2001,7 @@ receive_all(Port, [Expect|Rest]) ->
_ ->
%%% io:format("Unexpected message: ~s", [format(Other)]),
io:format("Unexpected message: ~w", [Other]),
- ?line test_server:fail(unexpected_message)
+ test_server:fail(unexpected_message)
end
end,
receive_all(Port, Rest);
@@ -2157,15 +2090,8 @@ build_packet(Left, Result, NextChar0) ->
build_packet(Left-1, [NextChar0|Result], NextChar).
sizes() ->
- case os:type() of
- vxworks ->
- % don't stress VxWorks too much
- [10, 13, 64, 127, 128, 255, 256, 1023, 1024,
- 8191, 8192, 16383, 16384];
- _ ->
- [10, 13, 64, 127, 128, 255, 256, 1023, 1024,
- 32767, 32768, 65535, 65536]
- end.
+ [10, 13, 64, 127, 128, 255, 256, 1023, 1024,
+ 32767, 32768, 65535, 65536].
sizes(Header_Size) ->
sizes(Header_Size, sizes(), []).
@@ -2203,18 +2129,18 @@ fun_spawn(Fun, Args) ->
spawn_link(erlang, apply, [Fun, Args]).
port_test(Config) when is_list(Config) ->
- ?line filename:join(?config(data_dir, Config), "port_test").
+ filename:join(?config(data_dir, Config), "port_test").
ports(doc) -> "Test that erlang:ports/0 returns a consistent snapshot of ports";
ports(suite) -> [];
ports(Config) when is_list(Config) ->
- ?line Path = ?config(data_dir, Config),
- ?line ok = load_driver(Path, "exit_drv"),
+ Path = ?config(data_dir, Config),
+ ok = load_driver(Path, "exit_drv"),
receive after 1000 -> ok end, % Wait for other ports to stabilize
- ?line OtherPorts = erlang:ports(),
+ OtherPorts = erlang:ports(),
io:format("Other ports: ~p\n",[OtherPorts]),
MaxPorts = 1024 - length(OtherPorts),
@@ -2222,7 +2148,7 @@ ports(Config) when is_list(Config) ->
ports_snapshots(100, TrafficPid, OtherPorts),
TrafficPid ! {self(),die},
- ?line receive {TrafficPid, dead} -> ok end,
+ receive {TrafficPid, dead} -> ok end,
ok.
ports_snapshots(0, _, _) ->
@@ -2230,12 +2156,12 @@ ports_snapshots(0, _, _) ->
ports_snapshots(Iter, TrafficPid, OtherPorts) ->
TrafficPid ! start,
- ?line receive after 1 -> ok end,
+ receive after 1 -> ok end,
Snapshot = erlang:ports(),
TrafficPid ! {self(), stop},
- ?line receive {TrafficPid, EventList, TrafficPorts} -> ok end,
+ receive {TrafficPid, EventList, TrafficPorts} -> ok end,
%%io:format("Snapshot=~p\n", [Snapshot]),
ports_verify(Snapshot, OtherPorts ++ TrafficPorts, EventList),
@@ -2252,7 +2178,7 @@ ports_traffic_stopped(MaxPorts, {PortList, PortCnt}) ->
%%io:format("Traffic started in ~p\n",[self()]),
ports_traffic_started(MaxPorts, {PortList, PortCnt}, []);
{Pid,die} ->
- ?line lists:foreach(fun(Port)-> erlang:port_close(Port) end,
+ lists:foreach(fun(Port)-> erlang:port_close(Port) end,
PortList),
Pid ! {self(),dead}
end.
@@ -2272,15 +2198,15 @@ ports_traffic_do(MaxPorts, {PortList, PortCnt}, EventList) ->
N = uniform(MaxPorts),
case N > PortCnt of
true -> % Open port
- ?line P = open_port({spawn, "exit_drv"}, []),
+ 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
- ?line P = lists:nth(N, PortList),
+ P = lists:nth(N, PortList),
%%io:format("Close port ~p\n",[P]),
- ?line true = erlang:port_close(P),
+ true = erlang:port_close(P),
ports_traffic_started(MaxPorts, {lists:delete(P,PortList), PortCnt-1},
[{close,P}|EventList])
end.
@@ -2301,7 +2227,7 @@ ports_verify(Ports, PortsAfter, EventList) ->
ports_verify(Ports, [P | PortsAfter], Tail);
[] ->
- ?line test_server:fail("Inconsistent snapshot from erlang:ports()")
+ test_server:fail("Inconsistent snapshot from erlang:ports()")
end
end.
@@ -2318,27 +2244,27 @@ close_deaf_port(doc) -> ["Send data to port program that does not read it, then
"Primary targeting Windows to test threaded_handle_closer in sys.c"];
close_deaf_port(suite) -> [];
close_deaf_port(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(100)),
- ?line DataDir = ?config(data_dir, Config),
- ?line DeadPort = os:find_executable("dead_port", DataDir),
- ?line Port = open_port({spawn,DeadPort++" 60"},[]),
- ?line erlang:port_command(Port,"Hello, can you hear me!?!?"),
- ?line port_close(Port),
+ Dog = test_server:timetrap(test_server:seconds(100)),
+ DataDir = ?config(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!?!?"),
+ port_close(Port),
Res = close_deaf_port_1(0, DeadPort),
io:format("Waiting for OS procs to terminate...\n"),
receive after 5*1000 -> ok end,
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
Res.
close_deaf_port_1(1000, _) ->
ok;
close_deaf_port_1(N, Cmd) ->
Timeout = integer_to_list(random:uniform(5*1000)),
- ?line try open_port({spawn_executable,Cmd},[{args,[Timeout]}]) of
+ try open_port({spawn_executable,Cmd},[{args,[Timeout]}]) of
Port ->
- ?line erlang:port_command(Port,"Hello, can you hear me!?!?"),
- ?line port_close(Port),
+ erlang:port_command(Port,"Hello, can you hear me!?!?"),
+ port_close(Port),
close_deaf_port_1(N+1, Cmd)
catch
_:eagain ->
diff --git a/erts/emulator/test/port_SUITE_data/dead_port.c b/erts/emulator/test/port_SUITE_data/dead_port.c
index 68e96fbf14..4dd9ee4cc2 100644
--- a/erts/emulator/test/port_SUITE_data/dead_port.c
+++ b/erts/emulator/test/port_SUITE_data/dead_port.c
@@ -17,15 +17,6 @@
* %CopyrightEnd%
*/
-#ifdef VXWORKS
-#include <vxWorks.h>
-#include <taskVarLib.h>
-#include <taskLib.h>
-#include <sysLib.h>
-#include <string.h>
-#include <ioLib.h>
-#endif
-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -37,12 +28,7 @@
#ifndef __WIN32__
#include <unistd.h>
-#ifdef VXWORKS
-#include "reclaim.h"
-#include <sys/times.h>
-#else
#include <sys/time.h>
-#endif
#define O_BINARY 0
#define _setmode(fd, mode)
@@ -53,13 +39,7 @@
#include "winbase.h"
#endif
-
-#ifdef VXWORKS
-#define MAIN(argc, argv) port_test(argc, argv)
-#else
#define MAIN(argc, argv) main(argc, argv)
-#endif
-
extern int errno;
@@ -86,9 +66,6 @@ char *argv[];
static void
delay(unsigned ms)
{
-#ifdef VXWORKS
- taskDelay((sysClkRateGet() * ms) / 1000);
-#else
#ifdef __WIN32__
Sleep(ms);
#else
@@ -98,5 +75,4 @@ delay(unsigned ms)
select(0, NULL, NULL, NULL, &t);
#endif
-#endif
}
diff --git a/erts/emulator/test/port_SUITE_data/port_test.c b/erts/emulator/test/port_SUITE_data/port_test.c
index 7b4e386d87..7abefab2e3 100644
--- a/erts/emulator/test/port_SUITE_data/port_test.c
+++ b/erts/emulator/test/port_SUITE_data/port_test.c
@@ -3,15 +3,6 @@
* Purpose: A port program to be used for testing the open_port bif.
*/
-#ifdef VXWORKS
-#include <vxWorks.h>
-#include <taskVarLib.h>
-#include <taskLib.h>
-#include <sysLib.h>
-#include <string.h>
-#include <ioLib.h>
-#endif
-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -23,12 +14,7 @@
#ifndef __WIN32__
#include <unistd.h>
-#ifdef VXWORKS
-#include "reclaim.h"
-#include <sys/times.h>
-#else
#include <sys/time.h>
-#endif
#define O_BINARY 0
#define _setmode(fd, mode)
@@ -40,22 +26,13 @@
#endif
-#ifdef VXWORKS
-#define REDIR_STDOUT(fd) ioTaskStdSet(0, 1, fd);
-#else
#define REDIR_STDOUT(fd) if (dup2(fd, 1) == -1) { \
fprintf(stderr, "%s: failed to duplicate handle %d to 1: %d\n", \
port_data->progname, fd, errno); \
exit(1); \
}
-#endif
-#ifdef VXWORKS
-#define MAIN(argc, argv) port_test(argc, argv)
-#else
#define MAIN(argc, argv) main(argc, argv)
-#endif
-
extern int errno;
@@ -101,7 +78,6 @@ static void dump(unsigned char* buf, int sz, int max);
static void replace_stdout(char* filename);
static void generate_reply(char* spec);
-#ifndef VXWORKS
#ifndef HAVE_STRERROR
extern int sys_nerr;
#ifndef sys_errlist /* sys_errlist is sometimes defined to
@@ -125,7 +101,6 @@ int err;
return msgstr;
}
#endif
-#endif
MAIN(argc, argv)
@@ -133,12 +108,6 @@ int argc;
char *argv[];
{
int ret;
-#ifdef VXWORKS
- if(taskVarAdd(0, (int *)&port_data) != OK) {
- fprintf(stderr, "Can't do taskVarAdd in port_test\n");
- exit(1);
- }
-#endif
if((port_data = (PORT_TEST_DATA *) malloc(sizeof(PORT_TEST_DATA))) == NULL) {
fprintf(stderr, "Couldn't malloc for port_data");
exit(1);
@@ -511,9 +480,6 @@ dump(buf, sz, max)
static void
delay(unsigned ms)
{
-#ifdef VXWORKS
- taskDelay((sysClkRateGet() * ms) / 1000);
-#else
#ifdef __WIN32__
Sleep(ms);
#else
@@ -523,7 +489,6 @@ delay(unsigned ms)
select(0, NULL, NULL, NULL, &t);
#endif
-#endif
}
/*
diff --git a/erts/emulator/test/port_SUITE_data/reclaim.h b/erts/emulator/test/port_SUITE_data/reclaim.h
deleted file mode 100644
index 1d57dc5b8a..0000000000
--- a/erts/emulator/test/port_SUITE_data/reclaim.h
+++ /dev/null
@@ -1,60 +0,0 @@
-#ifndef __RECLAIM_H__
-#define __RECLAIM_H__
-
-
-/* The Erlang release for VxWorks includes a simple mechanism for
- "resource reclamation" at task exit - it allows replacement of the
- functions that open/close "files" and malloc/free memory with versions
- that keep track, to be able to "reclaim" file descriptors and memory
- when a task exits (regardless of *how* it exits).
-
- The interface to this mechanism is made available via this file,
- with the following caveats:
-
- - The interface may change (or perhaps even be removed, though that
- isn't likely until VxWorks itself provides similar functionality)
- in future releases - i.e. you must always use the version of this
- file that comes with the Erlang release you are using.
-
- - Disaster is guaranteed if you use the mechanism incorrectly (see
- below for the correct way), e.g. allocate memory with the "tracking"
- version of malloc() and free it with the "standard" version of free().
-
- - The mechanism (of course) incurs some performance penalty - thus
- for a simple program you may be better off with careful programming,
- making sure that you do whatever close()/free()/etc calls that are
- appropriate at all exit points (though if you need to guard against
- taskDelete() etc, things get messy...).
-
- To use the mechanism, simply program your application normally, i.e.
- use open()/close()/malloc()/free() etc as usual, but #include this
- file before any usage of the relevant functions. NOTE: To avoid the
- "disaster" mentioned above, you *must* #include it in *all* (or none)
- of the files that manipulate a particular file descriptor, allocated
- memory area, etc. Finally, note that you can obviously not load your
- application before the Erlang system when using this interface.
-*/
-
-/* Sorry, no ANSI prototypes yet... */
-extern int save_open(),save_creat(),save_socket(),save_accept(),save_close();
-#define open save_open
-#define creat save_creat
-#define socket save_socket
-#define accept save_accept
-#define close save_close
-extern FILE *save_fopen(), *save_fdopen(), *save_freopen();
-extern int save_fclose();
-#define fopen save_fopen
-#define fdopen save_fdopen
-#define freopen save_freopen
-#define fclose save_fclose
-/* XXX Should do opendir/closedir too... */
-extern char *save_malloc(), *save_calloc(), *save_realloc();
-extern void save_free(), save_cfree();
-#define malloc save_malloc
-#define calloc save_calloc
-#define realloc save_realloc
-#define free save_free
-#define cfree save_cfree
-
-#endif
diff --git a/erts/emulator/test/port_bif_SUITE.erl b/erts/emulator/test/port_bif_SUITE.erl
index 8feea87d7e..f439867e9c 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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/port_bif_SUITE_data/port_test.c b/erts/emulator/test/port_bif_SUITE_data/port_test.c
index c6b128df66..28324a56a6 100644
--- a/erts/emulator/test/port_bif_SUITE_data/port_test.c
+++ b/erts/emulator/test/port_bif_SUITE_data/port_test.c
@@ -3,15 +3,6 @@
* Purpose: A port program to be used for testing the open_port bif.
*/
-#ifdef VXWORKS
-#include <vxWorks.h>
-#include <taskVarLib.h>
-#include <taskLib.h>
-#include <sysLib.h>
-#include <string.h>
-#include <ioLib.h>
-#endif
-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -23,12 +14,7 @@
#ifndef __WIN32__
#include <unistd.h>
-#ifdef VXWORKS
-#include "reclaim.h"
-#include <sys/times.h>
-#else
#include <sys/time.h>
-#endif
#define O_BINARY 0
#define _setmode(fd, mode)
@@ -40,22 +26,13 @@
#endif
-#ifdef VXWORKS
-#define REDIR_STDOUT(fd) ioTaskStdSet(0, 1, fd);
-#else
#define REDIR_STDOUT(fd) if (dup2(fd, 1) == -1) { \
fprintf(stderr, "%s: failed to duplicate handle %d to 1: %d\n", \
port_data->progname, fd, errno); \
exit(1); \
}
-#endif
-#ifdef VXWORKS
-#define MAIN(argc, argv) port_test(argc, argv)
-#else
#define MAIN(argc, argv) main(argc, argv)
-#endif
-
extern int errno;
@@ -101,7 +78,6 @@ static void dump(unsigned char* buf, int sz, int max);
static void replace_stdout(char* filename);
static void generate_reply(char* spec);
-#ifndef VXWORKS
#ifndef HAVE_STRERROR
extern int sys_nerr;
#ifndef sys_errlist /* sys_errlist is sometimes defined to
@@ -125,20 +101,13 @@ int err;
return msgstr;
}
#endif
-#endif
-
MAIN(argc, argv)
int argc;
char *argv[];
{
int ret;
-#ifdef VXWORKS
- if(taskVarAdd(0, (int *)&port_data) != OK) {
- fprintf(stderr, "Can't do taskVarAdd in port_test\n");
- exit(1);
- }
-#endif
+
if((port_data = (PORT_TEST_DATA *) malloc(sizeof(PORT_TEST_DATA))) == NULL) {
fprintf(stderr, "Couldn't malloc for port_data");
exit(1);
@@ -508,9 +477,6 @@ dump(buf, sz, max)
static void
delay(unsigned ms)
{
-#ifdef VXWORKS
- taskDelay((sysClkRateGet() * ms) / 1000);
-#else
#ifdef __WIN32__
Sleep(ms);
#else
@@ -520,7 +486,6 @@ delay(unsigned ms)
select(0, NULL, NULL, NULL, &t);
#endif
-#endif
}
/*
diff --git a/erts/emulator/test/port_bif_SUITE_data/reclaim.h b/erts/emulator/test/port_bif_SUITE_data/reclaim.h
deleted file mode 100644
index 1d57dc5b8a..0000000000
--- a/erts/emulator/test/port_bif_SUITE_data/reclaim.h
+++ /dev/null
@@ -1,60 +0,0 @@
-#ifndef __RECLAIM_H__
-#define __RECLAIM_H__
-
-
-/* The Erlang release for VxWorks includes a simple mechanism for
- "resource reclamation" at task exit - it allows replacement of the
- functions that open/close "files" and malloc/free memory with versions
- that keep track, to be able to "reclaim" file descriptors and memory
- when a task exits (regardless of *how* it exits).
-
- The interface to this mechanism is made available via this file,
- with the following caveats:
-
- - The interface may change (or perhaps even be removed, though that
- isn't likely until VxWorks itself provides similar functionality)
- in future releases - i.e. you must always use the version of this
- file that comes with the Erlang release you are using.
-
- - Disaster is guaranteed if you use the mechanism incorrectly (see
- below for the correct way), e.g. allocate memory with the "tracking"
- version of malloc() and free it with the "standard" version of free().
-
- - The mechanism (of course) incurs some performance penalty - thus
- for a simple program you may be better off with careful programming,
- making sure that you do whatever close()/free()/etc calls that are
- appropriate at all exit points (though if you need to guard against
- taskDelete() etc, things get messy...).
-
- To use the mechanism, simply program your application normally, i.e.
- use open()/close()/malloc()/free() etc as usual, but #include this
- file before any usage of the relevant functions. NOTE: To avoid the
- "disaster" mentioned above, you *must* #include it in *all* (or none)
- of the files that manipulate a particular file descriptor, allocated
- memory area, etc. Finally, note that you can obviously not load your
- application before the Erlang system when using this interface.
-*/
-
-/* Sorry, no ANSI prototypes yet... */
-extern int save_open(),save_creat(),save_socket(),save_accept(),save_close();
-#define open save_open
-#define creat save_creat
-#define socket save_socket
-#define accept save_accept
-#define close save_close
-extern FILE *save_fopen(), *save_fdopen(), *save_freopen();
-extern int save_fclose();
-#define fopen save_fopen
-#define fdopen save_fdopen
-#define freopen save_freopen
-#define fclose save_fclose
-/* XXX Should do opendir/closedir too... */
-extern char *save_malloc(), *save_calloc(), *save_realloc();
-extern void save_free(), save_cfree();
-#define malloc save_malloc
-#define calloc save_calloc
-#define realloc save_realloc
-#define free save_free
-#define cfree save_cfree
-
-#endif
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index fdc55a4cc5..6509871a7d 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -280,15 +280,7 @@ kb_128() ->
big_binary()}.
eight_kb() ->
- %%% This is really much more than eight kb, so vxworks platforms
- %%% gets away with 1/8 of the other platforms (due to limited
- %%% memory resources).
- B64 = case os:type() of
- vxworks ->
- ?line lists:seq(1, 8);
- _ ->
- ?line lists:seq(1, 64)
- end,
+ B64 = lists:seq(1, 64),
?line B512 = {<<1>>,B64,<<2,3>>,B64,make_unaligned_sub_binary(<<4,5,6,7,8,9>>),
B64,make_sub_binary([1,2,3,4,5,6]),
B64,make_sub_binary(lists:seq(1, ?heap_binary_size+1)),
diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl
index 7ff7449ff5..c9533d0748 100644
--- a/erts/emulator/test/timer_bif_SUITE.erl
+++ b/erts/emulator/test/timer_bif_SUITE.erl
@@ -71,38 +71,23 @@ end_per_group(_GroupName, Config) ->
start_timer_1(doc) -> ["Basic start_timer/3 functionality"];
start_timer_1(Config) when is_list(Config) ->
- ?line Ref1 = erlang:start_timer(1000, self(), plopp),
- ?line ok = get(1100, {timeout, Ref1, plopp}),
-
- ?line false = erlang:read_timer(Ref1),
- ?line false = erlang:cancel_timer(Ref1),
- ?line false = erlang:read_timer(Ref1),
-
- ?line Ref2 = erlang:start_timer(1000, self(), plapp),
- ?line Left2 = erlang:cancel_timer(Ref2),
- UpperLimit = case os:type() of
- vxworks ->
- %% The ticks of vxworks have a far lesser granularity
- %% than what is expected in this testcase, in
- %% fact the Left2 variable can get a little more than 1000...
- 1100;
- _ ->
- 1000
- end,
- ?line RetVal = case os:type() of
- vxworks ->
- {comment, "VxWorks behaves slightly unexpected, should be fixed,"};
- _ ->
- ok
- end,
- ?line true = (Left2 > 900) and (Left2 =< UpperLimit),
- ?line empty = get_msg(),
- ?line false = erlang:cancel_timer(Ref2),
-
- ?line Ref3 = erlang:start_timer(1000, self(), plopp),
- ?line no_message = get(900, {timeout, Ref3, plopp}),
-
- RetVal.
+ Ref1 = erlang:start_timer(1000, self(), plopp),
+ ok = get(1100, {timeout, Ref1, plopp}),
+
+ false = erlang:read_timer(Ref1),
+ false = erlang:cancel_timer(Ref1),
+ false = erlang:read_timer(Ref1),
+
+ Ref2 = erlang:start_timer(1000, self(), plapp),
+ Left2 = erlang:cancel_timer(Ref2),
+ UpperLimit = 1000,
+ true = (Left2 > 900) 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}),
+ ok.
send_after_1(doc) -> ["Basic send_after/3 functionality"];
send_after_1(Config) when is_list(Config) ->
@@ -153,19 +138,11 @@ send_after_2(Config) when is_list(Config) ->
send_after_3(doc) -> ["send_after/3: messages in the right order, worse than send_after_2"];
send_after_3(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skipped, "VxWorks timer granularity and order is not working good, this is subject to change!"};
- _ ->
- do_send_after_3()
- end.
-
-do_send_after_3() ->
- ?line _ = erlang:send_after(100, self(), b1),
- ?line _ = erlang:send_after(101, self(), b2),
- ?line _ = erlang:send_after(102, self(), b3),
- ?line _ = erlang:send_after(103, self(), last),
- ?line [b1, b2, b3, last] = collect(last),
+ _ = erlang:send_after(100, self(), b1),
+ _ = erlang:send_after(101, self(), b2),
+ _ = erlang:send_after(102, self(), b3),
+ _ = erlang:send_after(103, self(), last),
+ [b1, b2, b3, last] = collect(last),
% This behaviour is not guaranteed:
% ?line _ = erlang:send_after(100, self(), c1),
diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl
index 32e2a98e3c..1e0705fabe 100644
--- a/erts/emulator/test/trace_local_SUITE.erl
+++ b/erts/emulator/test/trace_local_SUITE.erl
@@ -70,7 +70,8 @@ config(priv_dir,_) ->
-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, stack_grow/1,info/1, delete/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,
@@ -80,6 +81,7 @@ config(priv_dir,_) ->
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)),
@@ -89,14 +91,23 @@ end_per_testcase(_Case, Config) ->
shutdown(),
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
- ok.
+
+ %% Reloading the module will clear all trace patterns, and
+ %% in a debug-compiled emulator run assertions of the counters
+ %% for the number of functions with breakpoints.
+
+ c:l(?MODULE).
+
+
+
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
case test_server:is_native(trace_local_SUITE) of
true -> [not_run];
false ->
- [basic, bit_syntax, return, on_and_off, stack_grow,
+ [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,
@@ -106,7 +117,8 @@ all() ->
exception_meta_apply_function, exception_meta_nocatch,
exception_meta_nocatch_apply,
exception_meta_nocatch_function,
- exception_meta_nocatch_apply_function]
+ exception_meta_nocatch_apply_function,
+ concurrency]
end.
groups() ->
@@ -350,7 +362,8 @@ same(A, B) ->
basic_test() ->
?line setup([call]),
- ?line erlang:trace_pattern({?MODULE,'_','_'},[],[local]),
+ 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]),
@@ -572,7 +585,118 @@ on_and_off_test() ->
end,
?line ?NM,
ok.
-
+
+systematic_on_off(Config) when is_list(Config) ->
+ setup([call]),
+ Local = combinations([local,meta,call_count,call_time]),
+ [systematic_on_off_1(Flags) || Flags <- Local],
+
+ %% Make sure that we don't get any trace messages when trace
+ %% is supposed to be off.
+ receive_no_next(500).
+
+systematic_on_off_1(Local) ->
+ io:format("~p\n", [Local]),
+
+ %% Global off.
+ verify_trace_info(false, []),
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, true, Local),
+ verify_trace_info(false, Local),
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, false, [global]),
+ verify_trace_info(false, Local),
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, false, Local),
+ verify_trace_info(false, []),
+
+ %% Global on.
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, true, [global]),
+ verify_trace_info(true, []),
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, false, Local),
+ verify_trace_info(true, []),
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, false, [global]),
+ verify_trace_info(false, []),
+
+ %% Implicitly turn off global call trace.
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, true, [global]),
+ verify_trace_info(true, []),
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, true, Local),
+ verify_trace_info(false, Local),
+
+ %% Implicitly turn off local call trace.
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, true, [global]),
+ verify_trace_info(true, []),
+
+ %% Turn off global call trace. Everything should be off now.
+ 1 = erlang:trace_pattern({?MODULE,exported_wrap,1}, false, [global]),
+ verify_trace_info(false, []),
+
+ ok.
+
+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]
+ end,
+ global_call(Global, Local),
+ local_call(Local),
+ ok.
+
+verify_trace_info({traced,global}, true, []) -> ok;
+verify_trace_info({traced,local}, false, _) -> ok;
+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)
+ catch
+ error:_ ->
+ io:format("Line ~p: {~p,~p}, false, ~p\n",
+ [?LINE,LocalFlag,Bool,Local]),
+ ?t:fail()
+ end;
+verify_trace_info({meta,Pid}, false, Local) when is_pid(Pid) ->
+ true = lists:member(meta, Local);
+verify_trace_info({call_time,_}, false, Local) ->
+ true = lists:member(call_time, Local);
+verify_trace_info({call_count,_}, false, Local) ->
+ true = lists:member(call_time, 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])
+ end.
+
+local_call(Local) ->
+ lambda_slave(fun() -> exported_wrap(local_call) end),
+ recv_local_call(Local, [local_call]).
+
+recv_local_call(Local, Args) ->
+ case lists:member(local, Local) of
+ false ->
+ ok;
+ true ->
+ ?CT(?MODULE, exported_wrap, Args)
+ end,
+ case lists:member(meta, Local) of
+ false ->
+ ok;
+ true ->
+ ?CTT(?MODULE, exported_wrap, Args)
+ end,
+ ok.
+
+combinations([_]=One) ->
+ [One];
+combinations([H|T]) ->
+ Cs = combinations(T),
+ [[H|C] || C <- Cs] ++ Cs.
stack_grow_test() ->
?line setup([call,return_to]),
@@ -703,16 +827,10 @@ exception_test(Opts) ->
?line ok.
exceptions() ->
- ?line Ref = make_ref(),
- ?line N = case os:type() of
- vxworks ->
- ?line 2000; % Limited memory on themachines, not actually
- % VxWorks' fault /PaN
- _ ->
- ?line 200000
- end,
- ?line LiL = seq(1, N-1, N), % Long Improper List
- ?line LL = seq(1, N, []), % Long List
+ Ref = make_ref(),
+ N = 200000,
+ LiL = seq(1, N-1, N), % Long Improper List
+ LL = seq(1, N, []), % Long List
[{{erlang,exit}, [done]},
{{erlang,error}, [1.0]},
{{erlang,error}, [Ref,[]]},
@@ -813,6 +931,42 @@ clean_location({crash,{Reason,Stk0}}) ->
{crash,{Reason,Stk}};
clean_location(Term) -> Term.
+concurrency(_Config) ->
+ N = erlang:system_info(schedulers),
+
+ %% Spawn 2*N processes that spin in a tight infinite loop,
+ %% and one process that will turn on and off local call
+ %% trace on the infinite_loop/0 function. We expect the
+ %% emulator to crash if there is a memory barrier bug or
+ %% if an aligned word-sized write is not atomic.
+
+ Ps0 = [spawn_monitor(fun() -> infinite_loop() end) ||
+ _ <- lists:seq(1, 2*N)],
+ OnAndOff = fun() -> concurrency_on_and_off() end,
+ Ps1 = [spawn_monitor(OnAndOff)|Ps0],
+ ?t: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),
+
+ %% Clean up.
+ [exit(Pid, kill) || {Pid,_} <- Ps],
+ [receive
+ {'DOWN',Ref,process,Pid,killed} -> ok
+ end || {Pid,Ref} <- Ps],
+ erlang:trace_pattern({?MODULE,infinite_loop,0}, false, [local]),
+ ok.
+
+concurrency_on_and_off() ->
+ 1 = erlang:trace_pattern({?MODULE,infinite_loop,0}, true, [local]),
+ 1 = erlang:trace_pattern({?MODULE,infinite_loop,0}, false, [local]),
+ concurrency_on_and_off().
+
+infinite_loop() ->
+ infinite_loop().
+
%%% Tracee target functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%
diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl
index f81cab3114..cc2eadafbc 100644
--- a/erts/emulator/test/trace_port_SUITE.erl
+++ b/erts/emulator/test/trace_port_SUITE.erl
@@ -472,14 +472,9 @@ default_tracer(Config) when is_list(Config) ->
?line M = N,
ok.
-
%%% Help functions.
-huge_data() ->
- case os:type() of
- vxworks -> huge_data(4711);
- _ -> huge_data(16384)
- end.
+huge_data() -> huge_data(16384).
huge_data(0) -> [];
huge_data(N) when N rem 2 == 0 ->
P = huge_data(N div 2),
diff --git a/erts/emulator/utils/make_preload b/erts/emulator/utils/make_preload
index 13019d4062..fade9829ca 100755
--- a/erts/emulator/utils/make_preload
+++ b/erts/emulator/utils/make_preload
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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
@@ -87,6 +87,7 @@ foreach $file (@ARGV) {
my $comment = '';
open(FILE, $file) or error("failed to read $file: $!");
+ binmode(FILE);
$_ = <FILE>;
$_ = beam_strip($_);
close(FILE);
diff --git a/erts/emulator/utils/make_tables b/erts/emulator/utils/make_tables
index 91efb4c023..a841f26d6a 100755
--- a/erts/emulator/utils/make_tables
+++ b/erts/emulator/utils/make_tables
@@ -167,7 +167,6 @@ typedef struct bif_entry {
extern BifEntry bif_table[];
extern Export* bif_export[];
-extern unsigned char erts_bif_trace_flags[];
#define BIF_SIZE $bif_size
@@ -197,7 +196,6 @@ includes("export.h", "sys.h", "erl_vm.h", "erl_process.h", "bif.h",
"erl_bif_table.h", "erl_atom_table.h");
print "\nExport* bif_export[BIF_SIZE];\n";
-print "unsigned char erts_bif_trace_flags[BIF_SIZE];\n\n";
print "BifEntry bif_table[] = {\n";
for ($i = 0; $i < @bif; $i++) {
diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c
index 2267f9b12b..3577abf6ba 100644
--- a/erts/epmd/src/epmd.c
+++ b/erts/epmd/src/epmd.c
@@ -64,7 +64,7 @@ int epmd_dbg(int level,int port) /* Utility to debug epmd... */
if(port)
{
argv[argc++] = "-port";
- sprintf(ibuff,"%d",port);
+ erts_snprintf(ibuff, sizeof(ibuff), "%d",port);
argv[argc++] = ibuff;
}
argv[argc] = NULL;
diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c
index da575affa1..36565b7438 100644
--- a/erts/epmd/src/epmd_srv.c
+++ b/erts/epmd/src/epmd_srv.c
@@ -23,6 +23,7 @@
#endif
#include "epmd.h" /* Renamed from 'epmd_r4.h' */
#include "epmd_int.h"
+#include "erl_printf.h" /* erts_snprintf */
#ifndef INADDR_NONE
# define INADDR_NONE 0xffffffff
@@ -633,7 +634,7 @@ static void do_request(g, fd, s, buf, bsize)
/* CAREFUL!!! These are parsed by "erl_epmd.erl" so a slight
change in syntax will break < OTP R3A */
- sprintf(wbuf,"name %s at port %d\n",node->symname, node->port);
+ erts_snprintf(wbuf, sizeof(wbuf), "name %s at port %d\n",node->symname, node->port);
len = strlen(wbuf);
if (reply(g, fd, wbuf, len) != len)
{
@@ -669,7 +670,7 @@ static void do_request(g, fd, s, buf, bsize)
/* CAREFUL!!! These are parsed by "erl_epmd.erl" so a slight
change in syntax will break < OTP R3A */
- sprintf(wbuf,"active name <%s> at port %d, fd = %d\n",
+ erts_snprintf(wbuf, sizeof(wbuf), "active name <%s> at port %d, fd = %d\n",
node->symname, node->port, node->fd);
len = strlen(wbuf) + 1;
if (reply(g, fd,wbuf,len) != len)
@@ -686,7 +687,7 @@ static void do_request(g, fd, s, buf, bsize)
/* CAREFUL!!! These are parsed by "erl_epmd.erl" so a slight
change in syntax will break < OTP R3A */
- sprintf(wbuf,"old/unused name <%s>, port = %d, fd = %d \n",
+ erts_snprintf(wbuf, sizeof(wbuf), "old/unused name <%s>, port = %d, fd = %d \n",
node->symname,node->port, node->fd);
len = strlen(wbuf) + 1;
if (reply(g, fd,wbuf,len) != len)
diff --git a/erts/epmd/test/Makefile b/erts/epmd/test/Makefile
index 868b1c023b..45c8be6809 100644
--- a/erts/epmd/test/Makefile
+++ b/erts/epmd/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2010. All Rights Reserved.
+# Copyright Ericsson AB 1998-2012. 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/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl
index 3f31cd979c..fd9969ae2b 100644
--- a/erts/epmd/test/epmd_SUITE.erl
+++ b/erts/epmd/test/epmd_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2012. 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/etc/common/Makefile.in b/erts/etc/common/Makefile.in
index b10f5d9a87..ea70946346 100644
--- a/erts/etc/common/Makefile.in
+++ b/erts/etc/common/Makefile.in
@@ -33,11 +33,7 @@ else
ifeq ($(TYPE),purify)
PURIFY = purify
TYPEMARKER =
-ifeq ($(findstring ose,$(TARGET)),ose)
-TYPE_FLAGS = -g -XO -DPURIFY
-else
TYPE_FLAGS = -g -O2 -DPURIFY
-endif
else
override TYPE=opt
@@ -64,6 +60,9 @@ LD = @LD@
LIBS = @LIBS@
LDFLAGS = @LDFLAGS@
+# For clock_gettime in heart
+RTLIBS = @LIBRT@
+
ifeq ($(TARGET),win32)
ifeq ($(TYPE),debug)
CFLAGS = $(subst -O2,-g,@CFLAGS@ @DEFS@ $(TYPE_FLAGS) @WFLAGS@ -I$(SYSDIR) \
@@ -77,25 +76,15 @@ EMUDIR = $(ERL_TOP)/erts/emulator/beam
EMUOSDIR = $(ERL_TOP)/erts/emulator/@ERLANG_OSTYPE@
SYSDIR = $(ERL_TOP)/erts/emulator/sys/@ERLANG_OSTYPE@
DRVDIR = $(ERL_TOP)/erts/emulator/drivers/@ERLANG_OSTYPE@
-VXETC = ../vxworks
UXETC = ../unix
OSEETC = ../ose
WINETC = ../win32
-ifeq ($(findstring vxworks,$(TARGET)), vxworks)
-ERLEXEC = erl.exec
-else
-ifeq ($(findstring ose,$(TARGET)), ose)
-ERLEXEC =
-TAR = @TAR@
-else
ifeq ($(TARGET), win32)
ERLEXEC = erlexec.dll
else
ERLEXEC = erlexec
endif
-endif
-endif
# On windows we always need reentrant libraries.
ifeq ($(TARGET),win32)
@@ -110,42 +99,6 @@ ERTS_LIB = $(ERL_TOP)/erts/lib_src/obj/$(TARGET)/$(TYPE)/MADE
# Release directory specification
# ----------------------------------------------------
-ifeq ($(findstring vxworks,$(TARGET)), vxworks)
-INSTALL_EMBEDDED_PROGS = $(BINDIR)/erl_io $(BINDIR)/rdate $(BINDIR)/vxcall
-INSTALL_EMBEDDED_DATA = $(BINDIR)/erl_script.sam $(VXETC)/resolv.conf
-INSTALL_INCLUDES = $(VXETC)/reclaim.h
-INSTALL_TOP = $(VXETC)/README.VxWorks
-INSTALL_MISC =
-INSTALL_SRC = heart.c $(VXETC)/heart_config.h $(VXETC)/heart_config.c \
- $(VXETC)/erl.exec.c $(VXETC)/rdate.c $(VXETC)/vxcall.c \
- $(VXETC)/erl_io.c
-ERLEXECDIR = $(VXETC)
-INSTALL_LIBS = $(OBJDIR)/reclaim.o
-INSTALL_OBJS = $(OBJDIR)/heart.o
-TEXTFILES = $(BINDIR)/erl_script.sam
-ERLSRV_OBJECTS=
-MC_OUTPUTS=
-ENTRY_LDFLAGS=
-ENTRY_OBJ=
-INSTALL_PROGS = \
- $(INET_GETHOST) \
- $(BINDIR)/heart \
- $(BINDIR)/$(ERLEXEC) \
- $(INSTALL_EMBEDDED_PROGS)
-else
-ifeq ($(findstring ose,$(TARGET)), ose)
-INSTALL_TOP = $(OSEETC)/README.OSE
-INSTALL_ERL_OSE = monolith lm erl_utils drivers port_progs host
-INSTALL_SRC =
-INSTALL_LIBS =
-INSTALL_OBJS =
-INSTALL_INCLUDES =
-INSTALL_PROGS =
-ERLSRV_OBJECTS=
-MC_OUTPUTS=
-ENTRY_LDFLAGS=
-ENTRY_OBJ=
-else
ifeq ($(TARGET),win32)
CFLAGS += -I$(EMUOSDIR) -I$(WINETC)
RC=rc.sh
@@ -207,7 +160,7 @@ endif
PORT_ENTRY_POINT=erl_port_entry
ENTRY_LDFLAGS=-entry:$(PORT_ENTRY_POINT)
-else
+else # UNIX (!win32)
ENTRY_LDFLAGS=
ENTRY_OBJ=
ERLSRV_OBJECTS=
@@ -232,8 +185,6 @@ INSTALL_PROGS = \
$(BINDIR)/$(ERLEXEC) \
$(INSTALL_EMBEDDED_PROGS)
endif
-endif
-endif
.PHONY: etc
etc: $(ENTRY_OBJ) $(INSTALL_PROGS) $(INSTALL_LIBS) $(TEXTFILES) $(INSTALL_TOP_BIN)
@@ -396,56 +347,13 @@ endif
# End of windows specific targets.
#---------------------------------------------------------
-ifeq ($(findstring vxworks,$(TARGET)), vxworks)
-$(BINDIR)/heart: $(OBJDIR)/heart.o $(OBJDIR)/heart_config.o
- $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/heart.o $(OBJDIR)/heart_config.o
-
-$(OBJDIR)/heart_config.o: $(VXETC)/heart_config.c
- $(CC) $(CFLAGS) -o $@ -c $(VXETC)/heart_config.c
-
-$(OBJDIR)/reclaim.o: $(VXETC)/reclaim.c
- $(CC) $(CFLAGS) -o $@ -c $(VXETC)/reclaim.c
-
-$(OBJDIR)/heart.o: heart.c
- $(CC) $(CFLAGS) -I$(VXETC) -o $@ -c heart.c
-
-$(BINDIR)/erl_script.sam: $(VXETC)/erl_script.sam.in ../../vsn.mk
- sed -e 's;%VSN%;$(VSN);' \
- $(VXETC)/erl_script.sam.in > $(BINDIR)/erl_script.sam
-else
-
$(BINDIR)/heart@EXEEXT@: $(OBJDIR)/heart.o $(ENTRY_OBJ)
$(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/heart.o \
- $(ENTRY_OBJ) $(WINDSOCK)
+ $(RTLIBS) $(ENTRY_OBJ) $(WINDSOCK)
$(OBJDIR)/heart.o: heart.c $(RC_GENERATED)
$(CC) $(CFLAGS) -o $@ -c heart.c
-endif
-
-
-# VxWorks specific executables and objects ...
-
-$(BINDIR)/erl_io: $(OBJDIR)/erl_io.o
- $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erl_io.o
-
-$(OBJDIR)/erl_io.o: $(VXETC)/erl_io.c
- $(CC) $(CFLAGS) -o $@ -c $(VXETC)/erl_io.c
-
-$(BINDIR)/rdate: $(OBJDIR)/rdate.o
- $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/rdate.o
-
-$(OBJDIR)/rdate.o: $(VXETC)/rdate.c
- $(CC) $(CFLAGS) -o $@ -c $(VXETC)/rdate.c
-
-$(BINDIR)/vxcall: $(OBJDIR)/vxcall.o
- $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/vxcall.o
-
-$(OBJDIR)/vxcall.o: $(VXETC)/vxcall.c
- $(CC) $(CFLAGS) -o $@ -c $(VXETC)/vxcall.c
-
-
-
#
# Objects & executables
#
diff --git a/erts/etc/common/dialyzer.c b/erts/etc/common/dialyzer.c
index 70b11b1b36..b8a7a2bf03 100644
--- a/erts/etc/common/dialyzer.c
+++ b/erts/etc/common/dialyzer.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2012. 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/etc/common/erlc.c b/erts/etc/common/erlc.c
index 0223cc5274..f63ba3ee64 100644
--- a/erts/etc/common/erlc.c
+++ b/erts/etc/common/erlc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2012. 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
@@ -335,7 +335,7 @@ main(int argc, char** argv)
/* Push the following options:
* o makedep_phony
*/
- buf = strsave("makedep_add_missing");
+ buf = strsave("makedep_phony");
PUSH2("@option", buf);
break;
default:
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
index 790b0ed400..52add1c1ba 100644
--- a/erts/etc/common/erlexec.c
+++ b/erts/etc/common/erlexec.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2012. 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
@@ -245,7 +245,9 @@ static char* config_script = NULL; /* used by option -start_erl and -config */
static HANDLE this_module_handle;
static int run_werl;
-
+static WCHAR *utf8_to_utf16(unsigned char *bytes);
+static char *utf16_to_utf8(WCHAR *wstr);
+static WCHAR *latin1_to_utf16(char *str);
#endif
/*
@@ -263,8 +265,12 @@ static void
set_env(char *key, char *value)
{
#ifdef __WIN32__
- if (!SetEnvironmentVariable((LPCTSTR) key, (LPCTSTR) value))
+ WCHAR *wkey = latin1_to_utf16(key);
+ WCHAR *wvalue = utf8_to_utf16(value);
+ if (!SetEnvironmentVariableW(wkey, wvalue))
error("SetEnvironmentVariable(\"%s\", \"%s\") failed!", key, value);
+ efree(wkey);
+ efree(wvalue);
#else
size_t size = strlen(key) + 1 + strlen(value) + 1;
char *str = emalloc(size);
@@ -277,25 +283,33 @@ set_env(char *key, char *value)
#endif
}
+
static char *
get_env(char *key)
{
#ifdef __WIN32__
DWORD size = 32;
- char *value = NULL;
+ WCHAR *value = NULL;
+ WCHAR *wkey = latin1_to_utf16(key);
+ char *res;
while (1) {
DWORD nsz;
if (value)
efree(value);
- value = emalloc(size);
+ value = emalloc(size*sizeof(WCHAR));
SetLastError(0);
- nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size);
+ nsz = GetEnvironmentVariableW(wkey, value, size);
if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
efree(value);
+ efree(wkey);
return NULL;
}
- if (nsz <= size)
- return value;
+ if (nsz <= size) {
+ efree(wkey);
+ res = utf16_to_utf8(value);
+ efree(value);
+ return res;
+ }
size = nsz;
}
#else
@@ -2080,4 +2094,147 @@ possibly_quote(char* arg)
return narg;
}
+/*
+ * Unicode helpers to handle environment and command line parameters on
+ * Windows. We internally handle all environment variables in UTF8,
+ * but put and get the environment using the WCHAR (limited UTF16) interface
+ *
+ * These are simplified to only handle Unicode characters that can fit in
+ * Windows simplified UTF16, i.e. characters that fit in 16 bits.
+ */
+
+static int utf8_len(unsigned char first)
+{
+ if ((first & ((unsigned char) 0x80)) == 0) {
+ return 1;
+ } else if ((first & ((unsigned char) 0xE0)) == 0xC0) {
+ return 2;
+ } else if ((first & ((unsigned char) 0xF0)) == 0xE0) {
+ return 3;
+ } else if ((first & ((unsigned char) 0xF8)) == 0xF0) {
+ return 4;
+ }
+ return 1; /* will be a '?' */
+}
+
+static WCHAR *utf8_to_utf16(unsigned char *bytes)
+{
+ unsigned int unipoint;
+ unsigned char *tmp = bytes;
+ WCHAR *target, *res;
+ int num = 0;
+
+ while (*tmp) {
+ num++;
+ tmp += utf8_len(*tmp);
+ }
+ res = target = emalloc((num + 1) * sizeof(WCHAR));
+ while (*bytes) {
+ if (((*bytes) & ((unsigned char) 0x80)) == 0) {
+ unipoint = (Uint) *bytes;
+ ++bytes;
+ } else if (((*bytes) & ((unsigned char) 0xE0)) == 0xC0) {
+ unipoint =
+ (((Uint) ((*bytes) & ((unsigned char) 0x1F))) << 6) |
+ ((Uint) (bytes[1] & ((unsigned char) 0x3F)));
+ bytes += 2;
+ } else if (((*bytes) & ((unsigned char) 0xF0)) == 0xE0) {
+ unipoint =
+ (((Uint) ((*bytes) & ((unsigned char) 0xF))) << 12) |
+ (((Uint) (bytes[1] & ((unsigned char) 0x3F))) << 6) |
+ ((Uint) (bytes[2] & ((unsigned char) 0x3F)));
+ if (unipoint > 0xFFFF) {
+ unipoint = (unsigned int) '?';
+ }
+ bytes +=3;
+ } else if (((*bytes) & ((unsigned char) 0xF8)) == 0xF0) {
+ unipoint = (unsigned int) '?'; /* Cannot put in a wchar */
+ bytes += 4;
+ } else {
+ unipoint = (unsigned int) '?';
+ }
+ *target++ = (WCHAR) unipoint;
+ }
+ *target = L'\0';
+ return res;
+}
+
+static int put_utf8(WCHAR ch, unsigned char *target, int sz, int *pos)
+{
+ Uint x = (Uint) ch;
+ if (x < 0x80) {
+ if (*pos >= sz) {
+ return -1;
+ }
+ target[(*pos)++] = (unsigned char) x;
+ }
+ else if (x < 0x800) {
+ if (((*pos) + 1) >= sz) {
+ return -1;
+ }
+ target[(*pos)++] = (((unsigned char) (x >> 6)) |
+ ((unsigned char) 0xC0));
+ target[(*pos)++] = (((unsigned char) (x & 0x3F)) |
+ ((unsigned char) 0x80));
+ } else {
+ if ((x >= 0xD800 && x <= 0xDFFF) ||
+ (x == 0xFFFE) ||
+ (x == 0xFFFF)) { /* Invalid unicode range */
+ return -1;
+ }
+ if (((*pos) + 2) >= sz) {
+ return -1;
+ }
+
+ target[(*pos)++] = (((unsigned char) (x >> 12)) |
+ ((unsigned char) 0xE0));
+ target[(*pos)++] = ((((unsigned char) (x >> 6)) & 0x3F) |
+ ((unsigned char) 0x80));
+ target[(*pos)++] = (((unsigned char) (x & 0x3F)) |
+ ((unsigned char) 0x80));
+ }
+ return 0;
+}
+
+static int need_bytes_for_utf8(WCHAR x)
+{
+ if (x < 0x80)
+ return 1;
+ else if (x < 0x800)
+ return 2;
+ else
+ return 3;
+}
+
+static WCHAR *latin1_to_utf16(char *str)
+{
+ int len = strlen(str);
+ int i;
+ WCHAR *wstr = emalloc((len+1) * sizeof(WCHAR));
+ for(i=0;i<len;++i)
+ wstr[i] = (WCHAR) str[i];
+ wstr[len] = L'\0';
+ return wstr;
+}
+
+static char *utf16_to_utf8(WCHAR *wstr)
+{
+ int len = wcslen(wstr);
+ char *result;
+ int i,pos;
+ int reslen = 0;
+ for(i=0;i<len;++i) {
+ reslen += need_bytes_for_utf8(wstr[i]);
+ }
+ result = emalloc(reslen+1);
+ pos = 0;
+ for(i=0;i<len;++i) {
+ if (put_utf8((int) wstr[i], result, reslen, &pos) < 0) {
+ break;
+ }
+ }
+ result[pos] = '\0';
+ return result;
+}
+
#endif
diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c
index 755e308219..7b78cc489d 100644
--- a/erts/etc/common/heart.c
+++ b/erts/etc/common/heart.c
@@ -66,8 +66,7 @@
* input and output file descriptors (0 and 1). These descriptors
* (and the standard error descriptor 2) must NOT be closed
* explicitely by this program at termination (in UNIX it is
- * taken care of by the operating system itself; in VxWorks
- * it is taken care of by the spawn driver part of the Emulator).
+ * taken care of by the operating system itself).
*
* END OF FILE
*
@@ -75,12 +74,6 @@
* that there is no process at the other end of the connection
* having the connection open for writing (end-of-file).
*
- * HARDWARE WATCHDOG
- *
- * When used with VxWorks(with CPU40), the hardware
- * watchdog is enabled, making sure that the system reboots
- * even if the heart port program malfunctions or the system
- * is completely overloaded.
*/
#ifdef HAVE_CONFIG_H
@@ -93,18 +86,12 @@
#include <fcntl.h>
#include <process.h>
#endif
-#ifdef VXWORKS
-#include "sys.h"
-#endif
/*
* Implement time correction using times() call even on Linuxes
* that can simulate gethrtime with clock_gettime, no use implementing
* a phony gethrtime in this file as the time questions are so infrequent.
*/
-#if defined(CORRET_USING_TIMES) || defined(GETHRTIME_WITH_CLOCK_GETTIME)
-# define HEART_CORRECT_USING_TIMES 1
-#endif
#include <stdio.h>
#include <stddef.h>
@@ -116,25 +103,13 @@
#include <time.h>
#include <errno.h>
-#ifdef VXWORKS
-# include <vxWorks.h>
-# include <ioLib.h>
-# include <selectLib.h>
-# include <netinet/in.h>
-# include <rebootLib.h>
-# include <sysLib.h>
-# include <taskLib.h>
-# include <wdLib.h>
-# include <taskHookLib.h>
-# include <selectLib.h>
-#endif
-#if !defined(__WIN32__) && !defined(VXWORKS)
+#if !defined(__WIN32__)
# include <sys/types.h>
# include <netinet/in.h>
# include <sys/time.h>
# include <unistd.h>
# include <signal.h>
-# if defined(HEART_CORRECT_USING_TIMES)
+# if defined(CORRECT_USING_TIMES)
# include <sys/times.h>
# include <limits.h>
# endif
@@ -447,7 +422,8 @@ message_loop(erlin_fd, erlout_fd)
*/
timestamp(&now);
if (now > last_received + heart_beat_timeout) {
- print_error("heart-beat time-out.");
+ print_error("heart-beat time-out, no activity for %lu seconds",
+ (unsigned long) (now - last_received));
return R_TIMEOUT;
}
/*
@@ -550,8 +526,7 @@ kill_old_erlang(void){
CloseHandle(erlh);
}
}
-#elif !defined(VXWORKS)
-/* Unix eh? */
+#else
static void
kill_old_erlang(void){
pid_t pid;
@@ -570,7 +545,7 @@ kill_old_erlang(void){
}
}
}
-#endif /* Not on VxWorks */
+#endif
#ifdef __WIN32__
void win_system(char *command)
@@ -653,7 +628,7 @@ do_terminate(reason)
case R_ERROR:
case R_CLOSED:
default:
-#if defined(__WIN32__) /* Not VxWorks */
+#if defined(__WIN32__)
{
if(!cmd[0]) {
char *command = get_env(HEART_COMMAND_ENV);
@@ -1026,60 +1001,30 @@ time_t timestamp(time_t *res)
return r;
}
-#elif defined(VXWORKS)
-
-static WDOG_ID watchdog_id;
-static volatile unsigned elapsed;
-static WIND_TCB *this_task;
-/* A simple variable is enough to lock the time update, as the
- watchdog is run at interrupt level and never preempted. */
-static volatile int lock_time;
-
-static void my_delete_hook(WIND_TCB *tcb)
-{
- if (tcb == this_task) {
- wdDelete(watchdog_id);
- watchdog_id = NULL;
- taskDeleteHookDelete((FUNCPTR) &my_delete_hook);
- }
-}
+#elif defined(HAVE_GETHRTIME) || defined(GETHRTIME_WITH_CLOCK_GETTIME)
-static void my_wd_routine(int count)
-{
- if (watchdog_id != NULL) {
- ++count;
- if (!lock_time) {
- elapsed += count;
- count = 0;
- }
- wdStart(watchdog_id, sysClkRateGet(),
- (FUNCPTR) &my_wd_routine, count);
- }
-}
+#if defined(GETHRTIME_WITH_CLOCK_GETTIME)
+typedef long long SysHrTime;
-void init_timestamp(void)
-{
- lock_time = 0;
- elapsed = 0;
- watchdog_id = wdCreate();
- this_task = (WIND_TCB *) taskIdSelf();
- taskDeleteHookAdd((FUNCPTR) &my_delete_hook);
- wdStart(watchdog_id, sysClkRateGet(),
- (FUNCPTR) &my_wd_routine, 0);
-}
+SysHrTime sys_gethrtime(void);
-time_t timestamp(time_t *res)
+SysHrTime sys_gethrtime(void)
{
- time_t r;
- ++lock_time;
- r = (time_t) elapsed;
- --lock_time;
- if (res != NULL)
- *res = r;
- return r;
+ struct timespec ts;
+ long long result;
+ if (clock_gettime(CLOCK_MONOTONIC,&ts) != 0) {
+ print_error("Fatal, could not get clock_monotonic value, terminating! "
+ "errno = %d\n", errno);
+ exit(1);
+ }
+ result = ((long long) ts.tv_sec) * 1000000000LL +
+ ((long long) ts.tv_nsec);
+ return (SysHrTime) result;
}
-
-#elif defined(HAVE_GETHRTIME)
+#else
+typedef hrtime_t SysHrTime;
+#define sys_gethrtime() gethrtime()
+#endif
void init_timestamp(void)
{
@@ -1087,14 +1032,14 @@ void init_timestamp(void)
time_t timestamp(time_t *res)
{
- hrtime_t ht = gethrtime();
+ SysHrTime ht = sys_gethrtime();
time_t r = (time_t) (ht / 1000000000);
if (res != NULL)
*res = r;
return r;
}
-#elif defined(HEART_CORRECT_USING_TIMES)
+#elif defined(CORRECT_USING_TIMES)
# ifdef NO_SYSCONF
# include <sys/param.h>
diff --git a/erts/etc/common/inet_gethost.c b/erts/etc/common/inet_gethost.c
index e923233ce9..b9a0e6bde3 100644
--- a/erts/etc/common/inet_gethost.c
+++ b/erts/etc/common/inet_gethost.c
@@ -2522,7 +2522,7 @@ static char *format_address(int siz, AddrByte *addr)
*buff='\0';
if (siz <= 4) {
while(siz--) {
- sprintf(tmp,"%d",(int) *addr++);
+ erts_snprintf(tmp, sizeof(tmp), "%d",(int) *addr++);
strcat(buff,tmp);
if(siz) {
strcat(buff,".");
@@ -2531,7 +2531,7 @@ static char *format_address(int siz, AddrByte *addr)
return buff;
}
while(siz--) {
- sprintf(tmp,"%02x",(int) *addr++);
+ erts_snprintf(tmp, sizeof(tmp), "%02x",(int) *addr++);
strcat(buff,tmp);
if(siz) {
strcat(buff,":");
@@ -2548,9 +2548,9 @@ static void debugf(char *format, ...)
va_start(ap,format);
#ifdef WIN32
- sprintf(buff,"%s[%d] (DEBUG):",program_name,(int) GetCurrentThreadId());
+ erts_snprintf(buff, sizeof(buff), "%s[%d] (DEBUG):",program_name,(int) GetCurrentThreadId());
#else
- sprintf(buff,"%s[%d] (DEBUG):",program_name,(int) getpid());
+ erts_snprintf(buff, sizeof(buff), "%s[%d] (DEBUG):",program_name,(int) getpid());
#endif
ptr = buff + strlen(buff);
erts_vsnprintf(ptr,sizeof(buff)-strlen(buff)-2,format,ap);
@@ -2574,7 +2574,7 @@ static void warning(char *format, ...)
va_list ap;
va_start(ap,format);
- sprintf(buff,"%s[%d]: WARNING:",program_name, (int) getpid());
+ erts_snprintf(buff, sizeof(buff), "%s[%d]: WARNING:",program_name, (int) getpid());
ptr = buff + strlen(buff);
erts_vsnprintf(ptr,sizeof(buff)-strlen(buff)-2,format,ap);
strcat(ptr,"\r\n");
@@ -2597,7 +2597,7 @@ static void fatal(char *format, ...)
va_list ap;
va_start(ap,format);
- sprintf(buff,"%s[%d]: FATAL ERROR:",program_name, (int) getpid());
+ erts_snprintf(buff, sizeof(buff), "%s[%d]: FATAL ERROR:",program_name, (int) getpid());
ptr = buff + strlen(buff);
erts_vsnprintf(ptr,sizeof(buff)-strlen(buff)-2,format,ap);
strcat(ptr,"\r\n");
diff --git a/erts/etc/unix/Install.src b/erts/etc/unix/Install.src
index 58f7b38ed0..2dcd070a6d 100644
--- a/erts/etc/unix/Install.src
+++ b/erts/etc/unix/Install.src
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2010. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/etc/unix/cerl.src b/erts/etc/unix/cerl.src
index 6a431ce4da..e0d7404de7 100644
--- a/erts/etc/unix/cerl.src
+++ b/erts/etc/unix/cerl.src
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. 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
@@ -302,7 +302,7 @@ else
# Set annotation level for gdb in emacs 22 and higher.
emacs_major=`$EMACS --version | head -1 | sed 's,^[^0-9]*\([0-9]*\).*,\1,g'`
if [ '!' -z "$emacs_major" -a $emacs_major -gt 21 ]; then
- GDBARGS="--annotate=3 "
+ GDBARGS="--annotate=1 "
fi
gdbcmd="$gdbcmd $GDBBP \
(insert-string \"source $ROOTDIR/erts/etc/unix/etp-commands\") \
diff --git a/erts/etc/unix/erl.src.src b/erts/etc/unix/erl.src.src
index fa187c5509..ce5d2b5def 100644
--- a/erts/etc/unix/erl.src.src
+++ b/erts/etc/unix/erl.src.src
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/etc/unix/etp-commands b/erts/etc/unix/etp-commands
index 1c886620bb..d98505c0ff 100644
--- a/erts/etc/unix/etp-commands
+++ b/erts/etc/unix/etp-commands
@@ -1022,16 +1022,17 @@ define etp-cp-1
# Non-reentrant
#
set $etp_cp = (Eterm)($arg0)
- set $etp_cp_low = modules
- set $etp_cp_high = $etp_cp_low + num_loaded_modules
- set $etp_cp_mid = mid_module
+ set $etp_ranges = &r[(int)the_active_code_index]
+ set $etp_cp_low = $etp_ranges->modules
+ set $etp_cp_high = $etp_cp_low + $etp_ranges->n
+ set $etp_cp_mid = (Range*)$etp_ranges->mid
set $etp_cp_p = 0
#
while $etp_cp_low < $etp_cp_high
if $etp_cp < $etp_cp_mid->start
set $etp_cp_high = $etp_cp_mid
else
- if $etp_cp > $etp_cp_mid->end
+ if $etp_cp > (BeamInstr*)$etp_cp_mid->end
set $etp_cp_low = $etp_cp_mid + 1
else
set $etp_cp_p = $etp_cp_low = $etp_cp_high = $etp_cp_mid
@@ -1263,7 +1264,684 @@ document etpf-stackdump
%---------------------------------------------------------------------------
end
+define etp-pix2proc
+# Args: Eterm
+#
+ set $proc = (Process *) *((UWord *) &erts_proc.tab[((int) $arg0)])
+ printf "(Process *) %p\n", $proc
+end
+
+define etp-pid2proc-1
+# Args: Eterm
+#
+ set $pix = (int) ($arg0 >> 4)
+
+ if (erts_proc.pix_cl_mask != 0)
+ set $proc = (Process *) *((UWord *) &erts_proc.tab[(((($pix) & erts_proc.pix_cl_mask) << erts_proc.pix_cl_shift) + ((($pix) >> erts_proc.pix_cli_shift) & erts_proc.pix_cli_mask))])
+ else
+ set $proc =(Process *) *((UWord *) &erts_proc.tab[((($pix) % erts_proc.max) % erts_proc.tab_cache_lines + (($pix) % erts_proc.max) / erts_proc.tab_cache_lines)])
+ end
+end
+
+define etp-pid2proc
+# Args: Eterm
+#
+ etp-pid2proc-1 $arg0
+ printf "(Process *) %p\n", $proc
+end
+
+define etp-proc-state-int
+# Args: int
+#
+ if ($arg0 & 0xfffff000)
+ printf "GARBAGE | "
+ end
+ if ($arg0 & 0x800)
+ printf "trapping-exit | "
+ end
+ if ($arg0 & 0x400)
+ printf "bound | "
+ end
+ if ($arg0 & 0x200)
+ printf "garbage-collecting | "
+ end
+ if ($arg0 & 0x100)
+ printf "suspended | "
+ end
+ if ($arg0 & 0x80)
+ printf "running | "
+ end
+ if ($arg0 & 0x40)
+ printf "in-run-queue | "
+ end
+ if ($arg0 & 0x20)
+ printf "active | "
+ end
+ if ($arg0 & 0x10)
+ printf "pending-exit | "
+ end
+ if ($arg0 & 0x8)
+ printf "exiting | "
+ end
+ if ($arg0 & 0x4)
+ printf "free | "
+ end
+ if ($arg0 & 0x3) == 0
+ printf "prio-max\n"
+ else
+ if ($arg0 & 0x3) == 1
+ printf "prio-high\n"
+ else
+ if ($arg0 & 0x3) == 2
+ printf "prio-normal\n"
+ else
+ printf "prio-low\n"
+ end
+ end
+ end
+end
+
+document etp-proc-state-int
+%---------------------------------------------------------------------------
+% etp-proc-state-int int
+%
+% Print state of process state value
+%---------------------------------------------------------------------------
+end
+
+
+define etp-proc-state
+# Args: Process*
+#
+ set $state_int = *(((Uint32 *) &(((Process *) $arg0)->state)))
+ etp-proc-state-int $state_int
+end
+
+document etp-proc-state
+%---------------------------------------------------------------------------
+% etp-proc-state Process*
+%
+% Print state of process
+%---------------------------------------------------------------------------
+end
+
+define etp-process-info
+# Args: Process*
+#
+ printf " Pid: "
+ etp-1 $arg0->id
+ printf "\n State: "
+ etp-proc-state $arg0
+ if ($arg0->reg)
+ printf " Registered name: "
+ etp-1 $arg0->reg->name
+ printf "\n"
+ end
+ if ($arg0->current)
+ printf " Current function: "
+ etp-1 $arg0->current[0]
+ printf ":"
+ etp-1 $arg0->current[1]
+ printf "/%d\n", $arg0->current[2]
+ end
+ if ($arg0->cp)
+ printf " CP: "
+ etp-cp-1 $arg0->cp
+ printf "\n"
+ end
+ if ($arg0->i)
+ printf " I: "
+ etp-cp-1 $arg0->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
+ end
+ printf " Mbuf size: %ld\n", $arg0->mbuf_sz
+ if (etp_smp_compiled)
+ printf " Msgq len: %ld (inner=%ld, outer=%ld)\n", ($arg0->msg.len + $arg0->u.alive.msg_inq.len), $arg0->msg.len, $arg0->u.alive.msg_inq.len
+ else
+ printf " Msgq len: %d\n", $arg0->msg.len
+ end
+ printf " Parent: "
+ etp-1 $arg0->parent
+ printf "\n Pointer: (Process *) %p\n", $arg0
+end
+
+document etp-process-info
+%---------------------------------------------------------------------------
+% etp-process-info Process*
+%
+% Print info about process
+%---------------------------------------------------------------------------
+end
+
+define etp-processes
+ if (!erts_initialized)
+ printf "No processes, since system isn't initialized!\n"
+ else
+ set $proc_ix = 0
+ while $proc_ix < erts_proc.max
+ set $proc = (Process *) *((UWord *) &erts_proc.tab[$proc_ix])
+ if ($proc != (Process *) 0)
+ printf "---\n"
+ printf " Pix: %d\n", $proc_ix
+ etp-process-info $proc
+ end
+ set $proc_ix++
+ end
+ printf "---\n",
+ end
+end
+
+document etp-processes
+%---------------------------------------------------------------------------
+% etp-processes
+%
+% Print misc info about all processes
+%---------------------------------------------------------------------------
+end
+
+
+define etp-port-status-int
+# Args: int
+#
+ if ($arg0 & 0x1)
+ printf " connected"
+ end
+ if ($arg0 & 0x2)
+ printf " exiting"
+ end
+ if ($arg0 & 0x4)
+ printf " distribution"
+ end
+ if ($arg0 & 0x8)
+ printf " binary-io"
+ end
+ if ($arg0 & 0x10)
+ printf " soft-eof"
+ end
+ if ($arg0 & 0x20)
+ printf " port-busy"
+ end
+ if ($arg0 & 0x40)
+ printf " closing"
+ end
+ if ($arg0 & 0x80)
+ printf " send-closed"
+ end
+ if ($arg0 & 0x100)
+ printf " linebuf-io"
+ end
+ if ($arg0 & 0x200)
+ printf " immortal"
+ end
+ if ($arg0 & 0x400)
+ printf " free"
+ end
+ if ($arg0 & 0x800)
+ printf " free-scheduled"
+ end
+ if ($arg0 & 0x1000)
+ printf " initializing"
+ end
+ if ($arg0 & 0x2000)
+ printf " port-specific-lock"
+ end
+ if ($arg0 & 0x4000)
+ printf " invalid"
+ end
+ if (etp_debug_compiled)
+ if ($arg0 & 0x7fff8000)
+ printf " GARBAGE"
+ end
+ else
+ if ($arg0 & 0xffff8000)
+ printf " GARBAGE"
+ end
+ end
+ printf "\n"
+end
+
+document etp-port-status-int
+%---------------------------------------------------------------------------
+% etp-proc-state-int int
+%
+% Print port status
+%---------------------------------------------------------------------------
+end
+
+
+define etp-port-status
+# Args: Port*
+#
+ set $status_int = *(((Uint32 *) &(((Port *) $arg0)->status)))
+ etp-port-status-int $status_int
+end
+
+document etp-port-status
+%---------------------------------------------------------------------------
+% etp-proc-state-int Port *
+%
+% Print port status
+%---------------------------------------------------------------------------
+end
+
+define etp-port-info
+# Args: Port*
+#
+ printf " Port: "
+ etp-1 $arg0->id
+ printf "\n Name: %s\n", $arg0->name
+ printf " Status:"
+ etp-port-status $arg0
+ if ($arg0->reg)
+ printf " Registered name: "
+ etp-1 $arg0->reg->name
+ printf "\n"
+ end
+ printf " Connected: "
+ etp-1 $arg0->connected
+ printf "\n Pointer: (Port *) %p\n", $arg0
+end
+
+document etp-port-info
+%---------------------------------------------------------------------------
+% etp-port-info Port*
+%
+% Print info about port
+%---------------------------------------------------------------------------
+end
+
+
+define etp-ports
+ if (!erts_initialized)
+ printf "No ports, since system isn't initialized!\n"
+ else
+ set $port_ix = 0
+ while $port_ix < erts_max_ports
+ set $port = &erts_port[$port_ix]
+ if ($port->status & 0x400) == 0
+ # I.e, not free
+ printf "---\n"
+ printf " Pix: %d\n", $port_ix
+ etp-port-info $port
+ end
+ set $port_ix++
+ end
+ printf "---\n",
+ end
+end
+
+document etp-ports
+%---------------------------------------------------------------------------
+% etp-ports
+%
+% Print misc info about all ports
+%---------------------------------------------------------------------------
+end
+
+define etp-rq-flags-int
+# Args: int
+#
+ if ($arg0 & 0x1f)
+ printf " Queue Mask:"
+ if ($arg0 & 0x1)
+ printf " max"
+ end
+ if ($arg0 & 0x2)
+ printf " high"
+ end
+ if ($arg0 & 0x4)
+ printf " normal"
+ end
+ if ($arg0 & 0x8)
+ printf " low"
+ end
+ if ($arg0 & 0x10)
+ printf " ports"
+ end
+ printf "\n"
+ end
+
+ if ($arg0 & 0x3fe0)
+ printf " Emigrate Mask:"
+ if ($arg0 & 0x20)
+ printf " max"
+ end
+ if ($arg0 & 0x40)
+ printf " high"
+ end
+ if ($arg0 & 0x80)
+ printf " normal"
+ end
+ if ($arg0 & 0x100)
+ printf " low"
+ end
+ if ($arg0 & 0x200)
+ printf " ports"
+ end
+ printf "\n"
+ end
+
+ if ($arg0 & 0x7fc00)
+ printf " Immigrate Mask:"
+ if ($arg0 & 0x400)
+ printf " max"
+ end
+ if ($arg0 & 0x800)
+ printf " high"
+ end
+ if ($arg0 & 0x1000)
+ printf " normal"
+ end
+ if ($arg0 & 0x2000)
+ printf " low"
+ end
+ if ($arg0 & 0x4000)
+ printf " ports"
+ end
+ printf "\n"
+ end
+
+ if ($arg0 & 0xf8000)
+ printf " Evaquate Mask:"
+ if ($arg0 & 0x8000)
+ printf " max"
+ end
+ if ($arg0 & 0x10000)
+ printf " high"
+ end
+ if ($arg0 & 0x20000)
+ printf " normal"
+ end
+ if ($arg0 & 0x40000)
+ printf " low"
+ end
+ if ($arg0 & 0x80000)
+ printf " ports"
+ end
+ printf "\n"
+ end
+
+ if ($arg0 & ~0xfffff)
+ printf " Misc Flags:"
+ if ($arg0 & 0x100000)
+ printf " out-of-work"
+ end
+ if ($arg0 & 0x200000)
+ printf " halftime-out-of-work"
+ end
+ if ($arg0 & 0x400000)
+ printf " suspended"
+ end
+ if ($arg0 & 0x800000)
+ printf " check-cpu-bind"
+ end
+ if ($arg0 & 0x1000000)
+ printf " inactive"
+ end
+ if ($arg0 & 0x2000000)
+ printf " non-empty"
+ end
+ if ($arg0 & 0x4000000)
+ printf " protected"
+ end
+ if ($arg0 & ~0x7ffffff)
+ printf " GARBAGE(0x%x)", ($arg0 & ~0x3ffffff)
+ end
+ printf "\n"
+ end
+end
+
+document etp-rq-flags-int
+%---------------------------------------------------------------------------
+% etp-rq-flags-int
+%
+% Print run queue flags
+%---------------------------------------------------------------------------
+end
+
+define etp-ssi-flags
+# Args: int
+#
+ if ($arg0 & 0x1)
+ printf " sleeping"
+ end
+ if ($arg0 & 0x2)
+ printf " poll"
+ end
+ if ($arg0 & 0x4)
+ printf " tse"
+ end
+ if ($arg0 & 0x8)
+ printf " waiting"
+ end
+ if ($arg0 & 0x10)
+ printf " suspended"
+ end
+ printf "\n"
+end
+
+document etp-ssi-flags
+%---------------------------------------------------------------------------
+% etp-ssi-flags
+% Arg int
+%
+% Print aux work flags
+%---------------------------------------------------------------------------
+end
+
+define etp-aux-work-flags
+# Args: int
+#
+ if ($arg0 & 0x1)
+ printf " delayed-dealloc"
+ end
+ if ($arg0 & 0x2)
+ printf " delayed-dealloc-thr-prgr"
+ end
+ if ($arg0 & 0x4)
+ printf " fix-alloc-dealloc"
+ end
+ if ($arg0 & 0x8)
+ printf " fix-alloc-lower-lim"
+ end
+ if ($arg0 & 0x10)
+ printf " async-ready"
+ end
+ if ($arg0 & 0x20)
+ printf " async-ready-clean"
+ end
+ if ($arg0 & 0x40)
+ printf " misc-work-thr-prgr"
+ end
+ if ($arg0 & 0x80)
+ printf " misc-work"
+ end
+ if ($arg0 & 0x100)
+ printf " check-children"
+ end
+ if ($arg0 & 0x200)
+ printf " set-tmo"
+ end
+ if ($arg0 & 0x400)
+ printf " mseg-cached-check"
+ end
+ if ($arg0 & ~0x7ff)
+ printf " GARBAGE"
+ end
+ printf "\n"
+end
+
+document etp-aux-work-flags
+%---------------------------------------------------------------------------
+% etp-aux-work-flags
+% Arg int
+%
+% Print aux work flags
+%---------------------------------------------------------------------------
+end
+
+define etp-schedulers
+ if (!erts_initialized)
+ printf "No schedulers, since system isn't initialized!\n"
+ else
+ 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
+
+ set $sched_ix++
+ end
+ printf "-------------------\n",
+ end
+end
+
+document etp-schedulers
+%---------------------------------------------------------------------------
+% etp-schedulers
+%
+% Print misc info about all schedulers
+%---------------------------------------------------------------------------
+end
+
+define etp-migration-info
+ set $minfo = (ErtsMigrationPaths *) *((UWord *) &erts_migration_paths)
+ set $rq_ix = 0
+ while $rq_ix < erts_no_run_queues
+ if ($minfo->mpath[$rq_ix])
+ printf "---\n"
+ printf "Run Queue Ix: %d\n", $rq_ix
+ etp-rq-flags-int $minfo->mpath[$rq_ix].flags
+ end
+ set $rq_ix++
+ end
+end
+document etp-migration-info
+%---------------------------------------------------------------------------
+% etp-migration-info
+%
+% Print migration information
+%---------------------------------------------------------------------------
+end
+
+define etp-system-info
+ printf "--------------- System Information ---------------\n"
+ printf "OTP release: %s\n", etp_otp_release
+ printf "ERTS version: %s\n", etp_erts_version
+ printf "Compile date: %s\n", etp_compile_date
+ printf "Arch: %s\n", etp_arch
+ 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"
+ else
+ printf "no\n"
+ end
+ if (etp_smp_compiled)
+ printf "SMP support: yes\n"
+ else
+ printf "SMP support: no\n"
+ end
+ printf "Thread support: "
+ if (etp_thread_compiled)
+ printf "yes\n"
+ else
+ printf "no\n"
+ end
+ printf "Kernel poll: "
+ if (etp_kernel_poll_support)
+ if (!erts_initialized)
+ printf "Supported\n"
+ else
+ if (erts_use_kernel_poll)
+ printf "Supported and used\n"
+ else
+ printf "Supported but not used\n"
+ end
+ end
+ else
+ printf "No support\n"
+ end
+ printf "Debug compiled: "
+ if (etp_debug_compiled)
+ printf "yes\n"
+ else
+ printf "no\n"
+ end
+ printf "Lock checking: "
+ if (etp_lock_check)
+ printf "yes\n"
+ else
+ printf "no\n"
+ end
+ printf "Lock counting: "
+ if (etp_lock_count)
+ printf "yes\n"
+ else
+ printf "no\n"
+ end
+
+ if (!erts_initialized)
+ printf "System not initialized\n"
+ else
+ printf "Node name: "
+ etp-1 erts_this_node->sysname
+ printf "\n"
+ printf "Number of schedulers: %d\n", erts_no_schedulers
+ printf "Number of async-threads: %d\n", erts_async_max_threads
+ end
+ printf "--------------------------------------------------\n"
+end
+
+document etp-system-info
+%---------------------------------------------------------------------------
+% etp-system-info
+%
+% Print general information about the system
+%---------------------------------------------------------------------------
+end
define etp-dictdump
# Args: ProcDict*
@@ -1407,69 +2085,6 @@ document etpf-offheapdump
%---------------------------------------------------------------------------
end
-define etp-print-procs
-# Args: Eterm
-#
-# Non-reentrant
-#
- etp-print-procs-1
-end
-
-define etp-print-procs-1
-# Args: Eterm*
-#
-# Non-reentrant
-#
- set $etp_print_procs_q = erts_max_processes / 10
- set $etp_print_procs_r = erts_max_processes % 10
- set $etp_print_procs_t = 10
- set $etp_print_procs_m = $etp_print_procs_q
- if $etp_print_procs_r > 0
- set $etp_print_procs_m++
- set $etp_print_procs_r--
- end
- set $etp_print_procs_i = 0
- set $etp_print_procs_found = 0
- while $etp_print_procs_i < erts_max_processes
- if process_tab[$etp_print_procs_i]
- printf "%d: ", $etp_print_procs_i
- etp-1 process_tab[$etp_print_procs_i]->id
- printf " "
- etp-1 ((Eterm)(process_tab[$etp_print_procs_i]->i))
- printf " heap=%d/%d(%d)", process_tab[$etp_print_procs_i]->htop - process_tab[$etp_print_procs_i]->heap, \
- process_tab[$etp_print_procs_i]->hend - process_tab[$etp_print_procs_i]->heap, \
- process_tab[$etp_print_procs_i]->hend - process_tab[$etp_print_procs_i]->stop
- printf " old=%d/%d ", process_tab[$etp_print_procs_i]->old_htop - process_tab[$etp_print_procs_i]->old_heap, \
- process_tab[$etp_print_procs_i]->old_hend - process_tab[$etp_print_procs_i]->old_heap
- printf " mbuf_sz=%d ", process_tab[$etp_print_procs_i]->mbuf_sz
- printf " min=%d ", process_tab[$etp_print_procs_i]->min_heap_size
- printf " flags=%x ", process_tab[$etp_print_procs_i]->flags
- printf " msgs=%d ", process_tab[$etp_print_procs_i]->msg.len
- printf "\n"
- end
- set $etp_print_procs_i++
- if $etp_print_procs_i > $etp_print_procs_m
- printf "%% %d%%...\n", $etp_print_procs_t
- set $etp_print_procs_t += 10
- set $etp_print_procs_m += $etp_print_procs_q
- if $etp_print_procs_r > 0
- set $etp_print_procs_m++
- set $etp_print_procs_r--
- end
- end
- end
- printf "%% 100%%.\n"
-end
-
-document etp-print-procs
-%---------------------------------------------------------------------------
-% etp-print-procs Eterm
-%
-% Print some information about ALL processes.
-%---------------------------------------------------------------------------
-end
-
-
define etp-search-heaps
# Args: Eterm
#
@@ -1498,20 +2113,21 @@ define etp-search-heaps-1
end
set $etp_search_heaps_i = 0
set $etp_search_heaps_found = 0
- while $etp_search_heaps_i < erts_max_processes
- if process_tab[$etp_search_heaps_i]
- if (process_tab[$etp_search_heaps_i]->heap <= ($arg0)) && \
- (($arg0) < process_tab[$etp_search_heaps_i]->hend)
+ while $etp_search_heaps_i < erts_proc.max
+ set $proc = (Process *) *((UWord *) &erts_proc.tab[$proc_ix])
+ if $proc
+ if ($proc->heap <= ($arg0)) && \
+ (($arg0) < $proc->hend)
printf "process_tab[%d]->heap+%d\n", $etp_search_heaps_i, \
- ($arg0)-process_tab[$etp_search_heaps_i]->heap
+ ($arg0)-$proc->heap
end
- if (process_tab[$etp_search_heaps_i]->old_heap <= ($arg0)) && \
- (($arg0) <= process_tab[$etp_search_heaps_i]->old_hend)
+ if ($proc->old_heap <= ($arg0)) && \
+ (($arg0) <= $proc->old_hend)
printf "process_tab[%d]->old_heap+%d\n", $etp_search_heaps_i, \
- ($arg0)-process_tab[$etp_search_heaps_i]->old_heap
+ ($arg0)-$proc->old_heap
end
set $etp_search_heaps_cnt = 0
- set $etp_search_heaps_p = process_tab[$etp_search_heaps_i]->mbuf
+ set $etp_search_heaps_p = $proc->mbuf
while $etp_search_heaps_p && ($etp_search_heaps_cnt < $etp_max_depth)
set $etp_search_heaps_cnt++
if (&($etp_search_heaps_p->mem) <= ($arg0)) && \
@@ -1523,7 +2139,7 @@ define etp-search-heaps-1
set $etp_search_heaps_p = $etp_search_heaps_p->next
end
if $etp_search_heaps_p
- printf "process_tab[%d] %% Too many HeapFragments\n", \
+ printf "Process ix=%d %% Too many HeapFragments\n", \
$etp_search_heaps_i
end
end
@@ -2070,7 +2686,7 @@ document etp-init
%---------------------------------------------------------------------------
end
-
etp-init
help etp-init
etp-show
+etp-system-info
diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c
index 6b350e8bd5..910be3dce8 100644
--- a/erts/etc/unix/run_erl.c
+++ b/erts/etc/unix/run_erl.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2012. 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/etc/vxworks/README.VxWorks b/erts/etc/vxworks/README.VxWorks
deleted file mode 100644
index 299e35b513..0000000000
--- a/erts/etc/vxworks/README.VxWorks
+++ /dev/null
@@ -1,350 +0,0 @@
-
- %CopyrightBegin%
-
- Copyright Ericsson AB 1997-2009. 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
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- %CopyrightEnd%
-
------------------------------------------------------------------------
-README, Erlang/OTP R11B for VxWorks on PPC860 and PPC603
------------------------------------------------------------------------
-20060515 -- Patrik Nyblom, [email protected]
-
-R11B is a libraries only release for VxWorks. Only the libraries of
-erl_interface (ei+erl_inteface) and ic are expected to be used. Still
-the whole erlang system is distributed, although no support will be
-given for anything else but the libraries. The information in this
-file still applies to the full erlang distribution and parts of it are
-therefore somewhat irrelevant to commercial users.
-
-
-Included OTP applications
--------------------------
-
-appmon
-asn1
-compiler
-cosEvent
-cosNotification
-cosTime
-cosTransaction
-debugger
-erl_interface
-erts
-eva [1]
-ic
-inets [2]
-jinterface
-kernel
-mesh
-mnemosyne
-mnesia [1]
-mnesia_session
-orber
-os_mon
-pman
-runtime_tools
-sasl
-snmp
-stdlib
-tools
-tv
-
-[1] Only ram_copies work, The VxWorks filesystems are not
- reliable enough for disk_copies to be fully supported.
-[2] CGI scripts do not work on VxWorks.
-
-Omitted applications
---------------------
-
-crypto
-emacs
-etk
-gs
-odbc
-parsetools
-toolbar
-ssl
-megaco
-webtools
-
-As `crypto' and `ssl' provides cryptographic functionality to `inets'
-and `snmp', the latter applications will not handle cryptography on
-VxWorks.
-
-Graphical interfaces
---------------------
-
-For applications using graphical interfaces, only the backend part works.
-
-Compilers
----------
-
-All compilers are expected to be run on a cross host. The VxWorks
-systems memory capabilities are too restricting to allow native
-compiling. The expected host system is a Sun Solaris machine, although
-Erlang compilation may be done on most platforms.
-
-Supported boards and configuration (only libraries supported)
-----------------------------------
-The following boards and configurations are supported:
-
-* Force PowerCore 603 with Force pcore603 BSP and VxWorks 3.5.1 (no
- SENS or SENS 1.1 + SPR23938) and a minimum of 32 Mb memory.
-
-* Force Powercore 750 with Force pcore750 BSP and VxWorks 3.5.1 (no
- SENS or SENS 1.1 + SPR23938) and a minimum of 32 Mb memory.
-
-* PSS Core PPC860 processors, only erl_interface (too small main memory).
-
-Most PowerPC boards with FPU are expected to work, but will need to be
-tested by OTP to be fully supported.
-
-The PPC603 build has been compiled with Wind River's `-mlongcall'
-flag (SPR25893) to support arbitrary function calls across more
-than 32 MB of memory.
-
-The PPC860 (PowerQuicc) has no FPU and requires a separate build.
-
-For Erlang to run, the Wind kernel has to be configured with a minimum
-of these variables defined in config.h (or by the Tornado
-configuration tool):
-
- INCLUDE_ANSI_ALL
- INCLUDE_ENV_VARS
- INCLUDE_EXC_HANDLING
- INCLUDE_EXC_TASK
- INCLUDE_FLOATING_POINT
- INCLUDE_FORMATTED_IO
- INCLUDE_IO_SYSTEM
- INCLUDE_LOADER
- INCLUDE_NETWORK
- INCLUDE_NET_INIT
- INCLUDE_NET_SHOW
- INCLUDE_NET_SYM_TBL or INCLUDE_STANDALONE_SYM_TBL
- INCLUDE_PIPES
- INCLUDE_POSIX_FTRUNC
- INCLUDE_RLOGIN or INCLUDE_TELNET (for pty's only)
- INCLUDE_SELECT
- INCLUDE_SEM_BINARY
- INCLUDE_SEM_COUNTING
- INCLUDE_SEM_MUTEX
- INCLUDE_SHELL (*)
- INCLUDE_SHOW_ROUTINES
- INCLUDE_SIGNALS
- INCLUDE_STARTUP_SCRIPT (*)
- INCLUDE_STDIO
- INCLUDE_SYM_TBL
- INCLUDE_TASK_HOOKS
- INCLUDE_TASK_VARS
- INCLUDE_TTY_DEV
- INCLUDE_UNLOADER
- INCLUDE_NFS or INCLUDE_RAMDRV or INCLUDE_DOSFS (i.e. a file system,
- possibly read-only) (**)
-
-(*) Needed for the example startup script, not actually needed in production if
- erlang is set up by a c routine called from usrConfig.c.
-(**) INCLUDE_NFS usually requires the NFS_USER_ID and NFS_GROUP_ID variables
- to be set in config.h
-
-As an erlang system may open a lot of files, it is recommended to raise the
-default NUM_FILES variable to something like 256 in config.h like this:
- #ifdef NUM_FILES
- #undef NUM_FILES
- #endif
- #define NUM_FILES 256
-
-The SENS stack *has* to be of version 1.1 or higher, 1.0 is *not*
-supported and will not work reliably. Upgrades as well as the patch
-for SPR23938 can be found at www.wrs.com (i.e. WindSurf). Also, the
-following constants in $WIND_BASE/target/h/netBufLib.h has to be
-raised to a value of at least four times the default:
-
- NUM_NET_MBLKS
- NUM_64
- NUM_128
- NUM_256
- NUM_512
- NUM_1024
- NUM_2048
-
- NUM_SYS_64
- NUM_SYS_128
- NUM_SYS_256
- NUM_SYS_512
-
-Use the show routines mbufShow and netStackSysPoolShow to verify that
-these pools are not exhausted.
-
-Installation
-------------
-
-To install Erlang on a VxWorks card, the following knowledge is
-expected:
-
-* VxWorks installation and configuration.
-
-* Network (TCP/IP) configuration.
-
-* Erlang basic operation and configuration.
-
-There is no specific install script for erlang on the VxWorks
-platform. There is however an example VxWorks startup file named
-erts-5.0.1/bin/erl_script.sam under the root of an unpacked
-release. There may of course be other things to do in the start
-script, like using the rdate program in the erlang distribution to get
-a correct date and set the TIMEZONE variable.
-
-Please consult the "Embedded System" documentation for further
-information on installation.
-
-Known Bugs and problems
------------------------
-
-We have found the VxWorks/NFS client file system to be unreliable.
-Important file operations like rename, ftruncate, cd and unlink
-doesn't always work as expected. Therefore more complex file using
-parts of OTP, like DETS and disk based mnesia tables cannot be used
-reliably with NFS. Lowering the NFS cache size (global variable
-nfsCacheSize) to 512 gives a more reliable NFS client, but to disk
-base the mnesia tables over NFS is still not a good idea, especially
-as disk based mnesia tables are not supported on VxWorks. Another
-problem with VxWorks file systems is that the error codes they produce
-are not consistent. We have worked around this problem by mapping the
-codes to POSIX ones, by doing this we make the VxWorks Erlang platform
-behave similarly to the UNIX and Windows implementations.
-
-The rename and ftruncate operations over NFS are emulated using
-copying of files. This is mainly for our own test suites and it is not
-recommended to use file:rename and/or file:ftruncate on NFS file
-systems in production.
-
-Floating point operations is somewhat faulty. For instance, testing
-floating point numbers for equality should be done with care. This is
-actually not a bug, IEEE specifies no equality among floating point
-numbers.
-
-Memory handling
----------------
-
-Please read the erl_set_memory_block(3) manual page in the ERTS
-documentation for information concerning memory handling in the erlang
-emulator. Also please observe that reclaim.o has to be loaded and
-reclaim_init() called before any other erlang routines are loaded and
-started. If one wish to use the resource reclamation routines in other
-programs, refer to the header file in `erts-5.0.1/include/reclaim.h'.
-Including that file in your C source makes malloc/realloc/free and
-open/fopen/socket/close etc be redefined to routines that makes the
-memory and files be free'd/closed when the task exits. Still,
-reclaim_init() *has* to be called before any program that uses this is
-started.
-
-Using heart
------------
-
-The default behavior of the heart object file that is part of the
-distribution is that it reboots the target when the Erlang process
-hasn't given it a heart beat in 60 seconds. The normal heart beat rate
-is one beat per five seconds. This makes an effective "software
-watchdog" but there is really no substitute for the real thing --- a
-hardware watchdog. If you want to add a hardware watchdog to the
-system please contact us for directions. If you want to disable the
-reboot you may set the environment variable HEART_DONT_REBOOT (see the
-example erlang start script, erl). Please note that if you DO want the
-card to reboot you mustn't define HEART_DONT_REBOOT at all. E.g. to
-disable heart reboot you may include the following line in the start
-script (as is indeed the case with the example start script).
-
- putenv "HEART_DONT_REBOOT=1"
-
-A few words on writing port program and dynamically loaded drivers for VxWorks
-------------------------------------------------------------------------------
-
-VxWorks has one name-space for all symbols. This makes it harder to
-write C programs whose global symbols doesn't interfere with each
-other. It is a good rule to avoid all globally visible symbols that
-are not absolutely necessary. Due to these facts we use the following
-naming rules that are crucial to follow. (there are more issues
-involved but the issues described here is a good beginning).
-
-Port programs must have a function with the same name as the object
-file. E.g. if you have an object file named `port_test.o' it must
-contain a globally visible function named `port_test'. This is the
-function that will be called when you output data from Erlang to the
-port. (The object file, in this example, should be named
-`port_test.o', but `port_test' will also do).
-
-Also, in an embedded system, it is recommended to load the port
-program into the system before the port program is used. This is to
-avoid the real time degradation dynamical linking in runtime would
-introduce. Use VxWorks command ld < "port_prg" to accomplish this.
-
-Dynamically linked drivers must have a function with the same name as
-the object file with the added suffix `_init'. We recommend the use of
-the macro DRIVER_INIT in `driver.h'. E.g. if you have an object file
-named `echo_drv.eld' it must contain a globally visible function
-`echo_drv_init'. (The object file, in this example, should be named
-`echo_drv.eld' (`eld' is short for Erlang Loadable Driver), but
-`echo_drv.o' and `echo_drv' will both also do). It is also very
-important to initialize all unused function pointer in the
-`driver_entry' struct to NULL (see example below).
-
-Example of dynamically linked driver
-------------------------------------
-
-#include <stdio.h>
-#include "driver.h"
-
-static int erlang_port;
-static long echo_start();
-static int echo_stop(), echo_read();
-
-static struct driver_entry echo_driver_entry = {
- null_func,
- echo_start,
- echo_stop,
- echo_read,
- null_func,
- null_func,
- "echo_drv",
- null_func
-};
-
-int DRIVER_INIT(echo_drv)(void *handle)
-{
- erlang_port = -1;
-
- echo_driver_entry.handle = handle;
- return (int) &echo_driver_entry;
-}
-
-static long echo_start(long port,char *buf)
-{
- if (erlang_port != -1) {
- return -1;
- }
-
- erlang_port = port;
- return port;
-}
-
-static int echo_read(long port, char *buf, int count)
-{
- return driver_output(erlang_port, buf, count);
-}
-
-static int echo_stop()
-{
- return erlang_port = -1;
-}
diff --git a/erts/etc/vxworks/erl.exec.c b/erts/etc/vxworks/erl.exec.c
deleted file mode 100644
index 6b45ebaa39..0000000000
--- a/erts/etc/vxworks/erl.exec.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2009. 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
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
-/*
- A simpified version of the 'erl.exec' "startup script".
- Called (e.g. from VxWorks shell) with all arguments in a
- single string, e.g.: erl "-name thisnode -s mymod myfunc".
- These arguments are handled as in 'erl.exec':
- -name
- -sname
- -noshell
- -noinput
- anything else is just passed on to the emulator. Note that there
- is no automatic start of epmd, that -oldshell is implicit, and
- that you need to set current directory appropriately if you want
- auto-load of port programs
-*/
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifndef DEFAULT_HOMEDIR /* used if environment HOME isn't set */
-#define DEFAULT_HOMEDIR "/"
-#endif
-
-#define ARGLEN 2048 /* Total length of args passed to erl_main */
-#define ARGMAX 64 /* Max no of "extra" args */
-
-static char *erl_cmd = "erl_main -n ";
-
-static toomuch()
-{
- fprintf(stderr, "erl: Too many arguments\n");
- return(-1);
-}
-
-static toolittle(arg)
-char *arg;
-{
- fprintf(stderr, "erl.exec: Missing argument for %s\n", arg);
- return(-1);
-}
-
-erl_exec(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
-int arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10;
-{
- char *shell = "-oldshell ", *noshell = "",
- *home, *rootdir, *bindir, *progname;
- char cmd[ARGLEN], eargs[ARGLEN], iargs[ARGLEN];
- char *args[ARGMAX], *arglast = NULL, *argp;
- int nargs = 0, len, i;
-
- if ((rootdir = getenv("ROOTDIR")) == NULL ||
- (bindir = getenv("BINDIR")) == NULL ||
- (progname = getenv("PROGNAME")) == NULL) {
- fprintf(stderr, "erl.exec: ROOTDIR, BINDIR, and PROGNAME must be set.");
- return -1;
- }
- eargs[0] = '\0';
- iargs[0] = '\0';
- if ((home = getenv("HOME")) == NULL)
- home = DEFAULT_HOMEDIR;
- argp = strtok_r((char *)arg1, " \t", &arglast);
- while (argp != NULL) {
- if (strcmp(argp, "-name") == 0) {
- if ((argp = strtok_r((char *)NULL, " \t", &arglast)) == NULL)
- return(toolittle("-name"));
- strcat(iargs, "-name ");
- strcat(iargs, argp);
- strcat(iargs, " ");
- } else if (strcmp(argp, "-sname") == 0) {
- if ((argp = strtok_r((char *)NULL, " \t", &arglast)) == NULL)
- return(toolittle("-sname"));
- strcat(iargs, "-sname ");
- strcat(iargs, argp);
- strcat(iargs, " ");
- } else if (strcmp(argp, "-noshell") == 0) {
- strcat(iargs, "-noshell -noinp_shell ");
- } else if (strcmp(argp, "-noinput") == 0) {
- strcat(iargs, "-noshell -noinput ");
- } else {
- if (nargs > ARGMAX - 1)
- return(toomuch());
- args[nargs++] = argp;
- }
- argp = strtok_r((char *)NULL, " \t", &arglast);
- }
- strcpy(cmd, erl_cmd);
- strcat(cmd, eargs);
- strcat(cmd, " -- -root ");
- strcat(cmd, rootdir);
- strcat(cmd, " -progname ");
- strcat(cmd, progname);
- strcat(cmd, " -- ");
- strcat(cmd, "-home ");
- strcat(cmd, home);
- strcat(cmd, " ");
- strcat(cmd, iargs);
-
- len = strlen(cmd);
- for (i = 0; i < nargs; i++) {
- if (len + strlen(args[i]) + 2 >= ARGLEN)
- return(toomuch());
- cmd[len++] = ' ';
- strcpy(&cmd[len], args[i]);
- len += strlen(args[i]);
- }
- argcall(cmd);
-}
-
diff --git a/erts/etc/vxworks/erl_io.c b/erts/etc/vxworks/erl_io.c
deleted file mode 100644
index 0032b77079..0000000000
--- a/erts/etc/vxworks/erl_io.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2009. 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
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
-/* Some stuff to let the Erlang and VxWorks shells coexist peacefully.
- Basically, run Erlang as a spawned task with input redirected to
- the slave side of a pseudo-tty, and connect explicitly to the master
- side of the pseudo-tty to send input to Erlang when desired. */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include <stdio.h>
-#include <ioLib.h>
-#include <taskLib.h>
-#include <ptyDrv.h>
-
-extern int spTaskPriority, spTaskOptions;
-
-#define TBUFSIZ 512
-
-#define DEFAULT_STACK_SIZE 100000
-
-static int slavefd = -1, masterfd = -1;
-static run_erl();
-
-/* Frontend to the Erlang startup function - callable from VxWorks shell
- or script. 'arg' is actually a string passed to the real startup. */
-start_erl(arg)
-int arg;
-{
- int stacksize;
- char *stackenv;
-
- /* create and open the pty - we want the master side to be open
- all the time, since closing it probably generates EOF on the
- slave side */
- (void)ptyDevCreate("/pty/erlang.", TBUFSIZ, TBUFSIZ);
- if (slavefd != -1)
- (void)close(slavefd);
- slavefd = open("/pty/erlang.S", O_RDONLY, 0);
- if (masterfd != -1)
- (void)close(masterfd);
- masterfd = open("/pty/erlang.M", O_WRONLY, 0);
-
- /* flush any old leftover garbage */
- (void) ioctl(masterfd, FIOFLUSH, 0);
- if ((stackenv = getenv("ERLSTACKSIZE")) == NULL)
- stacksize = DEFAULT_STACK_SIZE;
- else
- stacksize = atoi(stackenv);
- /* spawn Erlang, via stub below */
- return(taskSpawn("erlang", spTaskPriority, spTaskOptions, stacksize,
- run_erl, arg, 0,0,0,0,0,0,0,0,0));
-}
-
-/* Little stub that runs in the spawned task - we need this to redirect
- stdin reliably (redirections aren't "inherited" in VxWorks) */
-static
-run_erl(arg)
-int arg;
-{
- ioTaskStdSet(0, 0, slavefd); /* redirect stdin to slave side of pty */
-
- /* We don't want to redirect stdout/err since no one will be reading
- from the master side (to_erl - and the open()s above - could be
- made bidirectional, but still the master side would only be read
- when to_erl was running), and output can eventually fill the pty
- buffer and cause the Erlang system to block. Not redirecting
- stdout/err will have the effect that output from Erlang, e.g. the
- shell prompt, will appear on console/rlogin/whatever even when
- to_erl isn't running, which may be confusing - can't win 'em all... */
-
- erl_exec(arg, 0,0,0,0,0,0,0,0); /* call the real startup */
-}
-
-/* Function callable from VxWorks shell to talk to Erlang - stop talking
- and return to VxWorks shell through ^D (EOF) */
-to_erl()
-{
- char buf[TBUFSIZ];
- int cc;
-
- if (masterfd == -1) { /* sanity check */
- fprintf(stderr, "Must start_erl first!\n");
- return(-1);
- }
- while ((cc = read(0, buf, TBUFSIZ)) > 0) /* just pass everything through */
- if (write(masterfd, buf, cc) != cc) {
- fprintf(stderr, "Write to Erlang failed!\n");
- return(-1);
- }
- return(cc);
-}
diff --git a/erts/etc/vxworks/erl_script.sam.in b/erts/etc/vxworks/erl_script.sam.in
deleted file mode 100644
index 81c2b0128d..0000000000
--- a/erts/etc/vxworks/erl_script.sam.in
+++ /dev/null
@@ -1,100 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2009. 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
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-#
-# erl_script.sam
-# Sample VxWorks script to start Erlang
-#
-# Note! This is not a complete or ready to use VxWorks startup script,
-# rather an example of what You should add to Your existing startupscript
-# to execute the erlang emulator at boot.
-#
-# When writing Your own script to start erlang, the paths to
-# the binaries have to be changed to reflect your system.
-#
-# The ROOTDIR variable should not point to a ftp or rcp filesystem unless
-# the erlang machine is run in embedded mode. Loading of modules
-# is far to slow if the erlang binaries are not placed on a real filesystem
-# like NFS or any type of local filesystem.
-#
-
-#
-# Load modules
-#
-
-#
-# First load and initiate the reclaim facility
-#
-ld </home/tornado/erlvxworks/erts-%VSN%/bin/reclaim.o
-reclaim_init()
-
-#
-# Now load the runtime system
-#
-ld </home/tornado/erlvxworks/erts-%VSN%/bin/jam
-ld </home/tornado/erlvxworks/erts-%VSN%/bin/erl.exec
-ld </home/tornado/erlvxworks/erts-%VSN%/bin/erl_io
-ld </home/tornado/erlvxworks/erts-%VSN%/bin/vxcall
-ld </home/tornado/erlvxworks/erts-%VSN%/bin/heart
-ld </home/tornado/erlvxworks/erts-%VSN%/bin/epmd
-
-#
-# Stack sizes
-#
-putenv "ERLSTACKSIZE=100000"
-putenv "ERLPORTSTACKSIZE=100000"
-
-#
-# Activate Round robin scheduling
-#
-kernelTimeSlice 1
-
-#
-# Distribution
-# The VxWorks machines host name
-sethostname "sb001", 5
-# Erlangs internal resolver
-putenv "ERLRESCONF=/home/tornado/erlvxworks/erts-%VSN%/bin/resolv.conf"
-
-#
-# Start epmd (for distribution)
-#
-start_epmd "-daemon"
-
-#
-# Misc environment variables
-#
-putenv "ROOTDIR=/home/tornado/erlvxworks"
-putenv "BINDIR=/home/tornado/erlvxworks/erts-%VSN%/bin"
-putenv "PROGNAME=erl"
-putenv "HOME=/home/tornado/erlvxworks"
-
-#
-# Set heart no reboot mode (to make heart reboot -
-# don't define HEART_DONT_REBOOT at all)
-#
-putenv "HEART_DONT_REBOOT=1"
-
-# To get fullsweep garbage collection on systems with
-# very limited memory, set ERL_FULLSWEEP_AFTER to "0":
-# putenv "ERL_FULLSWEEP_AFTER=0"
-
-#
-# Start Erlang/OTP
-#
-start_erl "-oldshell -heart -sname vxnode -setcookie switch -boot start_sasl"
diff --git a/erts/etc/vxworks/heart_config.c b/erts/etc/vxworks/heart_config.c
deleted file mode 100644
index 7e60e61fbb..0000000000
--- a/erts/etc/vxworks/heart_config.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2009. 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
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
-/*
- * A basic heart configure module for VxWorks.
- *
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include <vxWorks.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <rebootLib.h>
-#include <sysLib.h>
-
-/* wd_init is executed to initialize a watchdog (if one is used). */
-int wd_init(timeout, prio)
- int timeout, prio;
-{
-
-}
-
-/* wd_reset should be called every 5th second from heart */
-void wd_reset()
-{
-
-}
-
-/* This routine is called when erlang has closed */
-void heart_reboot()
-{
- if (getenv("HEART_DONT_REBOOT") != NULL) {
- fprintf(stderr, "heart_config: HEART_DONT_REBOOT set, no reboot ...\n");
- } else {
- fprintf(stderr, "heart_config: rebooting ...\n");
- taskDelay(sysClkRateGet() * 5);
- reboot(BOOT_CLEAR);
- }
-}
-
-
-
-
diff --git a/erts/etc/vxworks/heart_config.h b/erts/etc/vxworks/heart_config.h
deleted file mode 100644
index 5ffaaa8c3f..0000000000
--- a/erts/etc/vxworks/heart_config.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2009. 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
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
-/*
- * This is heart's watchdog interface for VxWorks.
- */
-
-#ifndef _HW_WATCHDOG_H
-#define _HW_WATCHDOG_H
-
-extern void wd_init(int timeout, int prio); /* wd_init initializes the
- watchdog, if one is used. */
-extern void wd_reset(void); /* wd_reset is used by heart to kick
- the watchdog, if one is used. */
-extern void heart_reboot(void); /* reboot is called if heart discovers
- that the Erlang task has stopped sending
- heart beats. It can log system status
- and should reboot VxWorks. */
-
-#endif
diff --git a/erts/etc/vxworks/rdate.c b/erts/etc/vxworks/rdate.c
deleted file mode 100644
index 3e8cc644d0..0000000000
--- a/erts/etc/vxworks/rdate.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2009. 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
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <vxWorks.h>
-#include <timers.h>
-#ifdef NETDB
-#include <netdb.h>
-#endif
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-/*
- rdate("host") - Set the time from "host".
-*/
-
-/* No getservbyname() available... */
-#define TIME_PORT 37
-
-rdate(host)
-char *host;
-{
- u_long haddr;
-#ifdef NETDB
- struct hostent *hp;
-#endif
- struct sockaddr_in saddr;
- int sock;
- u_long net_time;
- struct timespec t;
-
- if ((haddr = inet_addr(host)) == ERROR) {
-#ifdef NETDB
- if ((hp = gethostbyname(host)) == NULL) {
-#else
- if ((haddr = hostGetByName(host)) == ERROR) {
-#endif
- printf("Host not found.\n");
- return(-1);
- }
-#ifdef NETDB
- memcpy(&haddr, hp->h_addr, sizeof(haddr));
-#endif
- }
- memset(&saddr, 0, sizeof(saddr));
- saddr.sin_family = AF_INET;
- memcpy(&saddr.sin_addr, &haddr, sizeof(haddr));
- saddr.sin_port = htons(TIME_PORT);
- if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
- perror("socket");
- return(-1);
- }
- if (connect(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
- perror("connect");
- close(sock);
- return(-1);
- }
- if (read(sock, &net_time, 4) != 4) {
- perror("read");
- close(sock);
- return(-1);
- }
- t.tv_sec = ntohl(net_time);
- t.tv_sec -= 2208988800; /* seconds 1900-01-01 -- 1970-01-01 */
- t.tv_nsec = 0;
- clock_settime(CLOCK_REALTIME, &t);
- close(sock);
- return(0);
-}
diff --git a/erts/etc/vxworks/reclaim.c b/erts/etc/vxworks/reclaim.c
deleted file mode 100644
index d8676b3750..0000000000
--- a/erts/etc/vxworks/reclaim.c
+++ /dev/null
@@ -1,551 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-2009. 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
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <vxWorks.h>
-#include <version.h>
-#include <string.h>
-#include <types.h>
-#include <sigLib.h>
-#include <ioLib.h>
-#include <iosLib.h>
-#include <fioLib.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <symLib.h>
-#include <sysLib.h>
-#include <sysSymTbl.h>
-#include <loadLib.h>
-#include <taskLib.h>
-#include <taskVarLib.h>
-#include <taskHookLib.h>
-#include <tickLib.h>
-#include <time.h>
-#include <rngLib.h>
-#include <semLib.h>
-#include <selectLib.h>
-#include <sockLib.h>
-#include <a_out.h>
-#include <wdLib.h>
-#include <timers.h>
-#include <ctype.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <stdarg.h>
-
-#include <stdio.h>
-#include <math.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-
-
-
-#define RECLAIM_NO_ALIAS /* No #defines for open/malloc/fopen etc */
-#include "reclaim.h"
-#include "reclaim_private.h"
-
-#undef open
-#undef creat
-#undef socket
-#undef accept
-#undef close
-#undef fopen
-#undef fdopen
-#undef freopen
-#undef fclose
-/* XXX Should do opendir/closedir too... */
-#undef malloc
-#undef calloc
-#undef realloc
-#undef free
-#undef cfree
-
-#ifdef _ARCH_PPC
-#define MAX_FILES_SYM_NAME "maxFiles"
-#else
-#define MAX_FILES_SYM_NAME "_maxFiles"
-#endif
-
-
-/*
- * Use another free() function upon task deletion?
- * Note! When changing free function, the new free function will have to
- * be able to cope with ALL previously used malloced areas, that is
- * it has to be able to find out how things are malloced
- * to free them in the right way!
- */
-static FreeFunction reclaim_free_function = NULL;
-
-/* delete hook handling (see below) */
-static int hook_added = 0; /* Initated at first reclaim_init, an extra
- non MT-safe check that we only get
- initialized once */
-
-/* Forward... */
-static void save_reclaim(WIND_TCB *tcbp);
-
-struct mall_data {
- struct mall_data *next;
- char *self;
-};
-
-struct task_data {
- FUNCPTR version; /* To recognize when we have reloaded */
- int max_files; /* It may change... */
- struct fd_set open_fds;
- struct mall_data *mall_data;
- FUNCPTR delete_hook;
- caddr_t hook_data;
- FILE *open_fps[1]; /* Will be max_files long */
-} *task_data = NULL;
-
-static int max_files = 50; /* default configAll.h */
-
-int reclaim_max_files(void)
-{
- return max_files;
-}
-
-#ifdef DEBUG
-#define check_hook() \
-((task_data != NULL || \
- fdprintf(2,"check_hook() TID = 0x%08x, Called from line %d\n", \
- (unsigned int)taskIdSelf(),\
- __LINE__)) && \
-(task_data != NULL || \
- (taskVarAdd(0, (int *)&task_data) == OK && \
- (task_data = (struct task_data *)\
- calloc(1, sizeof(struct task_data) + max_files*sizeof(FILE *))) != NULL && \
- (task_data->version = (FUNCPTR)save_reclaim) != NULL && \
- (task_data->max_files = max_files) != 0 && \
- fdprintf(2,"taskVar Added for 0x%08x\n",(unsigned int)taskIdSelf()))))
-#else
-#define check_hook() \
-(task_data != NULL || \
- (taskVarAdd(0, (int *)&task_data) == OK && \
- (task_data = (struct task_data *)\
- calloc(1, sizeof(struct task_data) + max_files*sizeof(FILE *))) != NULL && \
- (task_data->version = (FUNCPTR)save_reclaim) != NULL && \
- (task_data->max_files = max_files) != 0))
-#endif
-
-/*
- * Global initialization of the reclaim data structures, mainly
- * the max_files variable. This HAS to be called by some task before
- * the first task that utilizes this exit's, preferrably before any
- * task makes the first use of this library.
- */
-STATUS reclaim_init(void)
-{
- int *mp;
- SYM_TYPE type;
- struct task_data *tdp;
- int i;
-
- if (!hook_added) {
- /* race condition */
- ++hook_added;
- /* Try to find the maxFiles value */
- if (symFindByNameAndType(sysSymTbl, MAX_FILES_SYM_NAME, (char **)&mp,
- &type,
- N_EXT | N_BSS, N_EXT | N_BSS) == OK ||
- symFindByNameAndType(sysSymTbl, MAX_FILES_SYM_NAME, (char **)&mp,
- &type,
- N_EXT | N_DATA, N_EXT | N_DATA) == OK) {
-
-#ifdef DEBUG
- fdprintf(2, "Found maxFiles=%d\n", *mp);
-#endif
- if (*mp <= FD_SETSIZE)
- max_files = *mp;
- else
- max_files = FD_SETSIZE;
- }
- if (task_data != NULL && task_data->max_files != max_files) {
- /* fix our own iff we have one */
- if ((tdp = (struct task_data *)
- realloc(task_data, sizeof(struct task_data) +
- max_files*sizeof(FILE *))) != NULL) {
- task_data = tdp;
- for (i = task_data->max_files; i < max_files; i++)
- task_data->open_fps[i] = NULL;
- task_data->max_files = max_files;
- }
- }
- /* Make sure taskVariables are deleted AFTER our hook is run. */
- taskVarInit();
- if(taskDeleteHookAdd((FUNCPTR)save_reclaim) != OK) {
- fprintf(stderr,
- "Panic: taskDeleteHook cannot be added for reclaim.\n");
- return ERROR;
- }
- return OK;
- } else
- return ERROR;
-}
-
-/* N.B.!! We do *not* execute in the context of the dying task here,
- but rather that of tExcTask - we do get a pointer to the task's
- TCB though - this pointer is in fact also the task's ID. */
-static void save_reclaim(WIND_TCB *tcbp)
-{
- int i, var, oldfd;
- struct task_data *tdp;
- struct mall_data *mdp, *mdnextp;
-
- if ((var = taskVarGet((int)tcbp, (int *)&task_data)) != ERROR &&
- var != 0) {
- tdp = (struct task_data *)var;
- if (tdp->version == (FUNCPTR)save_reclaim) { /* Only handle our own */
-#ifdef DEBUG
- fdprintf(2, "Reclaiming for task id 0x%x:\nFiles: ", (int)tcbp);
-#endif
- /* Ugh! VxWorks doesn't even flush stdout/err - we need to
- get at those (which are task-private of course, i.e. we
- can't just do fflush(stdout) here) - we could be really
- pedantic and try to redefine stdout/err (which "are"
- function calls) too, snarfing the values when they are
- used - but besides the overhead this is problematic since
- they are actually #defines already... We'll peek in the
- TCB instead (no documentation of course). And of course,
- we must also meddle with the *file descriptor* indirections,
- or we'll just flush out on tExcTask's descriptors... */
- for (i = 1; i <= 2; i++) {
- if (tcbp->taskStdFp[i] != NULL) {
-#ifdef DEBUG
- fdprintf(2, "fflush(%s) ", i == 1 ? "stdout" : "stderr");
-#endif
- oldfd = ioTaskStdGet(0, i);
- ioTaskStdSet(0, i, tcbp->taskStd[i]);
- fflush(tcbp->taskStdFp[i]);
- ioTaskStdSet(0, i, oldfd);
- }
- }
- for (i = 3; i < tdp->max_files; i++) {
- if (FD_ISSET(i, &tdp->open_fds)) {
-#ifdef DEBUG
- fdprintf(2, "close(%d) ", i);
-#endif
- (void) close(i);
- }
- if (tdp->open_fps[i] != NULL) {
-#ifdef DEBUG
- fdprintf(2, "fclose(%0x%x) ", (int)tdp->open_fps[i]);
-#endif
- (void) fclose(tdp->open_fps[i]);
- }
- }
- i = 0;
- mdp = tdp->mall_data;
- while (mdp != NULL) {
- mdnextp = mdp->next;
- if(reclaim_free_function != NULL)
- (*reclaim_free_function)(mdp->self);
- else
- free(mdp->self);
- i++;
- mdp = mdnextp;
- }
-#ifdef DEBUG
- fdprintf(2, "\nFreeing memory: total %d mallocs\n", i);
-#endif
-
- if (tdp->delete_hook != NULL) {
-#ifdef DEBUG
- fdprintf(2, "Calling delete hook at 0x%08x\n", tdp->delete_hook);
-#endif
- (*tdp->delete_hook)(tdp->hook_data);
-#ifdef DEBUG
- fdprintf(2, "Called delete hook at 0x%08x\n", tdp->delete_hook);
-#endif
- }
-#ifdef DEBUG
- fdprintf(2, "Freeing own mem at 0x%08x\n", tdp);
-#endif
- (void) free((char *)tdp);
-#ifdef DEBUG
- fdprintf(2, "Freed own mem at 0x%08x, done (0x%08x)\n**********\n", tdp,
- taskIdSelf());
- checkStack(0);
-#endif
- }
- }
-#ifdef DEBUG
- else
- fdprintf(2, "No task data found for id 0x%x, var = %d\n", (int)tcbp, var);
-#endif
-}
-
-/*
- * This sets another free function to be used by the task deletion hook.
- * The free function HAS to be able to free ANY type of dynamically allocated
- * memory that can be in the task data list of allocated memory, that is
- * also memory that's allocated before the free function was set.
- * A "user-supplied" free function is GLOBAL to the system!!!
- * A race condition is present, a task delete hook may be running when this
- * function is called, that may not be especially funny...
- */
-void set_reclaim_free_function(FreeFunction f){
- reclaim_free_function = f;
-}
-
-void save_delete_hook(FUNCPTR func, caddr_t parm)
-{
- if (check_hook()) {
- task_data->delete_hook = func;
- task_data->hook_data = parm;
- }
-}
-
-/*
- * plain_malloc is only used by spawn_start; plain_free by call_proc;
- * save_fd is used by both.
- */
-void *plain_malloc(size_t size){
- return(malloc(size));
-}
-
-void *plain_realloc(void *ptr, size_t size){
- return(realloc(ptr, size));
-}
-
-void plain_free(void *ptr){
- free(ptr);
-}
-
-void save_fd(int fd){
- if (fd >= 0 && check_hook() && fd < task_data->max_files)
- FD_SET(fd, &task_data->open_fds);
-}
-
-int save_open(char *path, int flags, /*mode_t mode*/ ...){
- int fd;
- mode_t mode = 0;
- if(flags & O_CREAT){
- va_list pvar;
- va_start(pvar,flags);
-#ifdef __GNUC__
-#warning save_open() gives three known alignment warnings.
-#endif
- mode = va_arg(pvar, mode_t);
- va_end(pvar);
- }
- if ((fd = open(path, flags, mode)) >= 0 && check_hook())
- FD_SET(fd, &task_data->open_fds);
- return(fd);
-}
-
-int save_creat(char *path, int mode){
- int fd;
- if ((fd = creat(path, mode)) >= 0 && check_hook())
- FD_SET(fd, &task_data->open_fds);
- return(fd);
-}
-
-int save_socket(int domain, int type, int protocol){
- int fd;
- if ((fd = socket(domain, type, protocol)) >= 0 && check_hook())
- FD_SET(fd, &task_data->open_fds);
- return(fd);
-}
-
-int save_accept(int s, struct sockaddr *addr, int *addrlen){
- int fd;
- if ((fd = accept(s, addr, addrlen)) >= 0 && check_hook())
- FD_SET(fd, &task_data->open_fds);
- return(fd);
-}
-
-int save_close(int fd){
- if (fd >= 0 && fd <= FD_SETSIZE && check_hook())
- FD_CLR(fd, &task_data->open_fds);
- return(close(fd));
-}
-
-/* The dealing with FILE *'s below isn't strictly correct, we assume
- that one never has several pointing to the same fd - in the unlikely
- event that one does, all but the last one opened is forgotten */
-FILE *save_fopen(const char *filename, char *type){
- FILE *fp;
-
- if ((fp = fopen(filename, type)) != NULL &&
- check_hook() && fileno(fp) < task_data->max_files)
- task_data->open_fps[fileno(fp)] = fp;
- return(fp);
-}
-
-FILE *save_fdopen(int fd, char *type){
- FILE *fp;
-
- if ((fp = fdopen(fd, type)) != NULL &&
- check_hook() && fileno(fp) < task_data->max_files) {
- task_data->open_fps[fileno(fp)] = fp;
- FD_CLR(fd, &task_data->open_fds);
- }
- return(fp);
-}
-
-FILE *save_freopen(char *filename, char *type, FILE *stream){
- FILE *fp;
-
- if (check_hook()) {
- if(fileno(stream) < task_data->max_files &&
- task_data->open_fps[fileno(stream)] == stream)
- task_data->open_fps[fileno(stream)] = NULL;
- if ((fp = freopen(filename, type, stream)) != NULL &&
- fileno(fp) < task_data->max_files)
- task_data->open_fps[fileno(fp)] = fp;
- } else
- fp = freopen(filename, type, stream);
- return(fp);
-}
-
-int save_fclose(FILE *stream){
- if (check_hook() && fileno(stream) < task_data->max_files &&
- task_data->open_fps[fileno(stream)] == stream)
- task_data->open_fps[fileno(stream)] = NULL;
- return(fclose(stream));
-}
-
-/* We link all malloc'ed segments by adding a couple of pointers
- at the *end* - that way we can return the address malloc gave
- (need to make sure we align those pointers) */
-
-/*
- #define MALL_MARGIN 32
- #define save_mall_size(size) save_mall_size1((size) + 2 * MALL_MARGIN)
- #define save_mall_size1(size) \
- (((size) + sizeof(char *) - 1) & (unsigned long)(-sizeof(char*)))
-
- #define save_mall_enq(ptr, mdp) save_mall_enq1((ptr), (mdp) - MALL_MARGIN)
- #define save_mall_enq1(ptr, mdp) \
- (((struct mall_data *)(mdp))->self = (ptr), \
- ((struct mall_data *)(mdp))->next = task_data->mall_data, \
- task_data->mall_data = (struct mall_data *)(mdp))
-*/
-#define save_mall_size(size) \
-(((size) + sizeof(char *) - 1) & (unsigned long)(-sizeof(char*)))
-#define save_mall_enq(ptr, mdp) \
-(((struct mall_data *)(mdp))->self = (ptr), \
- ((struct mall_data *)(mdp))->next = task_data->mall_data, \
- task_data->mall_data = (struct mall_data *)(mdp))
-
-
-#define save_mall_deq(ptr) { \
- struct mall_data *mdp = task_data->mall_data, \
- **prevnext = &task_data->mall_data; \
- while (mdp != NULL && mdp->self != (ptr)) { \
- prevnext = &mdp->next; \
- mdp = mdp->next; \
- } \
- if (mdp != NULL) *prevnext = mdp->next; \
-}
-
-void *save_malloc2(size_t size, MallocFunction mf){
- unsigned msize = save_mall_size(size);
- char *ptr;
-
- if ((ptr = (*mf)(msize + sizeof(struct mall_data))) != NULL &&
- check_hook())
- save_mall_enq((void *) ptr, (void *) (ptr + msize));
- return((void *) ptr);
-}
-
-void *save_malloc(size_t size){
- return save_malloc2(size, &malloc);
-}
-
-void *save_calloc2(size_t nelem, size_t elsize, CallocFunction cf){
- unsigned msize = save_mall_size(nelem * elsize);
- char *ptr;
-
- if ((ptr = (*cf)(1, msize + sizeof(struct mall_data))) != NULL &&
- check_hook())
- save_mall_enq((void *) ptr, (void *) (ptr + msize));
- return((void *) ptr);
-}
-
-void *save_calloc(size_t nelem, size_t elsize){
- return save_calloc2(nelem,elsize,&calloc);
-}
-
-void *save_realloc2(void *optr, size_t size, ReallocFunction rf){
- unsigned msize = save_mall_size(size);
- char *ptr;
-
- /* First we must dequeue the old save block, after that
- we try to realloc, if that succeeds we enqueue the new
- block, if it fails we have to enqueue the old one anew
- so we must deduce the size of that old block first. */
-
- struct mall_data *mdp0 = task_data->mall_data,
- **prevnext0 = &task_data->mall_data;
- while (mdp0 != NULL && mdp0->self != (((char *) optr))) {
- prevnext0 = &mdp0->next;
- mdp0 = mdp0->next;
- }
- /* mdp0 == NULL (can) mean that the block that is realloced
- have been malloced with an (for example) ordinary malloc
- (that is not a save_malloc). This is handled like: no dequeing
- is done of that block, the new block is enqueued */
- if (mdp0 != NULL)
- save_mall_deq(((char *) optr));
-
- if ((ptr = (*rf)(optr, msize + sizeof(struct mall_data))) != NULL &&
- check_hook())
- save_mall_enq((void *) ptr, (void *) (ptr + msize));
- else if (mdp0 != NULL)
- /* re-enqueue the old block that has just been dequeued */
- save_mall_enq(((char *) optr), mdp0);
-
- return((void *) ptr);
-}
-
-void *save_realloc(void *optr, size_t size){
- return save_realloc2(optr,size,&realloc);
-}
-
-void save_free2(void *ptr, FreeFunction ff)
-{
- if (check_hook())
- save_mall_deq(((char *) ptr));
- (*ff)(ptr);
-}
-
-void save_free(void *ptr){
- save_free2(ptr,&free);
-}
-
-void save_cfree2(void *ptr, CfreeFunction cf)
-{
- if (check_hook())
- save_mall_deq(((char *)ptr));
- (*cf)(ptr);
-}
-
-void save_cfree(void *ptr){
- save_cfree2(ptr,&cfree);
-}
-
diff --git a/erts/etc/vxworks/reclaim.h b/erts/etc/vxworks/reclaim.h
deleted file mode 100644
index ca9aa8f6be..0000000000
--- a/erts/etc/vxworks/reclaim.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-2009. 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
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
-#ifndef _RECLAIM_H
-#define _RECLAIM_H
-
-
-/* The Erlang release for VxWorks includes a simple mechanism for
- "resource reclamation" at task exit - it allows replacement of the
- functions that open/close "files" and malloc/free memory with versions
- that keep track, to be able to "reclaim" file descriptors and memory
- when a task exits (regardless of *how* it exits).
-
- The interface to this mechanism is made available via this file,
- with the following caveats:
-
- - The interface may change (or perhaps even be removed, though that
- isn't likely until VxWorks itself provides similar functionality)
- in future releases - i.e. you must always use the version of this
- file that comes with the Erlang release you are using.
-
- - Disaster is guaranteed if you use the mechanism incorrectly (see
- below for the correct way), e.g. allocate memory with the "tracking"
- version of malloc() and free it with the "standard" version of free().
-
- - The mechanism (of course) incurs some performance penalty - thus
- for a simple program you may be better off with careful programming,
- making sure that you do whatever close()/free()/etc calls that are
- appropriate at all exit points (though if you need to guard against
- taskDelete() etc, things get messy...).
-
- To use the mechanism, simply program your application normally, i.e.
- use open()/close()/malloc()/free() etc as usual, but #include this
- file before any usage of the relevant functions. NOTE: To avoid the
- "disaster" mentioned above, you *must* #include it in *all* (or none)
- of the files that manipulate a particular file descriptor, allocated
- memory area, etc.
-
- Before any task that uses this utility is loaded (which includes the
- erlang emulator), the reclaim.o object file has to be loaded and
- the function reclaim_init() has to be called. reclaim_init should be called
- only _ONCE_ in a systems lifetime and has only a primitive guard
- against multiple calls (i.e. a global variable is checked). Therefore
- the initialization should occur either in the start script of the system
- or (even better) in the usrInit() part of system initialization. The
- object file itself should be loaded only once, so linking it with the
- kernel is a good idea, linking with each application is an extremely bad
- dito. Make really sure that it's loaded _before_ any application that
- uses it if You want to load it in the startup script.
-
- If You dont want to have #define's for the posix/stdio names
- of the file/memory operations (i.e. no #define malloc save_malloc etc),
- #define RECLAIM_NO_ALIAS in Your source before reclaim.h is included.
-*/
-
-#include <vxWorks.h> /* STATUS, size_t */
-#include <sockLib.h> /* struct sockaddr */
-#include <memLib.h>
-#include <stdio.h> /* FILE */
-
-#if defined(__STDC__)
-#define _RECLAIM_DECL_FUN(RetType, FunName, ParamList) \
-extern RetType FunName ParamList
-#define _RECLAIM_VOID_PTR void *
-#define _RECLAIM_VOID_PARAM void
-#define _RECLAIM_VOID_RETURN void
-#elif defined(__cplusplus)
-#define _RECLAIM_DECL_FUN(RetType, FunName, ParamList) \
-extern "C" RetType FunName ParamList
-#define _RECLAIM_VOID_PTR void *
-#define _RECLAIM_VOID_PARAM
-#define _RECLAIM_VOID_RETURN void
-#else
-#define _RECLAIM_DECL_FUN(RetType, FunName, Ignore) extern RetType FunName()
-#define DECLARE_FUNCTION_TYPE(RetType, Type, PList) typedef RetType (* Type)()
-#define _RECLAIM_VOID_PTR char *
-#define _RECLAIM_VOID_PARAM
-#define _RECLAIM_VOID_RETURN
-#endif /* __STDC__ / __cplusplus */
-
-/* Initialize the facility, on a per system basis. */
-_RECLAIM_DECL_FUN(STATUS, reclaim_init, (_RECLAIM_VOID_PARAM));
-
-/* File descriptor operations */
-_RECLAIM_DECL_FUN(int,save_open,(char *, int, ...));
-_RECLAIM_DECL_FUN(int,save_creat,(char *, int));
-_RECLAIM_DECL_FUN(int,save_socket,(int, int, int));
-_RECLAIM_DECL_FUN(int,save_accept,(int, struct sockaddr *, int *));
-_RECLAIM_DECL_FUN(int,save_close,(int));
-/* Interface to add an fd to what's reclaimed even though it's not open with
- one of the above functions */
-_RECLAIM_DECL_FUN(_RECLAIM_VOID_RETURN, save_fd, (int fd));
-#ifndef RECLAIM_NO_ALIAS
-#define open save_open
-#define creat save_creat
-#define socket save_socket
-#define accept save_accept
-#define close save_close
-#endif
-/* Stdio file operations */
-_RECLAIM_DECL_FUN(FILE *, save_fopen, (const char *, char *));
-_RECLAIM_DECL_FUN(FILE *, save_fdopen, (int, char *));
-_RECLAIM_DECL_FUN(FILE *, save_freopen, (char *, char *, FILE *));
-_RECLAIM_DECL_FUN(int, save_fclose, (FILE *));
-/* XXX Should do opendir/closedir too... */
-#ifndef RECLAIM_NO_ALIAS
-#define fopen save_fopen
-#define fdopen save_fdopen
-#define freopen save_freopen
-#define fclose save_fclose
-#endif
-/* Memory allocation */
-_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, save_malloc, (size_t));
-_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, save_calloc, (size_t, size_t));
-_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, save_realloc,
- (_RECLAIM_VOID_PTR, size_t));
-_RECLAIM_DECL_FUN(void, save_free, (_RECLAIM_VOID_PTR));
-_RECLAIM_DECL_FUN(void, save_cfree, (_RECLAIM_VOID_PTR));
-#ifndef RECLAIM_NO_ALIAS
-#define malloc save_malloc
-#define calloc save_calloc
-#define realloc save_realloc
-#define free save_free
-#define cfree save_cfree
-#endif
-/* Generic interfaces to malloc etc... */
-_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, plain_malloc, (size_t));
-_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, plain_realloc,
- (_RECLAIM_VOID_PTR, size_t));
-_RECLAIM_DECL_FUN(void, plain_free, (_RECLAIM_VOID_PTR));
-#endif /* _RECLAIM_H */
-
-
-
-
diff --git a/erts/etc/vxworks/reclaim_private.h b/erts/etc/vxworks/reclaim_private.h
deleted file mode 100644
index 4ed935bee2..0000000000
--- a/erts/etc/vxworks/reclaim_private.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-2009. 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
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
-#ifndef _RECLAIM_PRIVATE_H
-#define _RECLAIM_PRIVATE_H
-/*
- * Private header for the reclaim facility, also included in the emulator.
- */
-
-#include "reclaim.h"
-
-/* Typedefs for ANSI memory allocation function pointers */
-typedef void *(*MallocFunction)(size_t);
-typedef void *(*ReallocFunction)(void *, size_t);
-typedef void *(*CallocFunction)(size_t, size_t);
-typedef void (*FreeFunction)(void *);
-typedef STATUS (*CfreeFunction)(char *);
-
-/* Functions for internal use and use by the emulator */
-extern int reclaim_max_files(void);
-extern void set_reclaim_free_function(FreeFunction f);
-extern void save_delete_hook(FUNCPTR func, caddr_t parm);
-extern void *save_malloc2(size_t size, MallocFunction mf);
-extern void *save_calloc2(size_t nelem, size_t elsize, CallocFunction cf);
-extern void *save_realloc2(void *optr, size_t size, ReallocFunction rf);
-extern void save_free2(void *ptr, FreeFunction ff);
-extern void save_cfree2(void *ptr, CfreeFunction ff);
-
-#endif /* _RECLAIM_PRIVATE_H */
diff --git a/erts/etc/vxworks/resolv.conf b/erts/etc/vxworks/resolv.conf
deleted file mode 100644
index 85c89d64c4..0000000000
--- a/erts/etc/vxworks/resolv.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-domain du.uab.ericsson.se
-nameserver 134.138.176.16
-nameserver 136.225.254.224
-nameserver 134.138.128.25
-search du.uab.ericsson.se uab.ericsson.se ericsson.se
-lookup bind file
diff --git a/erts/etc/vxworks/vxcall.c b/erts/etc/vxworks/vxcall.c
deleted file mode 100644
index 3362d05fc5..0000000000
--- a/erts/etc/vxworks/vxcall.c
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2009. 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
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <vxWorks.h>
-#include <symLib.h>
-#include <sysSymTbl.h>
-#include <a_out.h>
-
-extern char *malloc();
-static STATUS lookup();
-
-/*
- Little utility to convert from Unix' argv,argv calling conventions to
- VxWorks' arg0,arg1,arg2,...
- Will do limited argument parsing - no parenthesis around nor commas
- between the args, which may be "-enclosed strings (without \ escapes),
- '-enclosed characters (also no \ escapes), integers, or symbols.
-*/
-
-int vxcall(argc, argv)
-int argc;
-char **argv;
-{
- int vxarg[10]; /* Max 10 args can be passed */
- FUNCPTR entry;
- SYM_TYPE type;
- int i, l;
-
-#ifdef DEBUG
- fdprintf(2, "vxcall:");
- for (i = 1; i < argc; i++)
- fdprintf(2, " %s", argv[i]);
- fdprintf(2, "\n");
-#endif
- if (lookup(argv[1], N_EXT | N_TEXT, (char **)&entry) != OK)
- return(ERROR);
- /* Do limited "C" parsing of the args */
- for (i = 0; i < 10; i++) {
- if (i < argc - 2) {
- switch (argv[i+2][0]) {
- case '"':
- l = strlen(argv[i+2]) - 1;
- if (argv[i+2][l] != '"')
- return(ERROR);
- /* just strip the quotes - should do \escapes within... */
- vxarg[i] = (int)&argv[i+2][1];
- argv[i+2][l] = '\0';
- break;
- case '\'':
- if (argv[i+2][2] != '\'')
- return(ERROR);
- vxarg[i] = argv[i+2][1]; /* should do \escapes... */
- break;
- case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- vxarg[i] = atoi(argv[i+2]); /* should do octal, hex, float.. */
- break;
- default:
- if (lookup(argv[i+2], 0, (char **)&vxarg[i]) != OK)
- return(ERROR);
- }
- } else
- vxarg[i] = 0;
- }
-#ifdef DEBUG
- fdprintf(2, "calling 0x%x(0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x)\n",
- entry, vxarg[0], vxarg[1], vxarg[2], vxarg[3], vxarg[4],
- vxarg[5], vxarg[6], vxarg[7], vxarg[8], vxarg[9]);
-#endif
- return((*entry)(vxarg[0], vxarg[1], vxarg[2], vxarg[3], vxarg[4],
- vxarg[5], vxarg[6], vxarg[7], vxarg[8], vxarg[9]));
-}
-
-/* Entry point for unix:cmd in post-4.1 erlang - uses "sh -c 'cmd...'" */
-int sh(argc, argv)
-int argc;
-char **argv;
-{
- int ll = strlen(argv[argc-1]) - 1;
-
-#ifdef DEBUG
- int i;
- fdprintf(2, "sh:");
- for (i = 1; i < argc; i++)
- fdprintf(2, " %s", argv[i]);
- fdprintf(2, "\n");
-#endif
- if (strcmp(argv[1], "-c") != 0 ||
- argv[2][0] != '\'' || argv[argc-1][ll] != '\'')
- return(ERROR);
- argv[argc-1][ll] = '\0'; /* delete trailing ' */
- argv[2]++; /* skip leading ' (*after* the above!) */
- return(vxcall(argc-1, argv+1));
-}
-
-/* Lookup symbol; get address for text symbols, value (assuming int)
- otherwise; return OK or ERROR on failure
- Symbol name is null-terminated and without the leading '_' */
-STATUS
-lookup(sym, stype, value)
-char *sym, **value;
-int stype;
-{
- char buf[256];
- char *symname = buf;
- int len, ret;
- SYM_TYPE type;
-
- len = strlen(sym);
- if (len > 254 && (symname = malloc(len+2)) == NULL)
- return(ERROR);
-#if defined _ARCH_PPC || defined SIMSPARCSOLARIS
- /* GCC for PPC or SIMSPARC doesn't add a leading _ to symbols */
- strcpy(symname, sym);
-#else
- sprintf(symname, "_%s", sym);
-#endif
- ret = (stype != 0) ?
- symFindByNameAndType(sysSymTbl, symname, value, &type, stype, stype) :
- symFindByName(sysSymTbl, symname, value, &type);
- if (symname != buf)
- free(symname);
- if (ret == OK && (type & N_TEXT) == 0) /* get value */
- *value = (char *)*((int *)*value);
- return(ret);
-}
diff --git a/erts/etc/vxworks/wd_example.c b/erts/etc/vxworks/wd_example.c
deleted file mode 100644
index 0e3a6a1cb2..0000000000
--- a/erts/etc/vxworks/wd_example.c
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2009. 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
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
-/*
- * File: frc5te_wd.c
- * Purpose: Watchdog NMI handling for FORCE 5TE
- *
- * Description:
- * The watchdog handler routines are system specific. A program that
- * wants to utilize a hardware watchdog should call wd_init and test
- * the return value. If wd_init returns true (!0); there is a hardware
- * watchdog, and that watchdog has been activated. If no watchdog exists,
- * wd_init returns false (0).
- *
- * To keep the watchdog happy, call wd_reset at least every X seconds,
- * where X is the number of seconds specified in the call to wd_init.
- *
- * The watchdog can be disarmed by setting the variable wd_disarmed to 1,
- * and armed again by setting the same variable to 0. Watchdog status
- * information can be retrieved using the function wd_status.
- *
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include <frc5e.h>
-#include <logLib.h>
-#include <taskLib.h>
-#include <sysLib.h>
-#include <stdio.h>
-#include "hw_watchdog.h"
-
-/* prototypes */
-extern sysNMIConnect();
-#ifdef __STDC__
-void wd_keeper(int);
-void wd_nmi_int(UINT8);
-void wd_status(void);
-#else
-void wd_keeper();
-void wd_nmi_int();
-void wd_status();
-#endif
-
-#define WD_NMI_MIN_DELAY 0.830 /* Min time before watchdog NMI (in seconds) */
-#define WD_RESET_FREQUENCY (WD_NMI_MIN_DELAY / 2) /* how often the timer is reset */
-
-#define WD_KEEPER_STACK_SIZE 10000
-
-/* global variables */
-extern int spTaskOptions;
-static volatile int wd_count_startval; /* start value set by wd_init */
-static volatile int wd_count; /* counter for wd_keeper */
-volatile int wd_disarmed = 0; /* debug feature */
-
-/* wd_init is executed to initialize the watchdog. It spawns the task */
-/* wd_keeper and returns true (non-zero) if a hardware watchdog exists, */
-/* or returns false (zero) otherwise. */
-int wd_init(timeout, prio)
- int timeout, prio;
-{
- taskSpawn("wd_keeper", prio, spTaskOptions, WD_KEEPER_STACK_SIZE,
- (FUNCPTR)wd_keeper, timeout,0,0,0,0,0,0,0,0,0);
- return !0; /* watchdog exists */
-}
-
-
-/* wd_reset is called as an alive-signal from the supervisor process. */
-/* If there is no call to this function within a certain time, the */
-/* watchdog will reboot the system. */
-void wd_reset()
-{
- wd_count = wd_count_startval;
-}
-
-
-/* wd_keeper runs as a separate task and resets the watchdog timer */
-/* before an NMI is generated. This task uses the counter wd_count to */
-/* decide if it should exit or keep resetting the timer. */
-/* Note! This task must run with higher priority than the application! */
-void wd_keeper(timeout)
- int timeout;
-{
- int wd_delay = sysClkRateGet() * WD_RESET_FREQUENCY;
- wd_count_startval = (int)(timeout / WD_RESET_FREQUENCY);
- wd_count = wd_count_startval;
-
- /* Connect and enable level 15 interrupts */
- sysNMIConnect((VOIDFUNCPTR) wd_nmi_int, WD_NMI, WD_NMI);
- *(char *)FRC5CE_GEN_PURPOSE2_REG |= FRC5CE_NMI_ENABLE;
-
- while ((wd_count > 0) || wd_disarmed) {
- *(char *)FRC5CE_VME_A32MAP_REG |= FRC5CE_WATCHDOG_ENABLE;
- taskDelay(wd_delay);
- if (!wd_disarmed) wd_count--;
- else wd_count = wd_count_startval;
- }
- logMsg("Watchdog keeper exits. No alive signal from application in %d seconds.\n",wd_count_startval * WD_RESET_FREQUENCY,0,0,0,0,0);
-}
-
-
-/* wd_nmi_int is the function connected to the watchdog interrupt. */
-/* It will report the failure to reset the watchdog timer. */
-void wd_nmi_int(type)
- UINT8 type;
-{
- switch(type) {
- case WD_NMI:
- logMsg("Watchdog interrupt! System will reboot.\n",0,0,0,0,0,0);
- break;
- default:
- logMsg("Bad type (%d) in call to watchdog interrupt handler.\n",type,0,0,0,0,0);
- break;
- }
-}
-
-
-/* wd_status displays the current value of the counter. */
-void wd_status()
-{
- fprintf(stderr, "Watchdog is %sarmed.\n", wd_disarmed ? "dis" : "");
- fprintf(stderr, "Counter value: %d\n", wd_count);
- fprintf(stderr, "Start value is: %d (%d seconds)\n",
- wd_count_startval, (int)(wd_count_startval * WD_RESET_FREQUENCY));
-}
diff --git a/erts/etc/win32/Makefile b/erts/etc/win32/Makefile
index 4218be1eff..cc9021da70 100644
--- a/erts/etc/win32/Makefile
+++ b/erts/etc/win32/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/etc/win32/erlsrv/erlsrv_service.c b/erts/etc/win32/erlsrv/erlsrv_service.c
index 242e2905a9..58738ee445 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-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2012. 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
@@ -104,11 +104,10 @@ static VOID WINAPI handler(DWORD control){
log_debug(buffer);
switch(control){
case SERVICE_CONTROL_STOP:
+ case SERVICE_CONTROL_SHUTDOWN:
set_stop_pending(30000,1);
SetEvent(eventStop);
return;
- case SERVICE_CONTROL_SHUTDOWN:
- return;
default:
reset_current();
break;
diff --git a/erts/etc/win32/nsis/Makefile b/erts/etc/win32/nsis/Makefile
index 4ed4ef2700..f377a68c69 100644
--- a/erts/etc/win32/nsis/Makefile
+++ b/erts/etc/win32/nsis/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. 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/etc/win32/port_entry.c b/erts/etc/win32/port_entry.c
index 49b5ad2f34..30b54035e0 100644
--- a/erts/etc/win32/port_entry.c
+++ b/erts/etc/win32/port_entry.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2012. 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
@@ -45,6 +45,8 @@ extern void mainCRTStartup(void);
BOOL WINAPI erl_port_default_handler(DWORD ctrl){
if(ctrl == CTRL_LOGOFF_EVENT)
return TRUE;
+ if(ctrl == CTRL_SHUTDOWN_EVENT)
+ return TRUE;
return FALSE;
}
diff --git a/erts/etc/win32/start_erl.c b/erts/etc/win32/start_erl.c
index 28c8e55bd3..facf79e5ff 100644
--- a/erts/etc/win32/start_erl.c
+++ b/erts/etc/win32/start_erl.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2012. 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
@@ -585,6 +585,9 @@ BOOL WINAPI LogoffHandlerRoutine( DWORD dwCtrlType )
if(dwCtrlType == CTRL_LOGOFF_EVENT) {
return TRUE;
}
+ if(dwCtrlType == CTRL_SHUTDOWN_EVENT) {
+ return TRUE;
+ }
return FALSE;
}
diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h
index 142c26c0ca..e1885c627a 100644
--- a/erts/include/internal/ethread.h
+++ b/erts/include/internal/ethread.h
@@ -355,10 +355,8 @@ extern ethr_runtime_t ethr_runtime__;
# include "sparc64/ethread.h"
# endif
# endif
-#if 0
# include "gcc/ethread.h"
# include "libatomic_ops/ethread.h"
-#endif
# endif
# elif defined(ETHR_HAVE_LIBATOMIC_OPS)
# include "libatomic_ops/ethread.h"
diff --git a/erts/lib_src/common/erl_printf_format.c b/erts/lib_src/common/erl_printf_format.c
index 473791dce4..fd25cce7ed 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-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2012. 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
@@ -457,6 +457,15 @@ static int fmt_double(fmtfn_t fn,void*arg,double val,
return res;
}
+/* 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;
+}
+
int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap)
{
char* ptr0 = fmt;
@@ -771,9 +780,7 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap)
case FMTC_s: {
char* str = va_arg(ap,char*);
- int len = strlen(str);
- if (precision >= 0 && precision < len)
- len = precision;
+ int len = (precision >= 0) ? my_strnlen(str,precision) : strlen(str);
if (width > 0 && !(fmt & FMTF_adj)) {
if (width > len)
BLANKS(fn, arg, width - len, count);
diff --git a/erts/lib_src/common/ethr_aux.c b/erts/lib_src/common/ethr_aux.c
index 89149b716b..f4ff08d368 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-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2012. 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/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index df1831f340..e46795e2b1 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/erlang.beam b/erts/preloaded/ebin/erlang.beam
index b78747bc84..66b7a011d6 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index cc170b86b2..1a250932da 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 45a409738c..2fa007f683 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_file.beam b/erts/preloaded/ebin/prim_file.beam
index 6400cda2b5..19cf856754 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 b2f3ab6c5b..5409cf2dfc 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 411fc8d524..55697cfe11 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 7a1f896d36..d2f30fd9cd 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 d5f5ba3c37..5bcc2eb6e4 100644
--- a/erts/preloaded/src/Makefile
+++ b/erts/preloaded/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2010. All Rights Reserved.
+# Copyright Ericsson AB 2008-2012. 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/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl
index 14a7a2bf20..d36fdeba3f 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -49,7 +49,7 @@
prim_read_file_info/2, prim_get_cwd/2]).
%% Used by escript and code
--export([set_primary_archive/3, release_archives/0]).
+-export([set_primary_archive/4, release_archives/0]).
-include_lib("kernel/include/file.hrl").
@@ -222,15 +222,16 @@ get_cwd(Drive) ->
check_file_result(get_cwd, Drive, request({get_cwd,[Drive]})).
-spec set_primary_archive(File :: string() | 'undefined',
- ArchiveBin :: binary() | 'undefined',
- FileInfo :: #file_info{} | 'undefined')
+ ArchiveBin :: binary() | 'undefined',
+ FileInfo :: #file_info{} | 'undefined',
+ ParserFun :: fun())
-> {ok, [string()]} | {error,_}.
-set_primary_archive(undefined, undefined, undefined) ->
- request({set_primary_archive, undefined, undefined, undefined});
-set_primary_archive(File, ArchiveBin, FileInfo)
+set_primary_archive(undefined, undefined, undefined, ParserFun) ->
+ request({set_primary_archive, undefined, undefined, undefined, ParserFun});
+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}).
+ request({set_primary_archive, File, ArchiveBin, FileInfo, ParserFun}).
-spec release_archives() -> 'ok' | {'error', _}.
@@ -318,8 +319,11 @@ loop(State, Parent, Paths) ->
{get_cwd,[_]=Args} ->
{Res,State1} = handle_get_cwd(State, Args),
{Res,State1,Paths};
- {set_primary_archive,File,Bin,FileInfo} ->
- {Res,State1} = handle_set_primary_archive(State, File, Bin, FileInfo),
+ {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),
@@ -359,8 +363,8 @@ handle_get_file(State = #state{loader = efile}, Paths, File) ->
handle_get_file(State = #state{loader = inet}, Paths, File) ->
?SAFE2(inet_get_file_from_port(State, File, Paths), State).
-handle_set_primary_archive(State= #state{loader = efile}, File, Bin, FileInfo) ->
- ?SAFE2(efile_set_primary_archive(State, File, Bin, FileInfo), State).
+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).
@@ -484,8 +488,10 @@ efile_get_file_from_port3(State, File, [P | Paths]) ->
efile_get_file_from_port3(State, _File, []) ->
{{error,enoent},State}.
-efile_set_primary_archive(#state{prim_state = PS} = State, File, Bin, FileInfo) ->
- {Res, PS2} = prim_set_primary_archive(PS, File, Bin, FileInfo),
+efile_set_primary_archive(#state{prim_state = PS} = State, File,
+ ArchiveBin, FileInfo, ParserFun) ->
+ {Res, PS2} = prim_set_primary_archive(PS, File, ArchiveBin,
+ FileInfo, ParserFun),
{Res,State#state{prim_state = PS2}}.
efile_release_archives(#state{prim_state = PS} = State) ->
@@ -791,7 +797,7 @@ prim_release_archives(PS) ->
prim_do_release_archives(PS, [{ArchiveFile, DictVal} | KeyVals], Acc) ->
Res =
case DictVal of
- {primary, _PrimZip, _FI} ->
+ {primary, _PrimZip, _FI, _ParserFun} ->
ok; % Keep primary archive
{Cache, _FI} ->
debug(PS, {release, cache, ArchiveFile}),
@@ -809,7 +815,7 @@ prim_do_release_archives(PS, [], []) ->
prim_do_release_archives(PS, [], Errors) ->
{{error, Errors}, PS#prim_state{primary_archive = undefined}}.
-prim_set_primary_archive(PS, undefined, undefined, undefined) ->
+prim_set_primary_archive(PS, undefined, undefined, undefined, _ParserFun) ->
debug(PS, {set_primary_archive, clean}),
case PS#prim_state.primary_archive of
undefined ->
@@ -817,48 +823,40 @@ prim_set_primary_archive(PS, undefined, undefined, undefined) ->
debug(PS, {return, Res}),
{Res, PS};
ArchiveFile ->
- {primary, PrimZip, _FI} = erase(ArchiveFile),
+ {primary, PrimZip, _FI, _ParserFun2} = erase(ArchiveFile),
ok = prim_zip:close(PrimZip),
PS2 = PS#prim_state{primary_archive = undefined},
Res = {ok, []},
debug(PS2, {return, Res}),
{Res, PS2}
end;
-prim_set_primary_archive(PS, ArchiveFile, ArchiveBin, #file_info{} = FileInfo)
- when is_list(ArchiveFile), is_binary(ArchiveBin) ->
+
+prim_set_primary_archive(PS, ArchiveFile0, ArchiveBin,
+ #file_info{} = FileInfo, ParserFun)
+ when is_list(ArchiveFile0), is_binary(ArchiveBin) ->
%% Try the archive file
- debug(PS, {set_primary_archive, ArchiveFile, byte_size(ArchiveBin)}),
+ debug(PS, {set_primary_archive, ArchiveFile0, byte_size(ArchiveBin)}),
+ ArchiveFile = real_path(absname(ArchiveFile0)),
{Res3, PS3} =
case PS#prim_state.primary_archive of
undefined ->
- Fun =
- fun({Funny, _GI, _GB}, A) ->
- case Funny of
- ["", "nibe", RevApp] -> % Reverse ebin
- %% Collect ebin directories in archive
- Ebin = reverse(RevApp) ++ "/ebin",
- {true, [Ebin | A]};
- _ ->
- {true, A}
- end
- end,
- Ebins0 = [ArchiveFile],
- case open_archive({ArchiveFile, ArchiveBin}, FileInfo, Ebins0, Fun) of
- {ok, PrimZip, {RevEbins, FI, _}} ->
- Ebins = reverse(RevEbins),
+ case load_prim_archive(ArchiveFile, ArchiveBin, FileInfo) of
+ {ok, PrimZip, FI, Ebins} ->
debug(PS, {set_primary_archive, Ebins}),
- put(ArchiveFile, {primary, PrimZip, FI}),
- {{ok, Ebins}, PS#prim_state{primary_archive = ArchiveFile}};
+ put(ArchiveFile, {primary, PrimZip, FI, ParserFun}),
+ {{ok, Ebins},
+ PS#prim_state{primary_archive = ArchiveFile}};
Error ->
debug(PS, {set_primary_archive, Error}),
{Error, PS}
end;
OldArchiveFile ->
debug(PS, {set_primary_archive, clean}),
- {primary, PrimZip, _FI} = erase(OldArchiveFile),
+ {primary, PrimZip, _FI, _ParserFun} = erase(OldArchiveFile),
ok = prim_zip:close(PrimZip),
PS2 = PS#prim_state{primary_archive = undefined},
- prim_set_primary_archive(PS2, ArchiveFile, ArchiveBin, FileInfo)
+ prim_set_primary_archive(PS2, ArchiveFile, ArchiveBin,
+ FileInfo, ParserFun)
end,
debug(PS3, {return, Res3}),
{Res3, PS3}.
@@ -873,11 +871,11 @@ prim_get_file(PS, File) ->
{Res, PS};
{archive, ArchiveFile, FileInArchive} ->
debug(PS, {archive_get_file, ArchiveFile, FileInArchive}),
- FunnyFile = funny_split(FileInArchive, $/),
+ FileComponents = path_split(FileInArchive),
Fun =
- fun({Funny, _GetInfo, GetBin}, Acc) ->
+ fun({Components, _GetInfo, GetBin}, Acc) ->
if
- Funny =:= FunnyFile ->
+ Components =:= FileComponents ->
{false, {ok, GetBin()}};
true ->
{true, Acc}
@@ -900,11 +898,11 @@ prim_list_dir(PS, Dir) ->
{Res, PS};
{archive, ArchiveFile, FileInArchive} ->
debug(PS, {archive_list_dir, ArchiveFile, FileInArchive}),
- FunnyDir = funny_split(FileInArchive, $/),
+ DirComponents = path_split(FileInArchive),
Fun =
- fun({Funny, _GetInfo, _GetBin}, {Status, Names} = Acc) ->
- case Funny of
- [RevName | FD] when FD =:= FunnyDir ->
+ fun({Components, _GetInfo, _GetBin}, {Status, Names} = Acc) ->
+ case Components of
+ [RevName | DC] when DC =:= DirComponents ->
case RevName of
"" ->
%% The listed directory
@@ -914,16 +912,16 @@ prim_list_dir(PS, Dir) ->
Name = reverse(RevName),
{true, {Status, [Name | Names]}}
end;
- ["", RevName | FD] when FD =:= FunnyDir ->
+ ["", RevName | DC] when DC =:= DirComponents ->
%% Directory
Name = reverse(RevName),
{true, {Status, [Name | Names]}};
- [RevName] when FunnyDir =:= [""] ->
- %% Top file
+ [RevName] when DirComponents =:= [""] ->
+ %% File in top directory
Name = reverse(RevName),
{true, {ok, [Name | Names]}};
- ["", RevName] when FunnyDir =:= [""] ->
- %% Top file
+ ["", RevName] when DirComponents =:= [""] ->
+ %% Directory in top directory
Name = reverse(RevName),
{true, {ok, [Name | Names]}};
_ ->
@@ -962,15 +960,14 @@ prim_read_file_info(PS, File) ->
end;
{archive, ArchiveFile, FileInArchive} ->
debug(PS, {archive_read_file_info, File}),
- FunnyFile = funny_split(FileInArchive, $/),
+ FileComponents = path_split(FileInArchive),
Fun =
- fun({Funny, GetInfo, _GetBin}, Acc) ->
- case Funny of
- [H | T] when H =:= "",
- T =:= FunnyFile ->
+ fun({Components, GetInfo, _GetBin}, Acc) ->
+ case Components of
+ ["" | F] when F =:= FileComponents ->
%% Directory
{false, {ok, GetInfo()}};
- F when F =:= FunnyFile ->
+ F when F =:= FileComponents ->
%% Plain file
{false, {ok, GetInfo()}};
_ ->
@@ -1011,7 +1008,7 @@ apply_archive(PS, Fun, Acc, Archive) ->
%% put(Archive, {Error, FI}),
{Error, PS}
end;
- {primary, PrimZip, FI} ->
+ {primary, PrimZip, FI, ParserFun} ->
case prim_file:read_file_info(Archive) of
{ok, FI2}
when FI#file_info.mtime =:= FI2#file_info.mtime ->
@@ -1022,6 +1019,16 @@ apply_archive(PS, Fun, Acc, Archive) ->
debug(PS, {primary, Error}),
{Error, PS}
end;
+ {ok, FI2} ->
+ ok = clear_cache(Archive, {ok, PrimZip}),
+ case load_prim_archive(Archive, FI2, ParserFun) of
+ {ok, PrimZip2, FI3, _Ebins} ->
+ debug(PS, {cache, {update, Archive}}),
+ put(Archive, {primary, PrimZip2, FI3, ParserFun});
+ Error2 ->
+ debug(PS, {cache, {clear, Error2}})
+ end,
+ apply_archive(PS, Fun, Acc, Archive);
Error ->
debug(PS, {cache, {clear, Error}}),
clear_cache(Archive, {ok, PrimZip}),
@@ -1063,50 +1070,69 @@ open_archive(Archive, Acc, Fun) ->
{error, Reason}
end.
+%% Open the given archive and iterate through all files with an own
+%% wrapper fun in order to identify each file as a component list as
+%% returned from path_split/1.
+%%
+%% In the archive (zip) file, directory elements might or might not be
+%% present. To ensure consistency, a directory element is added if it
+%% does not already exist (ensure_virtual_dirs/6). NOTE that there will
+%% be no such directory element for the top directory of the archive.
open_archive(Archive, FileInfo, Acc, Fun) ->
FakeFI = FileInfo#file_info{type = directory},
Wrapper =
- fun({N, GI, GB}, {A, I, FunnyDirs}) -> % Full iteration at open
- Funny = funny_split(N, $/),
- FunnyDirs2 =
- case Funny of
- ["" | FunnyDir] ->
- [FunnyDir | FunnyDirs];
+ fun({N, GI, GB}, {A, I, Dirs}) ->
+ Components = path_split(N),
+ Dirs2 =
+ case Components of
+ ["" | Dir] ->
+ %% This is a directory
+ [Dir | Dirs];
_ ->
- FunnyDirs
+ %% This is a regular file
+ Dirs
end,
- {Includes, FunnyDirs3, A2} =
- ensure_virtual_dirs(Funny, Fun, FakeFI, [{true, Funny}], FunnyDirs2, A),
- {_Continue, A3} = Fun({Funny, GI, GB}, A2),
- {true, Includes, {A3, I, FunnyDirs3}}
+ {Includes, Dirs3, A2} =
+ ensure_virtual_dirs(Components, Fun, FakeFI,
+ [{true, Components}], Dirs2, A),
+ {_Continue, A3} = Fun({Components, GI, GB}, A2),
+ {true, Includes, {A3, I, Dirs3}}
end,
prim_zip:open(Wrapper, {Acc, FakeFI, []}, Archive).
-ensure_virtual_dirs(Funny, Fun, FakeFI, Includes, FunnyDirs, Acc) ->
- case Funny of
- [_ | FunnyDir] ->
- case lists:member(FunnyDir, FunnyDirs) of % BIF
+ensure_virtual_dirs(Components, Fun, FakeFI, Includes, Dirs, Acc) ->
+ case Components of
+ [_] ->
+ %% Don't add virtual dir for top directory
+ {Includes, Dirs, Acc};
+ [_ | Dir] ->
+ case lists:member(Dir, Dirs) of % BIF
false ->
+ %% The directory does not yet exist - add it
GetInfo = fun() -> FakeFI end,
GetBin = fun() -> <<>> end,
- VirtualDir = ["" | FunnyDir],
+ VirtualDir = ["" | Dir],
Includes2 = [{true, VirtualDir, GetInfo, GetBin} | Includes],
- FunnyDirs2 = [FunnyDir | FunnyDirs],
- {I, F, Acc2} = ensure_virtual_dirs(FunnyDir, Fun, FakeFI, Includes2, FunnyDirs2, Acc),
+ Dirs2 = [Dir | Dirs],
+
+ %% Recursively ensure dir elements on all levels
+ {I, F, Acc2} = ensure_virtual_dirs(Dir, Fun, FakeFI,
+ Includes2, Dirs2, Acc),
+
{_Continue, Acc3} = Fun({VirtualDir, GetInfo, GetBin}, Acc2),
{I, F, Acc3};
true ->
- {reverse(Includes), FunnyDirs, Acc}
- end;
- [] ->
- {reverse(Includes), FunnyDirs, Acc}
+ %% The directory element does already exist
+ %% Recursivly ensure dir elements on all levels
+ ensure_virtual_dirs(Dir,Fun,FakeFI,Includes,Dirs,Acc)
+ end
end.
foldl_archive(PrimZip, Acc, Fun) ->
Wrapper =
- fun({Funny, GI, GB}, A) ->
+ fun({Components, GI, GB}, A) ->
%% Allow partial iteration at foldl
- {Continue, A2} = Fun({Funny, GI, GB}, A),
+ {Continue, A2} = Fun({Components, GI, GB}, A),
{Continue, true, A2}
end,
prim_zip:foldl(Wrapper, Acc, PrimZip).
@@ -1202,17 +1228,32 @@ reverse([A, B]) ->
reverse([A, B | L]) ->
lists:reverse(L, [B, A]). % BIF
-%% Returns all lists in reverse order
-funny_split(List, Sep) ->
- funny_split(List, Sep, [], []).
-
-funny_split([Sep | Tail], Sep, Path, Paths) ->
- funny_split(Tail, Sep, [], [Path | Paths]);
-funny_split([Head | Tail], Sep, Path, Paths) ->
- funny_split(Tail, Sep, [Head | Path], Paths);
-funny_split([], _Sep, Path, Paths) ->
+%% Returns a reversed list of path components, each component itself a
+%% reversed list (string), e.g.
+%% /path/to/file -> ["elif","ot","htap",""]
+%% /path/to/dir/ -> ["","rid","ot","htap",""]
+%% Note the "" marking leading and trailing / (slash).
+path_split(List) ->
+ path_split(List, [], []).
+
+path_split([$/ | Tail], Path, Paths) ->
+ path_split(Tail, [], [Path | Paths]);
+path_split([Head | Tail], Path, Paths) ->
+ path_split(Tail, [Head | Path], Paths);
+path_split([], Path, Paths) ->
[Path | Paths].
+%% The opposite of path_split/1
+path_join(Paths) ->
+ path_join(Paths,[]).
+
+path_join([""],Acc) ->
+ Acc;
+path_join([Path],Acc) ->
+ reverse(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).
@@ -1235,26 +1276,22 @@ do_name_split(undefined, File) ->
%% False match. Assume plain file
{file, File}
end;
-do_name_split(ArchiveFile0, File) ->
+do_name_split(ArchiveFile, File) ->
%% Look first in primary archive
- ArchiveFile = absname(ArchiveFile0),
- case string_match(File, ArchiveFile, []) of
+ case string_match(real_path(File), ArchiveFile, []) of
no_match ->
%% Archive or plain file
do_name_split(undefined, File);
{match, _RevPrimArchiveFile, FileInArchive} ->
%% Primary archive
- case FileInArchive of
- [$/ | FileInArchive2] ->
- {archive, ArchiveFile, FileInArchive2};
- _ ->
- {archive, ArchiveFile, FileInArchive}
- end
+ {archive, ArchiveFile, FileInArchive}
end.
string_match([Char | File], [Char | Archive], RevTop) ->
string_match(File, Archive, [Char | RevTop]);
-string_match(File, [], RevTop) ->
+string_match([] = File, [], RevTop) ->
+ {match, RevTop, File};
+string_match([$/ | File], [], RevTop) ->
{match, RevTop, File};
string_match(_File, _Archive, _RevTop) ->
no_match.
@@ -1314,14 +1351,14 @@ absname(Name) ->
case prim_file:get_cwd() of
{ok, Cwd} ->
Cwd ++ "/" ++ Name2;
- {error, _} ->
+ {error, _} ->
Name2
end;
volumerelative ->
case prim_file:get_cwd() of
{ok, Cwd} ->
absname_vr(Name2, Cwd);
- {error, _} ->
+ {error, _} ->
Name2
end
end.
@@ -1348,14 +1385,7 @@ pathtype(Name) when is_list(Name) ->
{unix, _} ->
unix_pathtype(Name);
{win32, _} ->
- win32_pathtype(Name);
- {vxworks, _} ->
- case vxworks_first(Name) of
- {device, _Rest, _Dev} ->
- absolute;
- _ ->
- relative
- end
+ win32_pathtype(Name)
end.
unix_pathtype(Name) ->
@@ -1380,58 +1410,18 @@ win32_pathtype(Name) ->
win32_pathtype([Char | List++Rest]);
[$/, $/|_] ->
absolute;
- [$\\, $/|_] ->
- absolute;
- [$/, $\\|_] ->
- absolute;
- [$\\, $\\|_] ->
- absolute;
[$/|_] ->
volumerelative;
- [$\\|_] ->
- volumerelative;
[C1, C2, List | Rest] when is_list(List) ->
- pathtype([C1, C2|List ++ Rest]);
+ win32_pathtype([C1, C2|List ++ Rest]);
[_Letter, $:, $/|_] ->
absolute;
- [_Letter, $:, $\\|_] ->
- absolute;
[_Letter, $:|_] ->
volumerelative;
_ ->
relative
end.
-vxworks_first(Name) ->
- case Name of
- [] ->
- {not_device, [], []};
- [$/ | T] ->
- vxworks_first2(device, T, [$/]);
- [$\\ | T] ->
- vxworks_first2(device, T, [$/]);
- [H | T] when is_list(H) ->
- vxworks_first(H ++ T);
- [H | T] ->
- vxworks_first2(not_device, T, [H])
- end.
-
-vxworks_first2(Devicep, Name, FirstComp) ->
- case Name of
- [] ->
- {Devicep, [], FirstComp};
- [$/ |T ] ->
- {Devicep, [$/ | T], FirstComp};
- [$\\ | T] ->
- {Devicep, [$/ | T], FirstComp};
- [$: | T]->
- {device, T, [$: | FirstComp]};
- [H | T] when is_list(H) ->
- vxworks_first2(Devicep, H ++ T, FirstComp);
- [H | T] ->
- vxworks_first2(Devicep, T, [H | FirstComp])
- end.
-
normalize(Name, Acc) ->
case Name of
[List | Rest] when is_list(List) ->
@@ -1445,3 +1435,70 @@ normalize(Name, Acc) ->
[] ->
reverse(Acc)
end.
+
+%% Remove .. and . from the path, e.g.
+%% /path/./to/this/../file -> /path/to/file
+%% This includes resolving symlinks.
+%%
+%% This is done to ensure that paths are totally normalized before
+%% comparing to find out if a file is inside the primary archive or
+%% not.
+real_path(Name) ->
+ real_path(Name,reverse(path_split(Name)),[],[]).
+
+real_path(_Name,[],Acc,_Links) ->
+ path_join(Acc);
+real_path(Name,["."|Paths],Acc,Links) ->
+ real_path(Name,Paths,Acc,Links);
+real_path(Name,[".."|Paths],[""]=Acc,Links) ->
+ %% /.. -> / (can't get higher than root)
+ real_path(Name,Paths,Acc,Links);
+real_path(Name,[".."|Paths],[Prev|Acc],Links) when Prev=/=".." ->
+ real_path(Name,Paths,Acc,Links);
+real_path(Name,[Path|Paths],Acc,Links) ->
+ This = [Path|Acc],
+ ThisFile = path_join(This),
+ case lists:member(ThisFile,Links) of
+ true -> % circular!!
+ Name;
+ false ->
+ case prim_file:read_link(ThisFile) of
+ {ok,Link} ->
+ case reverse(path_split(Link)) of
+ [""|_] = LinkPaths ->
+ real_path(Name,LinkPaths++Paths,[],[ThisFile|Links]);
+ LinkPaths ->
+ real_path(Name,LinkPaths++Paths,Acc,[ThisFile|Links])
+ end;
+ _ ->
+ real_path(Name,Paths,This,Links)
+ end
+ end.
+
+load_prim_archive(ArchiveFile, ArchiveBin, #file_info{}=FileInfo) ->
+ Fun = fun({Components, _GI, _GB}, A) ->
+ case Components of
+ ["", "nibe", RevApp] -> % Reverse ebin
+ %% Collect ebin directories in archive
+ Ebin = lists:reverse(RevApp, "/ebin"),
+ {true, [Ebin | A]};
+ _ ->
+ {true, A}
+ end
+ end,
+ Ebins0 = [ArchiveFile],
+ case open_archive({ArchiveFile, ArchiveBin}, FileInfo,
+ Ebins0, Fun) of
+ {ok, PrimZip, {RevEbins, FI, _}} ->
+ Ebins = reverse(RevEbins),
+ {ok, PrimZip, FI, Ebins};
+ Error ->
+ Error
+ end;
+load_prim_archive(ArchiveFile, FileInfo, ParserFun) ->
+ case ParserFun(ArchiveFile) of
+ {ok, ArchiveBin} ->
+ load_prim_archive(ArchiveFile, ArchiveBin, FileInfo);
+ Error ->
+ Error
+ end.
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 1cbce5b80b..646acf5798 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -48,9 +48,7 @@
-deprecated([hash/2]).
-% Get rid of autoimports of spawn to avoid clashes with ourselves.
--compile({no_auto_import,[spawn/1]}).
--compile({no_auto_import,[spawn/4]}).
+%% Get rid of autoimports of spawn to avoid clashes with ourselves.
-compile({no_auto_import,[spawn_link/1]}).
-compile({no_auto_import,[spawn_link/4]}).
-compile({no_auto_import,[spawn_opt/2]}).
@@ -59,10 +57,2123 @@
-export_type([timestamp/0]).
+-type ext_binary() :: binary().
-type timestamp() :: {MegaSecs :: non_neg_integer(),
Secs :: non_neg_integer(),
MicroSecs :: non_neg_integer()}.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Native code BIF stubs and their types
+%% (BIF's actually implemented in this module goes last in the file)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Exports for all native code stubs
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-export([adler32/1, adler32/2, adler32_combine/3, append_element/2]).
+-export([atom_to_binary/2, atom_to_list/1, binary_part/2, binary_part/3]).
+-export([binary_to_atom/2, binary_to_existing_atom/2, binary_to_list/1]).
+-export([binary_to_list/3, binary_to_term/1, binary_to_term/2]).
+-export([bit_size/1, bitsize/1, bitstr_to_list/1, bitstring_to_list/1]).
+-export([bump_reductions/1, byte_size/1, call_on_load_function/1]).
+-export([cancel_timer/1, check_old_code/1, check_process_code/2, crc32/1]).
+-export([crc32/2, crc32_combine/3, date/0, decode_packet/3]).
+-export([delete_module/1, demonitor/1, demonitor/2, display/1]).
+-export([display_nl/0, display_string/1, dist_exit/3, erase/0, erase/1]).
+-export([error/1, error/2, exit/1, exit/2, external_size/1]).
+-export([external_size/2, finish_after_on_load/2, finish_loading/1, float/1]).
+-export([float_to_list/1, fun_info/2, fun_to_list/1, function_exported/3]).
+-export([garbage_collect/0, garbage_collect/1]).
+-export([garbage_collect_message_area/0, get/0, get/1, 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([integer_to_list/1, iolist_size/1, iolist_to_binary/1]).
+-export([is_alive/0, is_builtin/3, is_process_alive/1, length/1, link/1]).
+-export([list_to_atom/1, list_to_binary/1, list_to_bitstr/1]).
+-export([list_to_bitstring/1, list_to_existing_atom/1, list_to_float/1]).
+-export([list_to_integer/1, list_to_pid/1, list_to_tuple/1, loaded/0]).
+-export([localtime/0, make_ref/0, match_spec_test/3, md5/1, md5_final/1]).
+-export([md5_init/0, md5_update/2, module_loaded/1, monitor/2]).
+-export([monitor_node/2, monitor_node/3, nif_error/1, nif_error/2
+]).
+-export([node/0, node/1, now/0, phash/2, phash2/1, phash2/2]).
+-export([pid_to_list/1, port_close/1, port_command/2, port_command/3]).
+-export([port_connect/2, port_control/3, port_get_data/1]).
+-export([port_set_data/2, port_to_list/1, ports/0]).
+-export([posixtime_to_universaltime/1, pre_loaded/0, prepare_loading/2]).
+-export([process_display/2]).
+-export([process_flag/3, process_info/1, processes/0, purge_module/1]).
+-export([put/2, raise/3, read_timer/1, ref_to_list/1, register/2]).
+-export([registered/0, resume_process/1, round/1, self/0, send_after/3]).
+-export([seq_trace/2, seq_trace_print/1, seq_trace_print/2, setnode/2]).
+-export([setnode/3, size/1, spawn/3, spawn_link/3, split_binary/2]).
+-export([start_timer/3, suspend_process/2, system_monitor/0]).
+-export([system_monitor/1, system_monitor/2, system_profile/0]).
+-export([system_profile/2, throw/1, time/0, trace/3, trace_delivered/1]).
+-export([trace_info/2, trunc/1, tuple_size/1, universaltime/0]).
+-export([universaltime_to_posixtime/1, unlink/1, unregister/1, whereis/1]).
+
+-export([abs/1, append/2, element/2, get_module_info/2, hd/1,
+ is_atom/1, is_binary/1, is_bitstring/1, is_boolean/1,
+ is_float/1, is_function/1, is_function/2, is_integer/1,
+ is_list/1, is_number/1, is_pid/1, is_port/1, is_record/2,
+ is_record/3, is_reference/1, is_tuple/1, load_module/2,
+ load_nif/2, localtime_to_universaltime/2, make_fun/3,
+ make_tuple/2, make_tuple/3, nodes/1, open_port/2,
+ port_call/2, port_call/3, port_info/1, port_info/2, process_flag/2,
+ process_info/2, send/2, send/3, seq_trace_info/1,
+ setelement/3, spawn_opt/1,
+ statistics/1, subtract/2, system_flag/2,
+ term_to_binary/1, term_to_binary/2, tl/1, trace_pattern/2,
+ trace_pattern/3, tuple_to_list/1, system_info/1,
+ universaltime_to_localtime/1]).
+-export([dt_get_tag/0, dt_get_tag_data/0, dt_prepend_vm_tag_data/1, dt_append_vm_tag_data/1,
+ dt_put_tag/1, dt_restore_tag/1, dt_spread_tag/1]).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Simple native code BIFs
+%%% These are here for the types/specs, the real implementation is in the C code.
+%%% The first chunk is originally auto-generated from
+%%% $ERL_TOP/lib/hipe/cerl/erl_bif_types.erl as released in R15B.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+%% types
+
+-type fun_info_item() ::
+ arity |
+ env |
+ index |
+ name |
+ module |
+ new_index |
+ new_uniq |
+ pid |
+ type |
+ uniq.
+
+-type seq_trace_info() ::
+ 'send' |
+ 'receive' |
+ 'print' |
+ 'timestamp' |
+ 'label' |
+ 'serial'.
+
+-type seq_trace_info_returns() ::
+ { seq_trace_info(), non_neg_integer() |
+ boolean() |
+ { non_neg_integer(), non_neg_integer() } } |
+ [].
+
+-type system_profile_option() ::
+ 'exclusive' |
+ 'runnable_ports' |
+ 'runnable_procs' |
+ 'scheduler'.
+
+-type system_monitor_option() ::
+ 'busy_port' |
+ 'busy_dist_port' |
+ {'long_gc', non_neg_integer()} |
+ {'large_heap', non_neg_integer()}.
+
+
+-type raise_stacktrace() ::
+ [{module(), atom(), arity() | [term()]} |
+ {function(), [term()]}] |
+ [{module(), atom(), arity() | [term()], [{atom(),term()}]} |
+ {function(), [term()], [{atom(),term()}]}].
+
+-type bitstring_list() ::
+ maybe_improper_list(byte() | bitstring() | bitstring_list(), bitstring() | []).
+
+-type trace_flag() ::
+ all |
+ send |
+ 'receive' |
+ procs |
+ call |
+ silent |
+ return_to |
+ running |
+ exiting |
+ garbage_collection |
+ timestamp |
+ cpu_timestamp |
+ arity |
+ set_on_spawn |
+ set_on_first_spawn |
+ set_on_link |
+ set_on_first_link |
+ {tracer, pid() | port()}.
+
+-type trace_info_item_result() ::
+ {traced, global | local | false | undefined} |
+ {match_spec, trace_match_spec() | false | undefined} |
+ {meta, pid() | port() | false | undefined | []} |
+ {meta_match_spec, trace_match_spec() | false | undefined} |
+ {call_count, non_neg_integer() | boolean() | undefined} |
+ {call_time, [{pid(), non_neg_integer(),
+ non_neg_integer(), non_neg_integer()}] | boolean() | undefined}.
+
+-type trace_info_flag() ::
+ send |
+ 'receive' |
+ set_on_spawn |
+ call |
+ return_to |
+ procs |
+ set_on_first_spawn |
+ set_on_link |
+ running |
+ garbage_collection |
+ timestamp |
+ arity.
+
+-type trace_info_return() ::
+ undefined |
+ {flags, [trace_info_flag()]} |
+ {tracer, pid() | port() | []} |
+ trace_info_item_result() |
+ {all, [ trace_info_item_result() ] | false | undefined}.
+
+%% Specs and stubs
+%% adler32/1
+-spec erlang:adler32(Data) -> non_neg_integer() when
+ Data :: iodata().
+adler32(_Data) ->
+ erlang:nif_error(undefined).
+
+%% adler32/2
+-spec erlang:adler32(OldAdler, Data) -> non_neg_integer() when
+ OldAdler :: non_neg_integer(),
+ Data :: iodata().
+adler32(_OldAdler, _Data) ->
+ erlang:nif_error(undefined).
+
+%% adler32_combine/3
+-spec erlang:adler32_combine(FirstAdler, SecondAdler, SecondSize) -> non_neg_integer() when
+ FirstAdler :: non_neg_integer(),
+ SecondAdler :: non_neg_integer(),
+ SecondSize :: non_neg_integer().
+adler32_combine(_FirstAdler, _SecondAdler, _SecondSize) ->
+ erlang:nif_error(undefined).
+
+%% append_element/2
+-spec erlang:append_element(Tuple1, Term) -> Tuple2 when
+ Tuple1 :: tuple(),
+ Tuple2 :: tuple(),
+ Term :: term().
+append_element(_Tuple1, _Term) ->
+ erlang:nif_error(undefined).
+
+%% atom_to_binary/2
+-spec atom_to_binary(Atom, Encoding) -> binary() when
+ Atom :: atom(),
+ Encoding :: latin1 | unicode | utf8.
+atom_to_binary(_Atom, _Encoding) ->
+ erlang:nif_error(undefined).
+
+%% atom_to_list/1
+-spec atom_to_list(Atom) -> string() when
+ Atom :: atom().
+atom_to_list(_Atom) ->
+ erlang:nif_error(undefined).
+
+%% binary_part/2
+%% Shadowed by erl_bif_types: erlang:binary_part/2
+-spec binary_part(Subject, PosLen) -> binary() when
+ Subject :: binary(),
+ PosLen :: {Start :: non_neg_integer(), Length :: integer()}.
+binary_part(_Subject, _PosLen) ->
+ erlang:nif_error(undefined).
+
+%% binary_part/3
+%% Shadowed by erl_bif_types: erlang:binary_part/3
+-spec binary_part(Subject, Start, Length) -> binary() when
+ Subject :: binary(),
+ Start :: non_neg_integer(),
+ Length :: integer().
+binary_part(_Subject, _Start, _Length) ->
+ erlang:nif_error(undefined).
+
+%% binary_to_atom/2
+-spec binary_to_atom(Binary, Encoding) -> atom() when
+ Binary :: binary(),
+ Encoding :: latin1 | unicode | utf8.
+binary_to_atom(_Binary, _Encoding) ->
+ erlang:nif_error(undefined).
+
+%% binary_to_existing_atom/2
+-spec binary_to_existing_atom(Binary, Encoding) -> atom() when
+ Binary :: binary(),
+ Encoding :: latin1 | unicode | utf8.
+binary_to_existing_atom(_Binary, _Encoding) ->
+ erlang:nif_error(undefined).
+
+%% binary_to_list/1
+-spec binary_to_list(Binary) -> [byte()] when
+ Binary :: binary().
+binary_to_list(_Binary) ->
+ erlang:nif_error(undefined).
+
+%% binary_to_list/3
+-spec binary_to_list(Binary, Start, Stop) -> [byte()] when
+ Binary :: binary(),
+ Start :: pos_integer(),
+ Stop :: pos_integer().
+binary_to_list(_Binary, _Start, _Stop) ->
+ erlang:nif_error(undefined).
+
+%% binary_to_term/1
+-spec binary_to_term(Binary) -> term() when
+ Binary :: ext_binary().
+binary_to_term(_Binary) ->
+ erlang:nif_error(undefined).
+
+%% binary_to_term/2
+-spec binary_to_term(Binary, Opts) -> term() when
+ Binary :: ext_binary(),
+ Opts :: [safe].
+binary_to_term(_Binary, _Opts) ->
+ erlang:nif_error(undefined).
+
+%% bit_size/1
+%% Shadowed by erl_bif_types: erlang:bit_size/1
+-spec bit_size(Bitstring) -> non_neg_integer() when
+ Bitstring :: bitstring().
+bit_size(_Bitstring) ->
+ erlang:nif_error(undefined).
+
+%% bitsize/1
+-spec bitsize(P1) -> non_neg_integer() when
+ P1 :: bitstring().
+bitsize(_P1) ->
+ erlang:nif_error(undefined).
+
+%% bitstr_to_list/1
+-spec erlang:bitstr_to_list(P1) -> [byte() | bitstring()] when
+ P1 :: bitstring().
+bitstr_to_list(_P1) ->
+ erlang:nif_error(undefined).
+
+%% bitstring_to_list/1
+-spec bitstring_to_list(Bitstring) -> [byte() | bitstring()] when
+ Bitstring :: bitstring().
+bitstring_to_list(_Bitstring) ->
+ erlang:nif_error(undefined).
+
+%% bump_reductions/1
+-spec erlang:bump_reductions(Reductions) -> true when
+ Reductions :: pos_integer().
+bump_reductions(_Reductions) ->
+ erlang:nif_error(undefined).
+
+%% byte_size/1
+%% Shadowed by erl_bif_types: erlang:byte_size/1
+-spec byte_size(Bitstring) -> non_neg_integer() when
+ Bitstring :: bitstring().
+byte_size(_Bitstring) ->
+ erlang:nif_error(undefined).
+
+%% call_on_load_function/1
+-spec erlang:call_on_load_function(P1) -> term() when
+ P1 :: atom().
+call_on_load_function(_P1) ->
+ erlang:nif_error(undefined).
+
+%% cancel_timer/1
+-spec erlang:cancel_timer(TimerRef) -> Time | false when
+ TimerRef :: reference(),
+ Time :: non_neg_integer().
+cancel_timer(_TimerRef) ->
+ erlang:nif_error(undefined).
+
+%% check_old_code/1
+-spec check_old_code(Module) -> boolean() when
+ Module :: module().
+check_old_code(_Module) ->
+ erlang:nif_error(undefined).
+
+%% check_process_code/2
+-spec check_process_code(Pid, Module) -> boolean() when
+ Pid :: pid(),
+ Module :: module().
+check_process_code(_Pid, _Module) ->
+ erlang:nif_error(undefined).
+
+%% crc32/1
+-spec erlang:crc32(Data) -> non_neg_integer() when
+ Data :: iodata().
+crc32(_Data) ->
+ erlang:nif_error(undefined).
+
+%% crc32/2
+-spec erlang:crc32(OldCrc, Data) -> non_neg_integer() when
+ OldCrc :: non_neg_integer(),
+ Data :: iodata().
+crc32(_OldCrc, _Data) ->
+ erlang:nif_error(undefined).
+
+%% crc32_combine/3
+-spec erlang:crc32_combine(FirstCrc, SecondCrc, SecondSize) -> non_neg_integer() when
+ FirstCrc :: non_neg_integer(),
+ SecondCrc :: non_neg_integer(),
+ SecondSize :: non_neg_integer().
+crc32_combine(_FirstCrc, _SecondCrc, _SecondSize) ->
+ erlang:nif_error(undefined).
+
+%% date/0
+-spec date() -> Date when
+ Date :: calendar:date().
+date() ->
+ erlang:nif_error(undefined).
+
+%% decode_packet/3
+-spec erlang:decode_packet(Type, Bin, Options) ->
+ {ok, Packet, Rest} |
+ {more, Length} |
+ {error, Reason} when
+ Type :: 'raw' | 0 | 1 | 2 | 4 | 'asn1' | 'cdr' | 'sunrm' | 'fcgi'
+ | 'tpkt' | 'line' | 'http' | 'http_bin' | 'httph' | 'httph_bin',
+ Bin :: binary(),
+ Options :: [Opt],
+ Opt :: {packet_size, non_neg_integer()}
+ | {line_length, non_neg_integer()},
+ Packet :: binary() | HttpPacket,
+ Rest :: binary(),
+ Length :: non_neg_integer() | undefined,
+ Reason :: term(),
+ HttpPacket :: HttpRequest
+ | HttpResponse
+ | HttpHeader
+ | 'http_eoh'
+ | HttpError,
+ HttpRequest :: {'http_request', HttpMethod, HttpUri, HttpVersion},
+ HttpResponse :: {'http_response', HttpVersion, integer(), HttpString},
+ HttpHeader :: {'http_header',
+ integer(),
+ HttpField,
+ Reserved :: term(),
+ Value :: HttpString},
+ HttpError :: {'http_error', HttpString},
+ HttpMethod :: 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE'
+ | 'TRACE' | HttpString,
+ HttpUri :: '*'
+ | { 'absoluteURI',
+ 'http' | 'https',
+ Host :: HttpString,
+ Port :: inet:port_number() | 'undefined',
+ Path :: HttpString}
+ | {'scheme', Scheme :: HttpString, HttpString}
+ | {'abs_path', HttpString}
+ | HttpString,
+ HttpVersion :: {Major :: non_neg_integer(), Minor :: non_neg_integer()},
+ HttpField :: 'Cache-Control'
+ | 'Connection'
+ | 'Date'
+ | 'Pragma'
+ | 'Transfer-Encoding'
+ | 'Upgrade'
+ | 'Via'
+ | 'Accept'
+ | 'Accept-Charset'
+ | 'Accept-Encoding'
+ | 'Accept-Language'
+ | 'Authorization'
+ | 'From'
+ | 'Host'
+ | 'If-Modified-Since'
+ | 'If-Match'
+ | 'If-None-Match'
+ | 'If-Range'
+ | 'If-Unmodified-Since'
+ | 'Max-Forwards'
+ | 'Proxy-Authorization'
+ | 'Range'
+ | 'Referer'
+ | 'User-Agent'
+ | 'Age'
+ | 'Location'
+ | 'Proxy-Authenticate'
+ | 'Public'
+ | 'Retry-After'
+ | 'Server'
+ | 'Vary'
+ | 'Warning'
+ |'Www-Authenticate'
+ | 'Allow'
+ | 'Content-Base'
+ | 'Content-Encoding'
+ | 'Content-Language'
+ | 'Content-Length'
+ | 'Content-Location'
+ | 'Content-Md5'
+ | 'Content-Range'
+ | 'Content-Type'
+ | 'Etag'
+ | 'Expires'
+ | 'Last-Modified'
+ | 'Accept-Ranges'
+ | 'Set-Cookie'
+ | 'Set-Cookie2'
+ | 'X-Forwarded-For'
+ | 'Cookie'
+ | 'Keep-Alive'
+ | 'Proxy-Connection'
+ | HttpString,
+ HttpString :: string() | binary().
+decode_packet(_Type, _Bin, _Options) ->
+ erlang:nif_error(undefined).
+
+%% delete_module/1
+-spec delete_module(Module) -> true | undefined when
+ Module :: module().
+delete_module(_Module) ->
+ erlang:nif_error(undefined).
+
+%% demonitor/1
+-spec demonitor(MonitorRef) -> true when
+ MonitorRef :: reference().
+demonitor(_MonitorRef) ->
+ erlang:nif_error(undefined).
+
+%% demonitor/2
+-spec demonitor(MonitorRef, OptionList) -> boolean() when
+ MonitorRef :: reference(),
+ OptionList :: [Option],
+ Option :: flush | info.
+demonitor(_MonitorRef, _OptionList) ->
+ erlang:nif_error(undefined).
+
+%% display/1
+-spec erlang:display(Term) -> true when
+ Term :: term().
+display(_Term) ->
+ erlang:nif_error(undefined).
+
+%% display_nl/0
+-spec erlang:display_nl() -> true.
+display_nl() ->
+ erlang:nif_error(undefined).
+
+%% display_string/1
+-spec erlang:display_string(P1) -> true when
+ P1 :: string().
+display_string(_P1) ->
+ erlang:nif_error(undefined).
+
+%% dist_exit/3
+-spec erlang:dist_exit(P1, P2, P3) -> true when
+ P1 :: pid(),
+ P2 :: kill | noconnection | normal,
+ P3 :: pid() | port().
+dist_exit(_P1, _P2, _P3) ->
+ erlang:nif_error(undefined).
+
+%% dt_append_vm_tag_data/1
+-spec erlang:dt_append_vm_tag_data(IoData) -> IoDataRet when
+ IoData :: iodata(),
+ IoDataRet :: iodata().
+dt_append_vm_tag_data(_IoData) ->
+ erlang:nif_error(undefined).
+
+%% dt_get_tag/0
+-spec erlang:dt_get_tag() -> binary() | undefined.
+dt_get_tag() ->
+ erlang:nif_error(undefined).
+
+%% dt_get_tag_data/0
+-spec erlang:dt_get_tag_data() -> binary() | undefined.
+dt_get_tag_data() ->
+ erlang:nif_error(undefined).
+
+%% dt_prepend_vm_tag_data/1
+-spec erlang:dt_prepend_vm_tag_data(IoData) -> IoDataRet when
+ IoData :: iodata(),
+ IoDataRet :: iodata().
+dt_prepend_vm_tag_data(_IoData) ->
+ erlang:nif_error(undefined).
+
+%% dt_put_tag/1
+-spec erlang:dt_put_tag(IoData) -> binary() | undefined when
+ IoData :: iodata().
+dt_put_tag(_IoData) ->
+ erlang:nif_error(undefined).
+
+%% dt_restore_tag/1
+-spec erlang:dt_restore_tag(TagData) -> true when
+ TagData :: term().
+dt_restore_tag(_TagData) ->
+ erlang:nif_error(undefined).
+
+%% dt_spread_tag/1
+-spec erlang:dt_spread_tag(boolean()) -> TagData when
+ TagData :: term().
+dt_spread_tag(_Bool) ->
+ erlang:nif_error(undefined).
+
+%% erase/0
+-spec erase() -> [{Key, Val}] when
+ Key :: term(),
+ Val :: term().
+erase() ->
+ erlang:nif_error(undefined).
+
+%% erase/1
+-spec erase(Key) -> Val | undefined when
+ Key :: term(),
+ Val :: term().
+erase(_Key) ->
+ erlang:nif_error(undefined).
+
+%% error/1
+%% Shadowed by erl_bif_types: erlang:error/1
+-spec error(Reason) -> no_return() when
+ Reason :: term().
+error(_Reason) ->
+ erlang:nif_error(undefined).
+
+%% error/2
+%% Shadowed by erl_bif_types: erlang:error/2
+-spec error(Reason, Args) -> no_return() when
+ Reason :: term(),
+ Args :: [term()].
+error(_Reason, _Args) ->
+ erlang:nif_error(undefined).
+
+%% exit/1
+%% Shadowed by erl_bif_types: erlang:exit/1
+-spec exit(Reason) -> no_return() when
+ Reason :: term().
+exit(_Reason) ->
+ erlang:nif_error(undefined).
+
+%% exit/2
+-spec exit(Pid, Reason) -> true when
+ Pid :: pid() | port(),
+ Reason :: term().
+exit(_Pid, _Reason) ->
+ erlang:nif_error(undefined).
+
+%% external_size/1
+-spec erlang:external_size(Term) -> non_neg_integer() when
+ Term :: term().
+external_size(_Term) ->
+ erlang:nif_error(undefined).
+
+%% external_size/2
+-spec erlang:external_size(Term, Options) -> non_neg_integer() when
+ Term :: term(),
+ Options :: [{minor_version, Version :: non_neg_integer()}].
+external_size(_Term, _Options) ->
+ erlang:nif_error(undefined).
+
+%% finish_loading/2
+-spec erlang:finish_loading(PreparedCodeBinaries) -> ok | Error when
+ PreparedCodeBinaries :: [PreparedCodeBinary],
+ PreparedCodeBinary :: binary(),
+ ModuleList :: [module()],
+ Error :: {not_purged,ModuleList} | {on_load,ModuleList}.
+finish_loading(_List) ->
+ erlang:nif_error(undefined).
+
+%% finish_after_on_load/2
+-spec erlang:finish_after_on_load(P1, P2) -> true when
+ P1 :: atom(),
+ P2 :: boolean().
+finish_after_on_load(_P1, _P2) ->
+ erlang:nif_error(undefined).
+
+%% float/1
+%% Shadowed by erl_bif_types: erlang:float/1
+-spec float(Number) -> float() when
+ Number :: number().
+float(_Number) ->
+ erlang:nif_error(undefined).
+
+%% float_to_list/1
+-spec float_to_list(Float) -> string() when
+ Float :: float().
+float_to_list(_Float) ->
+ erlang:nif_error(undefined).
+
+%% fun_info/2
+-spec erlang:fun_info(Fun, Item) -> {Item, Info} when
+ Fun :: function(),
+ Item :: fun_info_item(),
+ Info :: term().
+fun_info(_Fun, _Item) ->
+ erlang:nif_error(undefined).
+
+%% fun_to_list/1
+-spec erlang:fun_to_list(Fun) -> string() when
+ Fun :: function().
+fun_to_list(_Fun) ->
+ erlang:nif_error(undefined).
+
+%% function_exported/3
+-spec erlang:function_exported(Module, Function, Arity) -> boolean() when
+ Module :: module(),
+ Function :: atom(),
+ Arity :: arity().
+function_exported(_Module, _Function, _Arity) ->
+ erlang:nif_error(undefined).
+
+%% garbage_collect/0
+-spec garbage_collect() -> true.
+garbage_collect() ->
+ erlang:nif_error(undefined).
+
+%% garbage_collect/1
+-spec garbage_collect(Pid) -> boolean() when
+ Pid :: pid().
+garbage_collect(_Pid) ->
+ erlang:nif_error(undefined).
+
+%% garbage_collect_message_area/0
+-spec erlang:garbage_collect_message_area() -> boolean().
+garbage_collect_message_area() ->
+ erlang:nif_error(undefined).
+
+%% get/0
+-spec get() -> [{Key, Val}] when
+ Key :: term(),
+ Val :: term().
+get() ->
+ erlang:nif_error(undefined).
+
+%% get/1
+-spec get(Key) -> Val | undefined when
+ Key :: term(),
+ Val :: term().
+get(_Key) ->
+ erlang:nif_error(undefined).
+
+%% get_keys/1
+-spec get_keys(Val) -> [Key] when
+ Val :: term(),
+ Key :: term().
+get_keys(_Val) ->
+ erlang:nif_error(undefined).
+
+%% get_module_info/1
+-spec erlang:get_module_info(P1) -> [{atom(), [{atom(), term()}]}] when
+ P1 :: atom().
+get_module_info(_P1) ->
+ erlang:nif_error(undefined).
+
+%% get_stacktrace/0
+-spec erlang:get_stacktrace() -> [stack_item()].
+get_stacktrace() ->
+ erlang:nif_error(undefined).
+
+%% group_leader/0
+-spec group_leader() -> pid().
+group_leader() ->
+ erlang:nif_error(undefined).
+
+%% group_leader/2
+-spec group_leader(GroupLeader, Pid) -> true when
+ GroupLeader :: pid(),
+ Pid :: pid().
+group_leader(_GroupLeader, _Pid) ->
+ erlang:nif_error(undefined).
+
+%% halt/0
+%% Shadowed by erl_bif_types: erlang:halt/0
+-spec halt() -> no_return().
+halt() ->
+ erlang:nif_error(undefined).
+
+%% 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/2
+%% Shadowed by erl_bif_types: erlang:halt/2
+-spec halt(Status, Options) -> no_return() when
+ Status :: non_neg_integer() | 'abort' | string(),
+ Options :: [Option],
+ Option :: {flush, boolean()}.
+halt(_Status, _Options) ->
+ erlang:nif_error(undefined).
+
+%% hash/2
+-spec erlang:hash(Term, Range) -> pos_integer() when
+ Term :: term(),
+ Range :: pos_integer().
+hash(_Term, _Range) ->
+ erlang:nif_error(undefined).
+
+%% hibernate/3
+-spec erlang:hibernate(Module, Function, Args) -> no_return() when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()].
+hibernate(_Module, _Function, _Args) ->
+ erlang:nif_error(undefined).
+
+%% integer_to_list/1
+-spec integer_to_list(Integer) -> string() when
+ Integer :: integer().
+integer_to_list(_Integer) ->
+ erlang:nif_error(undefined).
+
+%% iolist_size/1
+-spec iolist_size(Item) -> non_neg_integer() when
+ Item :: iolist() | binary().
+iolist_size(_Item) ->
+ erlang:nif_error(undefined).
+
+%% iolist_to_binary/1
+-spec iolist_to_binary(IoListOrBinary) -> binary() when
+ IoListOrBinary :: iolist() | binary().
+iolist_to_binary(_IoListOrBinary) ->
+ erlang:nif_error(undefined).
+
+%% is_alive/0
+-spec is_alive() -> boolean().
+is_alive() ->
+ erlang:nif_error(undefined).
+
+%% is_builtin/3
+-spec erlang:is_builtin(Module, Function, Arity) -> boolean() when
+ Module :: module(),
+ Function :: atom(),
+ Arity :: arity().
+is_builtin(_Module, _Function, _Arity) ->
+ erlang:nif_error(undefined).
+
+%% is_process_alive/1
+-spec is_process_alive(Pid) -> boolean() when
+ Pid :: pid().
+is_process_alive(_Pid) ->
+ erlang:nif_error(undefined).
+
+%% length/1
+%% Shadowed by erl_bif_types: erlang:length/1
+-spec length(List) -> non_neg_integer() when
+ List :: [term()].
+length(_List) ->
+ erlang:nif_error(undefined).
+
+%% link/1
+-spec link(PidOrPort) -> true when
+ PidOrPort :: pid() | port().
+link(_PidOrPort) ->
+ erlang:nif_error(undefined).
+
+%% list_to_atom/1
+-spec list_to_atom(String) -> atom() when
+ String :: string().
+list_to_atom(_String) ->
+ erlang:nif_error(undefined).
+
+%% list_to_binary/1
+-spec list_to_binary(IoList) -> binary() when
+ IoList :: iolist().
+list_to_binary(_IoList) ->
+ erlang:nif_error(undefined).
+
+%% list_to_bitstr/1
+-spec erlang:list_to_bitstr(P1) -> bitstring() when
+ P1 :: bitstring_list().
+list_to_bitstr(_P1) ->
+ erlang:nif_error(undefined).
+
+%% list_to_bitstring/1
+-spec list_to_bitstring(BitstringList) -> bitstring() when
+ BitstringList :: bitstring_list().
+list_to_bitstring(_BitstringList) ->
+ erlang:nif_error(undefined).
+
+%% list_to_existing_atom/1
+-spec list_to_existing_atom(String) -> atom() when
+ String :: string().
+list_to_existing_atom(_String) ->
+ erlang:nif_error(undefined).
+
+%% list_to_float/1
+-spec list_to_float(String) -> float() when
+ String :: string().
+list_to_float(_String) ->
+ erlang:nif_error(undefined).
+
+%% list_to_integer/1
+-spec list_to_integer(String) -> integer() when
+ String :: string().
+list_to_integer(_String) ->
+ erlang:nif_error(undefined).
+
+%% list_to_pid/1
+-spec list_to_pid(String) -> pid() when
+ String :: string().
+list_to_pid(_String) ->
+ erlang:nif_error(undefined).
+
+%% list_to_tuple/1
+-spec list_to_tuple(List) -> tuple() when
+ List :: [term()].
+list_to_tuple(_List) ->
+ erlang:nif_error(undefined).
+
+%% loaded/0
+-spec erlang:loaded() -> [Module] when
+ Module :: module().
+loaded() ->
+ erlang:nif_error(undefined).
+
+%% localtime/0
+-spec erlang:localtime() -> DateTime when
+ DateTime :: calendar:datetime().
+localtime() ->
+ erlang:nif_error(undefined).
+
+%% make_ref/0
+-spec make_ref() -> reference().
+make_ref() ->
+ 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,
+ TestResult :: {ok, term(), [return_trace], [ {error | warning, string()} ]} | {error, [ {error | warning, string()} ]}.
+match_spec_test(_P1, _P2, _P3) ->
+ erlang:nif_error(undefined).
+
+%% md5/1
+-spec erlang:md5(Data) -> Digest when
+ Data :: iodata(),
+ Digest :: binary().
+md5(_Data) ->
+ erlang:nif_error(undefined).
+
+%% md5_final/1
+-spec erlang:md5_final(Context) -> Digest when
+ Context :: binary(),
+ Digest :: binary().
+md5_final(_Context) ->
+ erlang:nif_error(undefined).
+
+%% md5_init/0
+-spec erlang:md5_init() -> Context when
+ Context :: binary().
+md5_init() ->
+ erlang:nif_error(undefined).
+
+%% md5_update/2
+-spec erlang:md5_update(Context, Data) -> NewContext when
+ Context :: binary(),
+ Data :: iodata(),
+ NewContext :: binary().
+md5_update(_Context, _Data) ->
+ erlang:nif_error(undefined).
+
+%% module_loaded/1
+-spec module_loaded(Module) -> boolean() when
+ Module :: module().
+module_loaded(_Module) ->
+ erlang:nif_error(undefined).
+
+%% monitor/2
+-spec monitor(Type, Item) -> MonitorRef when
+ Type :: process,
+ Item :: pid() | Module | {Module, Node},
+ Module :: module(),
+ Node :: node(),
+ MonitorRef :: reference().
+monitor(_Type, _Item) ->
+ erlang:nif_error(undefined).
+
+%% monitor_node/2
+-spec monitor_node(Node, Flag) -> true when
+ Node :: node(),
+ Flag :: boolean().
+monitor_node(_Node, _Flag) ->
+ erlang:nif_error(undefined).
+
+%% monitor_node/3
+-spec erlang:monitor_node(Node, Flag, Options) -> true when
+ Node :: node(),
+ Flag :: boolean(),
+ Options :: [Option],
+ Option :: allow_passive_connect.
+monitor_node(_Node, _Flag, _Options) ->
+ erlang:nif_error(undefined).
+
+%% nif_error/1
+%% Shadowed by erl_bif_types: erlang:nif_error/1
+-spec erlang:nif_error(Reason) -> no_return() when
+ Reason :: term().
+nif_error(_Reason) ->
+ erlang:nif_error(undefined).
+
+%% nif_error/2
+%% Shadowed by erl_bif_types: erlang:nif_error/2
+-spec erlang:nif_error(Reason, Args) -> no_return() when
+ Reason :: term(),
+ Args :: [term()].
+nif_error(_Reason, _Args) ->
+ erlang:nif_error(undefined).
+
+%% node/0
+%% Shadowed by erl_bif_types: erlang:node/0
+-spec node() -> Node when
+ Node :: node().
+node() ->
+ erlang:nif_error(undefined).
+
+%% node/1
+%% Shadowed by erl_bif_types: erlang:node/1
+-spec node(Arg) -> Node when
+ Arg :: pid() | port() | reference(),
+ Node :: node().
+node(_Arg) ->
+ erlang:nif_error(undefined).
+
+%% now/0
+-spec now() -> Timestamp when
+ Timestamp :: timestamp().
+now() ->
+ erlang:nif_error(undefined).
+
+%% phash/2
+-spec erlang:phash(Term, Range) -> Hash when
+ Term :: term(),
+ Range :: pos_integer(),
+ Hash :: pos_integer().
+phash(_Term, _Range) ->
+ erlang:nif_error(undefined).
+
+%% phash2/1
+-spec erlang:phash2(Term) -> Hash when
+ Term :: term(),
+ Hash :: non_neg_integer().
+phash2(_Term) ->
+ erlang:nif_error(undefined).
+
+%% phash2/2
+-spec erlang:phash2(Term, Range) -> Hash when
+ Term :: term(),
+ Range :: pos_integer(),
+ Hash :: non_neg_integer().
+phash2(_Term, _Range) ->
+ erlang:nif_error(undefined).
+
+%% pid_to_list/1
+-spec pid_to_list(Pid) -> string() when
+ Pid :: pid().
+pid_to_list(_Pid) ->
+ erlang:nif_error(undefined).
+
+%% port_close/1
+-spec port_close(Port) -> true when
+ Port :: port() | atom().
+port_close(_Port) ->
+ erlang:nif_error(undefined).
+
+%% port_command/2
+-spec port_command(Port, Data) -> true when
+ Port :: port() | atom(),
+ Data :: iodata().
+port_command(_Port, _Data) ->
+ erlang:nif_error(undefined).
+
+%% port_command/3
+-spec port_command(Port, Data, OptionList) -> boolean() when
+ Port :: port() | atom(),
+ Data :: iodata(),
+ OptionList :: [Option],
+ Option :: force | nosuspend.
+port_command(_Port, _Data, _OptionList) ->
+ erlang:nif_error(undefined).
+
+%% port_connect/2
+-spec port_connect(Port, Pid) -> true when
+ Port :: port() | atom(),
+ Pid :: pid().
+port_connect(_Port, _Pid) ->
+ erlang:nif_error(undefined).
+
+%% port_control/3
+-spec port_control(Port, Operation, Data) -> Res when
+ Port :: port() | atom(),
+ Operation :: integer(),
+ Data :: iodata(),
+ Res :: string() | binary().
+port_control(_Port, _Operation, _Data) ->
+ erlang:nif_error(undefined).
+
+%% port_get_data/1
+-spec erlang:port_get_data(P1) -> term() when
+ P1 :: port() | atom().
+port_get_data(_P1) ->
+ erlang:nif_error(undefined).
+
+%% port_set_data/2
+-spec erlang:port_set_data(P1, P2) -> true when
+ P1 :: port() | atom(),
+ P2 :: term().
+port_set_data(_P1, _P2) ->
+ erlang:nif_error(undefined).
+
+%% port_to_list/1
+-spec erlang:port_to_list(Port) -> string() when
+ Port :: port().
+port_to_list(_Port) ->
+ erlang:nif_error(undefined).
+
+%% ports/0
+-spec erlang:ports() -> [port()].
+ports() ->
+ erlang:nif_error(undefined).
+
+%% posixtime_to_universaltime/1
+-spec erlang:posixtime_to_universaltime(P1) -> {calendar:date(), calendar:time()} when
+ P1 :: integer().
+posixtime_to_universaltime(_P1) ->
+ erlang:nif_error(undefined).
+
+%% prepare_loading/2
+-spec erlang:prepare_loading(Module, Code) -> PreparedCode | {error, Reason} when
+ Module :: module(),
+ Code :: binary(),
+ PreparedCode :: binary(),
+ Reason :: bad_file.
+prepare_loading(_Module, _Code) ->
+ erlang:nif_error(undefined).
+
+%% pre_loaded/0
+-spec pre_loaded() -> [module()].
+pre_loaded() ->
+ erlang:nif_error(undefined).
+
+%% process_display/2
+-spec erlang:process_display(Pid, Type) -> true when
+ Pid :: pid(),
+ Type :: backtrace.
+process_display(_Pid, _Type) ->
+ erlang:nif_error(undefined).
+
+%% process_flag/3
+-spec process_flag(Pid, Flag, Value) -> OldValue when
+ Pid :: pid(),
+ Flag :: save_calls,
+ Value :: non_neg_integer(),
+ OldValue :: non_neg_integer().
+process_flag(_Pid, _Flag, _Value) ->
+ erlang:nif_error(undefined).
+
+%% process_info/1
+-spec process_info(Pid) -> Info when
+ Pid :: pid(),
+ Info :: [InfoTuple] | undefined,
+ InfoTuple :: process_info_result_item().
+process_info(_Pid) ->
+ erlang:nif_error(undefined).
+
+%% processes/0
+-spec processes() -> [pid()].
+processes() ->
+ erlang:nif_error(undefined).
+
+%% purge_module/1
+-spec purge_module(Module) -> true when
+ Module :: atom().
+purge_module(_Module) ->
+ erlang:nif_error(undefined).
+
+%% put/2
+-spec put(Key, Val) -> term() when
+ Key :: term(),
+ Val :: term().
+put(_Key, _Val) ->
+ erlang:nif_error(undefined).
+
+%% raise/3
+-spec erlang:raise(Class, Reason, Stacktrace) -> no_return() when
+ Class :: error | exit | throw,
+ Reason :: term(),
+ Stacktrace :: raise_stacktrace().
+raise(_Class, _Reason, _Stacktrace) ->
+ erlang:nif_error(undefined).
+
+%% read_timer/1
+-spec erlang:read_timer(TimerRef) -> non_neg_integer() | false when
+ TimerRef :: reference().
+read_timer(_TimerRef) ->
+ erlang:nif_error(undefined).
+
+%% ref_to_list/1
+-spec erlang:ref_to_list(Ref) -> string() when
+ Ref :: reference().
+ref_to_list(_Ref) ->
+ erlang:nif_error(undefined).
+
+%% register/2
+-spec register(RegName, PidOrPort) -> true when
+ RegName :: atom(),
+ PidOrPort :: port() | pid().
+register(_RegName, _PidOrPort) ->
+ erlang:nif_error(undefined).
+
+%% registered/0
+-spec registered() -> [RegName] when
+ RegName :: atom().
+registered() ->
+ erlang:nif_error(undefined).
+
+%% resume_process/1
+-spec erlang:resume_process(Suspendee) -> true when
+ Suspendee :: pid().
+resume_process(_Suspendee) ->
+ erlang:nif_error(undefined).
+
+%% round/1
+%% Shadowed by erl_bif_types: erlang:round/1
+-spec round(Number) -> integer() when
+ Number :: number().
+round(_Number) ->
+ erlang:nif_error(undefined).
+
+%% self/0
+%% Shadowed by erl_bif_types: erlang:self/0
+-spec self() -> pid().
+self() ->
+ erlang:nif_error(undefined).
+
+%% send_after/3
+-spec erlang:send_after(Time, Dest, Msg) -> TimerRef when
+ Time :: non_neg_integer(),
+ Dest :: pid() | atom(),
+ Msg :: term(),
+ TimerRef :: reference().
+send_after(_Time, _Dest, _Msg) ->
+ erlang:nif_error(undefined).
+
+%% seq_trace/2
+-spec erlang:seq_trace(P1, P2) -> seq_trace_info_returns() | {term(), term(), term(), term(), term()} when
+ P1 :: atom(),
+ P2 :: boolean() | {integer(), integer()} | integer() | [].
+seq_trace(_P1, _P2) ->
+ erlang:nif_error(undefined).
+
+%% seq_trace_print/1
+-spec erlang:seq_trace_print(P1) -> boolean() when
+ P1 :: term().
+seq_trace_print(_P1) ->
+ erlang:nif_error(undefined).
+
+%% seq_trace_print/2
+-spec erlang:seq_trace_print(P1, P2) -> boolean() when
+ P1 :: atom() | integer(),
+ P2 :: term().
+seq_trace_print(_P1, _P2) ->
+ erlang:nif_error(undefined).
+
+%% setnode/2
+-spec erlang:setnode(P1, P2) -> true when
+ P1 :: atom(),
+ P2 :: integer().
+setnode(_P1, _P2) ->
+ erlang:nif_error(undefined).
+
+%% setnode/3
+-spec erlang:setnode(P1, P2, P3) -> true when
+ P1 :: atom(),
+ P2 :: port(),
+ P3 :: {term(), term(), term(), term()}.
+setnode(_P1, _P2, _P3) ->
+ erlang:nif_error(undefined).
+
+%% size/1
+%% Shadowed by erl_bif_types: erlang:size/1
+-spec size(Item) -> non_neg_integer() when
+ Item :: tuple() | binary().
+size(_Item) ->
+ erlang:nif_error(undefined).
+
+%% spawn/3
+-spec spawn(Module, Function, Args) -> pid() when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()].
+spawn(_Module, _Function, _Args) ->
+ erlang:nif_error(undefined).
+
+%% spawn_link/3
+-spec spawn_link(Module, Function, Args) -> pid() when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()].
+spawn_link(_Module, _Function, _Args) ->
+ erlang:nif_error(undefined).
+
+%% split_binary/2
+-spec split_binary(Bin, Pos) -> {binary(), binary()} when
+ Bin :: binary(),
+ Pos :: non_neg_integer().
+split_binary(_Bin, _Pos) ->
+ erlang:nif_error(undefined).
+
+%% start_timer/3
+-spec erlang:start_timer(Time, Dest, Msg) -> TimerRef when
+ Time :: non_neg_integer(),
+ Dest :: pid() | atom(),
+ Msg :: term(),
+ TimerRef :: reference().
+start_timer(_Time, _Dest, _Msg) ->
+ erlang:nif_error(undefined).
+
+%% suspend_process/2
+-spec erlang:suspend_process(Suspendee, OptList) -> boolean() when
+ Suspendee :: pid(),
+ OptList :: [Opt],
+ Opt :: unless_suspending | asynchronous.
+suspend_process(_Suspendee, _OptList) ->
+ erlang:nif_error(undefined).
+
+%% system_monitor/0
+-spec erlang:system_monitor() -> MonSettings when
+ MonSettings :: undefined | { MonitorPid, Options },
+ MonitorPid :: pid(),
+ Options :: [ system_monitor_option() ].
+system_monitor() ->
+ erlang:nif_error(undefined).
+
+%% system_monitor/1
+-spec erlang:system_monitor(Arg) -> MonSettings when
+ Arg :: undefined | { MonitorPid, Options },
+ MonSettings :: undefined | { MonitorPid, Options },
+ MonitorPid :: pid(),
+ Options :: [ system_monitor_option() ].
+system_monitor(_Arg) ->
+ erlang:nif_error(undefined).
+
+%% system_monitor/2
+-spec erlang:system_monitor(MonitorPid, Options) -> MonSettings when
+ MonitorPid :: pid(),
+ Options :: [ system_monitor_option() ],
+ MonSettings :: undefined | { OldMonitorPid, OldOptions },
+ OldMonitorPid :: pid(),
+ OldOptions :: [ system_monitor_option() ].
+system_monitor(_MonitorPid, _Options) ->
+ erlang:nif_error(undefined).
+
+%% system_profile/0
+-spec erlang:system_profile() -> ProfilerSettings when
+ ProfilerSettings :: undefined | { ProfilerPid, Options},
+ ProfilerPid :: pid() | port(),
+ Options :: [ system_profile_option() ].
+system_profile() ->
+ erlang:nif_error(undefined).
+
+%% system_profile/2
+-spec erlang:system_profile(ProfilerPid, Options) -> ProfilerSettings when
+ ProfilerPid :: pid() | port() | undefined,
+ Options :: [ system_profile_option() ],
+ ProfilerSettings :: undefined | { pid() | port(), [ system_profile_option() ]}.
+system_profile(_ProfilerPid, _Options) ->
+ erlang:nif_error(undefined).
+
+%% throw/1
+%% Shadowed by erl_bif_types: erlang:throw/1
+-spec throw(Any) -> no_return() when
+ Any :: term().
+throw(_Any) ->
+ erlang:nif_error(undefined).
+
+%% time/0
+-spec time() -> Time when
+ Time :: calendar:time().
+time() ->
+ erlang:nif_error(undefined).
+
+%% trace/3
+-spec erlang:trace(PidSpec, How, FlagList) -> integer() when
+ PidSpec :: pid() | existing | new | all,
+ How :: boolean(),
+ FlagList :: [trace_flag()].
+trace(_PidSpec, _How, _FlagList) ->
+ erlang:nif_error(undefined).
+
+%% trace_delivered/1
+-spec erlang:trace_delivered(Tracee) -> Ref when
+ Tracee :: pid() | all,
+ Ref :: reference().
+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,
+ Module :: module(),
+ Function :: atom(),
+ Arity :: arity(),
+ Item :: flags | tracer | traced | match_spec | meta | meta_match_spec | call_count | call_time | all,
+ Res :: trace_info_return().
+trace_info(_PidOrFunc, _Item) ->
+ erlang:nif_error(undefined).
+
+%% trunc/1
+%% Shadowed by erl_bif_types: erlang:trunc/1
+-spec trunc(Number) -> integer() when
+ Number :: number().
+trunc(_Number) ->
+ erlang:nif_error(undefined).
+
+%% tuple_size/1
+%% Shadowed by erl_bif_types: erlang:tuple_size/1
+-spec tuple_size(Tuple) -> non_neg_integer() when
+ Tuple :: tuple().
+tuple_size(_Tuple) ->
+ erlang:nif_error(undefined).
+
+%% universaltime/0
+-spec erlang:universaltime() -> DateTime when
+ DateTime :: calendar:datetime().
+universaltime() ->
+ erlang:nif_error(undefined).
+
+%% universaltime_to_posixtime/1
+-spec erlang:universaltime_to_posixtime(P1) -> integer() when
+ P1 :: {calendar:date(), calendar:time()}.
+universaltime_to_posixtime(_P1) ->
+ erlang:nif_error(undefined).
+
+%% unlink/1
+-spec unlink(Id) -> true when
+ Id :: pid() | port().
+unlink(_Id) ->
+ erlang:nif_error(undefined).
+
+%% unregister/1
+-spec unregister(RegName) -> true when
+ RegName :: atom().
+unregister(_RegName) ->
+ erlang:nif_error(undefined).
+
+%% whereis/1
+-spec whereis(RegName) -> pid() | port() | undefined when
+ RegName :: atom().
+whereis(_RegName) ->
+ erlang:nif_error(undefined).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% More complicated native code BIFs
+%%% These are here for the types/specs, the real implementation is in the C code.
+%%% This chunk is handwritten, i.e. contains more complicated specs.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% types and specs
+
+%% Shadowed by erl_bif_types: erlang:abs/1
+-spec abs(Float) -> float() when
+ Float :: float();
+ (Int) -> non_neg_integer() when
+ Int :: integer().
+abs(_Number) ->
+ erlang:nif_error(undefined).
+
+%% Not documented
+%% Shadowed by erl_bif_types: erlang:append/2
+-spec erlang:append(List,Tail) -> maybe_improper_list() when
+ List :: [term()],
+ Tail :: term().
+append(_List,_Tail) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:element/2
+-spec element(N, Tuple) -> term() when
+ N :: pos_integer(),
+ Tuple :: tuple().
+element(_N, _Tuple) ->
+ erlang:nif_error(undefined).
+
+%% Not documented
+-spec erlang:get_module_info(Module, Item) -> ModuleInfo when
+ Module :: atom(),
+ Item :: module | imports | exports | functions | attributes | compile | native_addresses,
+ ModuleInfo :: atom() | [] | [{atom(), arity()}] | [{atom(), term()}] | [{atom(), arity(), integer()}].
+get_module_info(_Module, _Item) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:hd/1
+-spec hd(List) -> term() when
+ List :: [term(), ...].
+hd(_List) ->
+ erlang:nif_error(undefined).
+
+%% erlang:info/1 no longer exists!
+
+%% Shadowed by erl_bif_types: erlang:is_atom/1
+-spec is_atom(Term) -> boolean() when
+ Term :: term().
+is_atom(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_binary/1
+-spec is_binary(Term) -> boolean() when
+ Term :: term().
+is_binary(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_bitstring/1
+-spec is_bitstring(Term) -> boolean() when
+ Term :: term().
+is_bitstring(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_boolean/1
+-spec is_boolean(Term) -> boolean() when
+ Term :: term().
+is_boolean(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_float/1
+-spec is_float(Term) -> boolean() when
+ Term :: term().
+is_float(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_function/1
+-spec is_function(Term) -> boolean() when
+ Term :: term().
+is_function(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_function/2
+-spec is_function(Term, Arity) -> boolean() when
+ Term :: term(),
+ Arity :: arity().
+is_function(_Term, _Arity) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_integer/1
+-spec is_integer(Term) -> boolean() when
+ Term :: term().
+is_integer(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_list/1
+-spec is_list(Term) -> boolean() when
+ Term :: term().
+is_list(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_number/1
+-spec is_number(Term) -> boolean() when
+ Term :: term().
+is_number(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_pid/1
+-spec is_pid(Term) -> boolean() when
+ Term :: term().
+is_pid(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_port/1
+-spec is_port(Term) -> boolean() when
+ Term :: term().
+is_port(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_record/2
+-spec is_record(Term,RecordTag) -> boolean() when
+ Term :: term(),
+ RecordTag :: atom().
+is_record(_Term,_RecordTag) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_record/3
+-spec is_record(Term,RecordTag,Size) -> boolean() when
+ Term :: term(),
+ RecordTag :: atom(),
+ Size :: non_neg_integer().
+is_record(_Term,_RecordTag,_Size) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_reference/1
+-spec is_reference(Term) -> boolean() when
+ Term :: term().
+is_reference(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_tuple/1
+-spec is_tuple(Term) -> boolean() when
+ Term :: term().
+is_tuple(_Term) ->
+ erlang:nif_error(undefined).
+
+-spec load_module(Module, Binary) -> {module, Module} | {error, Reason} when
+ Module :: module(),
+ Binary :: binary(),
+ Reason :: badfile | not_purged | on_load.
+load_module(Mod, Code) ->
+ case erlang:prepare_loading(Mod, Code) of
+ {error,_}=Error ->
+ Error;
+ Bin when erlang:is_binary(Bin) ->
+ case erlang:finish_loading([Bin]) of
+ ok ->
+ {module,Mod};
+ {Error,[Mod]} ->
+ {error,Error}
+ end
+ end.
+
+-spec erlang:load_nif(Path, LoadInfo) -> ok | Error when
+ Path :: string(),
+ LoadInfo :: term(),
+ Error :: {error, {Reason, Text :: string()}},
+ Reason :: load_failed | bad_lib | load | reload | upgrade | old_code.
+load_nif(_Path, _LoadInfo) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:localtime_to_universaltime(Localtime, IsDst) -> Universaltime when
+ Localtime :: calendar:datetime(),
+ Universaltime :: calendar:datetime(),
+ IsDst :: true | false | undefined.
+localtime_to_universaltime(_Localtime, _IsDst) ->
+ erlang:nif_error(undefined).
+
+%% CHECK! Why the strange very thorough specification of the error
+%% condition with disallowed arity in erl_bif_types?
+%% Not documented
+-spec erlang:make_fun(Module, Function, Arity) -> function() when
+ Module :: atom(),
+ Function :: atom(),
+ Arity :: arity().
+make_fun(_Module,_Function, _Arity) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:make_tuple/2
+-spec erlang:make_tuple(Arity, InitialValue) -> tuple() when
+ Arity :: arity(),
+ InitialValue :: term().
+make_tuple(_Arity,_InitialValue) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:make_tuple/3
+-spec erlang:make_tuple(Arity, DefaultValue, InitList) -> tuple() when
+ Arity :: arity(),
+ DefaultValue :: term(),
+ InitList :: [{Position :: pos_integer(), term()}].
+make_tuple(_Arity,_DefaultValue,_InitList) ->
+ erlang:nif_error(undefined).
+
+-spec nodes(Arg) -> Nodes when
+ Arg :: NodeType | [NodeType],
+ NodeType :: visible | hidden | connected | this | known,
+ Nodes :: [node()].
+nodes(_Arg) ->
+ erlang:nif_error(undefined).
+
+-spec open_port(PortName, PortSettings) -> port() when
+ PortName :: {spawn, Command :: string()} |
+ {spawn_driver, Command :: [byte()]} |
+ {spawn_executable, FileName :: file:name() } |
+ {fd, In :: non_neg_integer(), Out :: non_neg_integer()},
+ PortSettings :: [Opt],
+ Opt :: {packet, N :: 1 | 2 | 4}
+ | stream
+ | {line, L :: non_neg_integer()}
+ | {cd, Dir :: string()}
+ | {env, Env :: [{Name :: string(), Val :: string() | false}]}
+ | {args, [string() | binary()]}
+ | {arg0, string() | binary()}
+ | exit_status
+ | use_stdio
+ | nouse_stdio
+ | stderr_to_stdout
+ | in
+ | out
+ | binary
+ | eof.
+open_port(_PortName,_PortSettings) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:port_call/2
+-spec erlang:port_call(Port, Data) -> term() when
+ Port :: port() | atom(),
+ Data :: term().
+port_call(_Port, _Data) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:port_call/3
+-spec erlang:port_call(Port, Operation, Data) -> term() when
+ Port :: port() | atom(),
+ Operation :: integer(),
+ Data :: term().
+port_call(_Port, _Operation, _Data) ->
+ erlang:nif_error(undefined).
+
+-type port_info_item() ::
+ registered_name |
+ id |
+ connected |
+ links |
+ name |
+ input |
+ output |
+ os_pid.
+
+-type port_info_result_item() ::
+ {registered_name, RegName :: atom()} |
+ {id, Index :: non_neg_integer()} |
+ {connected, Pid :: pid()} |
+ {links, Pids :: [pid()]} |
+ {name, String :: string()} |
+ {input, Bytes :: non_neg_integer()} |
+ {output, Bytes :: non_neg_integer()} |
+ {os_pid, OsPid :: non_neg_integer() | 'undefined'}.
+
+%% Shadowed by erl_bif_types: erlang:port_info/1
+-spec erlang:port_info(Port) -> Result when
+ Port :: port() | atom(),
+ Result :: [port_info_result_item()] | undefined.
+port_info(_Result) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:port_info/2
+-spec erlang:port_info(Port, Item) -> Result when
+ Port :: port() | atom(),
+ Item :: port_info_item(),
+ Result :: port_info_result_item() | undefined.
+port_info(_Result, _Item) ->
+ erlang:nif_error(undefined).
+
+-type priority_level() ::
+ low | normal | high | max.
+
+-spec process_flag(trap_exit, Boolean) -> OldBoolean when
+ Boolean :: boolean(),
+ OldBoolean :: boolean();
+ (error_handler, Module) -> OldModule when
+ Module :: atom(),
+ OldModule :: atom();
+ (min_heap_size, MinHeapSize) -> OldMinHeapSize when
+ MinHeapSize :: non_neg_integer(),
+ OldMinHeapSize :: non_neg_integer();
+ (min_bin_vheap_size, MinBinVHeapSize) -> OldMinBinVHeapSize when
+ MinBinVHeapSize :: non_neg_integer(),
+ OldMinBinVHeapSize :: non_neg_integer();
+ (priority, Level) -> OldLevel when
+ Level :: priority_level(),
+ OldLevel :: priority_level();
+ (save_calls, N) -> OldN when
+ N :: 0..10000,
+ OldN :: 0..10000;
+ (sensitive, Boolean) -> OldBoolean when
+ Boolean :: boolean(),
+ OldBoolean :: boolean();
+ %% Deliberately not documented.
+ ({monitor_nodes, term()}, term()) -> term();
+ (monitor_nodes, term()) -> term().
+
+process_flag(_Flag, _Value) ->
+ erlang:nif_error(undefined).
+
+-type process_info_item() ::
+ backtrace |
+ binary |
+ catchlevel |
+ current_function |
+ current_location |
+ current_stacktrace |
+ dictionary |
+ error_handler |
+ garbage_collection |
+ group_leader |
+ heap_size |
+ initial_call |
+ links |
+ last_calls |
+ memory |
+ message_que_len |
+ messages |
+ min_heap_size |
+ min_bin_vheap_size |
+ monitored_by |
+ monitors |
+ priority |
+ reductions |
+ registered_name |
+ sequential_trace_token |
+ stack_size |
+ status |
+ suspending |
+ total_heap_size |
+ trace |
+ trap_exit.
+
+-type process_info_result_item() ::
+ {backtrace, Bin :: binary()} |
+ {binary, BinInfo :: [{non_neg_integer(),
+ non_neg_integer(),
+ non_neg_integer()}]} |
+ {catchlevel, CatchLevel :: non_neg_integer()} |
+ {current_function,
+ {Module :: module(), Function :: atom(), Arity :: arity()}} |
+ {current_location,
+ {Module :: module(), Function :: atom(), Arity :: arity(),
+ Location :: [{file, Filename :: string()} | % not a stack_item()!
+ {line, Line :: pos_integer()}]}} |
+ {current_stacktrace, Stack :: [stack_item()]} |
+ {dictionary, Dictionary :: [{Key :: term(), Value :: term()}]} |
+ {error_handler, Module :: module()} |
+ {garbage_collection, GCInfo :: [{atom(),non_neg_integer()}]} |
+ {group_leader, GroupLeader :: pid()} |
+ {heap_size, Size :: non_neg_integer()} |
+ {initial_call, mfa()} |
+ {links, Pids :: [pid()]} |
+ {last_calls, false | (Calls :: [mfa()])} |
+ {memory, Size :: non_neg_integer()} |
+ {message_que_len, MessageQueueLen :: non_neg_integer()} |
+ {messages, MessageQueue :: [term()]} |
+ {min_heap_size, MinHeapSize :: non_neg_integer()} |
+ {min_bin_vheap_size, MinBinVHeapSize :: non_neg_integer()} |
+ {monitored_by, Pids :: [pid()]} |
+ {monitors,
+ Monitors :: [{process, Pid :: pid() |
+ {RegName :: atom(), Node :: node()}}]} |
+ {priority, Level :: priority_level()} |
+ {reductions, Number :: non_neg_integer()} |
+ {registered_name, Atom :: atom()} |
+ {sequential_trace_token, [] | (SequentialTraceToken :: term())} |
+ {stack_size, Size :: non_neg_integer()} |
+ {status, Status :: exiting | garbage_collecting | waiting | running | runnable | suspended} |
+ {suspending,
+ SuspendeeList :: [{Suspendee :: pid(),
+ ActiveSuspendCount :: non_neg_integer(),
+ OutstandingSuspendCount ::non_neg_integer()}]} |
+ {total_heap_size, Size :: non_neg_integer()} |
+ {trace, InternalTraceFlags :: non_neg_integer()} |
+ {trap_exit, Boolean :: boolean()}.
+
+-type stack_item() ::
+ {Module :: module(),
+ Function :: atom(),
+ Arity :: arity() | (Args :: [term()]),
+ Location :: [{file, Filename :: string()} |
+ {line, Line :: pos_integer()}]}.
+
+-spec process_info(Pid, Item) ->
+ InfoTuple | [] | undefined when
+ Pid :: pid(),
+ Item :: process_info_item(),
+ InfoTuple :: process_info_result_item();
+ (Pid, ItemList) -> InfoTupleList | [] | undefined when
+ Pid :: pid(),
+ ItemList :: [Item],
+ Item :: process_info_item(),
+ InfoTupleList :: [InfoTuple],
+ InfoTuple :: process_info_result_item().
+process_info(_Pid,_ItemSpec) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:send(Dest, Msg) -> Msg when
+ Dest :: dst(),
+ Msg :: term().
+send(_Dest,_Msg) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:send(Dest, Msg, Options) -> Res when
+ Dest :: dst(),
+ Msg :: term(),
+ Options :: [nosuspend | noconnect],
+ Res :: ok | nosuspend | noconnect.
+send(_Dest,_Msg,_Options) ->
+ erlang:nif_error(undefined).
+
+%% Not documented
+-spec erlang:seq_trace_info(send) -> {send, boolean()};
+ ('receive') -> {'receive', boolean()};
+ (print) -> {print, boolean()};
+ (timestamp) -> {timestamp, boolean()};
+ (label) -> [] | {label, non_neg_integer()};
+ (serial) -> [] | {serial, {non_neg_integer(), non_neg_integer()}}.
+seq_trace_info(_What) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:setelement/3
+-spec setelement(Index, Tuple1, Value) -> Tuple2 when
+ Index :: pos_integer(),
+ Tuple1 :: tuple(),
+ Tuple2 :: tuple(),
+ Value :: term().
+setelement(_Index, _Tuple1, _Value) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:spawn_opt({Module, Function, Args, Options}) -> pid() | {pid(), reference()} when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Options :: [Option],
+ Option :: link | monitor | {priority, Level}
+ | {fullsweep_after, Number :: non_neg_integer()}
+ | {min_heap_size, Size :: non_neg_integer()}
+ | {min_bin_vheap_size, VSize :: non_neg_integer()},
+ Level :: low | normal | high.
+spawn_opt(_Tuple) ->
+ erlang:nif_error(undefined).
+
+-spec statistics(context_switches) -> {ContextSwitches,0} when
+ ContextSwitches :: non_neg_integer();
+ (exact_reductions) -> {Total_Exact_Reductions,
+ Exact_Reductions_Since_Last_Call} when
+ Total_Exact_Reductions :: non_neg_integer(),
+ Exact_Reductions_Since_Last_Call :: non_neg_integer();
+ (garbage_collection) -> {Number_of_GCs, Words_Reclaimed, 0} when
+ Number_of_GCs :: non_neg_integer(),
+ Words_Reclaimed :: non_neg_integer();
+ (io) -> {{input, Input}, {output, Output}} when
+ Input :: non_neg_integer(),
+ Output :: non_neg_integer();
+ (reductions) -> {Total_Reductions,
+ Reductions_Since_Last_Call} when
+ Total_Reductions :: non_neg_integer(),
+ Reductions_Since_Last_Call :: non_neg_integer();
+ (run_queue) -> non_neg_integer();
+ (runtime) -> {Total_Run_Time, Time_Since_Last_Call} when
+ Total_Run_Time :: non_neg_integer(),
+ Time_Since_Last_Call :: non_neg_integer();
+ (scheduler_wall_time) -> [{SchedulerId, ActiveTime, TotalTime}] | undefined when
+ SchedulerId :: pos_integer(),
+ ActiveTime :: non_neg_integer(),
+ TotalTime :: non_neg_integer();
+ (wall_clock) -> {Total_Wallclock_Time,
+ Wallclock_Time_Since_Last_Call} when
+ Total_Wallclock_Time :: non_neg_integer(),
+ Wallclock_Time_Since_Last_Call :: non_neg_integer().
+statistics(_Item) ->
+ erlang:nif_error(undefined).
+
+%% Not documented
+%% Shadowed by erl_bif_types: erlang:subtract/2
+-spec erlang:subtract([term()], [term()]) -> [term()].
+subtract(_,_) ->
+ erlang:nif_error(undefined).
+
+-type scheduler_bind_type() ::
+ 'no_node_processor_spread' |
+ 'no_node_thread_spread' |
+ 'no_spread' |
+ 'processor_spread' |
+ 'spread' |
+ 'thread_spread' |
+ 'thread_no_node_processor_spread' |
+ 'unbound'.
+
+-spec erlang:system_flag(backtrace_depth, Depth) -> OldDepth when
+ Depth :: non_neg_integer(),
+ OldDepth :: non_neg_integer();
+ (cpu_topology, CpuTopology) -> OldCpuTopology when
+ CpuTopology :: cpu_topology(),
+ OldCpuTopology :: cpu_topology();
+ (fullsweep_after, Number) -> OldNumber when
+ Number :: non_neg_integer(),
+ OldNumber :: non_neg_integer();
+ (min_heap_size, MinHeapSize) -> OldMinHeapSize when
+ MinHeapSize :: non_neg_integer(),
+ OldMinHeapSize :: non_neg_integer();
+ (min_bin_vheap_size, MinBinVHeapSize) ->
+ OldMinBinVHeapSize when
+ MinBinVHeapSize :: non_neg_integer(),
+ OldMinBinVHeapSize :: non_neg_integer();
+ (multi_scheduling, BlockState) -> OldBlockState when
+ BlockState :: block | unblock,
+ OldBlockState :: block | unblock | enabled;
+ (scheduler_bind_type, How) -> OldBindType when
+ How :: scheduler_bind_type() | default_bind,
+ OldBindType :: scheduler_bind_type();
+ (scheduler_wall_time, Boolean) -> OldBoolean when
+ Boolean :: boolean(),
+ OldBoolean :: boolean();
+ (schedulers_online, SchedulersOnline) ->
+ OldSchedulersOnline when
+ SchedulersOnline :: pos_integer(),
+ OldSchedulersOnline :: pos_integer();
+ (trace_control_word, TCW) -> OldTCW when
+ TCW :: non_neg_integer(),
+ OldTCW :: non_neg_integer();
+ %% These are deliberately not documented
+ (internal_cpu_topology, term()) -> term();
+ (sequential_tracer, pid() | port() | false) -> pid() | port() | false;
+ (1,0) -> true.
+
+system_flag(_Flag, _Value) ->
+ erlang:nif_error(undefined).
+
+-spec term_to_binary(Term) -> ext_binary() when
+ Term :: term().
+term_to_binary(_Term) ->
+ erlang:nif_error(undefined).
+
+-spec term_to_binary(Term, Options) -> ext_binary() when
+ Term :: term(),
+ Options :: [compressed |
+ {compressed, Level :: 0..9} |
+ {minor_version, Version :: 0..1} ].
+term_to_binary(_Term, _Options) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:tl/1
+-spec tl(List) -> term() when
+ List :: [term(), ...].
+tl(_List) ->
+ erlang:nif_error(undefined).
+
+-type trace_pattern_mfa() ::
+ {atom(),atom(),arity() | '_'} | on_load.
+-type trace_match_spec() ::
+ [{[term()] | '_' ,[term()],[term()]}].
+
+-spec erlang:trace_pattern(MFA, MatchSpec) -> non_neg_integer() when
+ MFA :: trace_pattern_mfa(),
+ MatchSpec :: (MatchSpecList :: trace_match_spec())
+ | boolean()
+ | restart
+ | pause.
+trace_pattern(_MFA, _MatchSpec) ->
+ erlang:nif_error(undefined).
+
+-type trace_pattern_flag() ::
+ global | local |
+ meta | {meta, Pid :: pid()} |
+ call_count |
+ call_time.
+
+-spec erlang:trace_pattern(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).
+
+%% Shadowed by erl_bif_types: erlang:tuple_to_list/1
+-spec tuple_to_list(Tuple) -> [term()] when
+ Tuple :: tuple().
+tuple_to_list(_Tuple) ->
+ erlang:nif_error(undefined).
+
+-type cpu_topology() ::
+ [LevelEntry :: level_entry()] | undefined.
+-type level_entry() ::
+ {LevelTag :: level_tag(), SubLevel :: sub_level()}
+ | {LevelTag :: level_tag(),
+ InfoList :: info_list(),
+ SubLevel :: sub_level()}.
+-type level_tag() :: core | node | processor | thread.
+-type sub_level() :: [LevelEntry :: level_entry()]
+ | (LogicalCpuId :: {logical, non_neg_integer()}).
+-type info_list() :: [].
+
+%% Note: changing the ordering number of a clause will change the docs!
+%% Shadowed by erl_bif_types: erlang:system_info/1
+-spec erlang:system_info
+ (allocated_areas) -> [ tuple() ];
+ (allocator) ->
+ {Allocator, Version, Features, Settings} when
+ Allocator :: undefined | glibc,
+ Version :: [non_neg_integer()],
+ Features :: [atom()],
+ Settings :: [{Subsystem :: atom(),
+ [{Parameter :: atom(),
+ Value :: term()}]}];
+ (alloc_util_allocators) -> [Alloc] when
+ Alloc :: atom();
+ ({allocator, Alloc}) -> [_] when %% More or less anything
+ Alloc :: atom();
+ ({allocator_sizes, Alloc}) -> [_] when %% More or less anything
+ Alloc :: atom();
+ (build_type) -> opt | debug | purify | quantify | purecov |
+ gcov | valgrind | gprof | lcnt;
+ (c_compiler_used) -> {atom(), term()};
+ (check_io) -> [_];
+ (compat_rel) -> integer();
+ (cpu_topology) -> CpuTopology when
+ CpuTopology :: cpu_topology();
+ ({cpu_topology, defined | detected | used}) -> CpuTopology when
+ CpuTopology :: cpu_topology();
+ (creation) -> integer();
+ (debug_compiled) -> boolean();
+ (dist) -> binary();
+ (dist_ctrl) -> {Node :: node(),
+ ControllingEntity :: port() | pid()};
+ (driver_version) -> string();
+ (dynamic_trace) -> none | dtrace | systemtap;
+ (dynamic_trace_probes) -> boolean();
+ (elib_malloc) -> false;
+ (dist_buf_busy_limit) -> non_neg_integer();
+ (fullsweep_after) -> {fullsweep_after, non_neg_integer()};
+ (garbage_collection) -> [{atom(), integer()}];
+ (heap_sizes) -> [non_neg_integer()];
+ (heap_type) -> private;
+ (info) -> binary();
+ (kernel_poll) -> boolean();
+ (loaded) -> binary();
+ (logical_processors |
+ logical_processors_available |
+ logical_processors_online) -> unknown | pos_integer();
+ (machine) -> string();
+ (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_blockers) -> [PID :: pid()];
+ (otp_release) -> string();
+ (process_count) -> pos_integer();
+ (process_limit) -> pos_integer();
+ (procs) -> binary();
+ (scheduler_bind_type) -> spread |
+ processor_spread |
+ thread_spread |
+ thread_no_node_processor_spread |
+ no_node_processor_spread |
+ no_node_thread_spread |
+ no_spread |
+ unbound;
+ (scheduler_bindings) -> tuple();
+ (scheduler_id) -> SchedulerId :: pos_integer();
+ (schedulers | schedulers_online) -> pos_integer();
+ (smp_support) -> boolean();
+ (system_version) -> string();
+ (system_architecture) -> string();
+ (threads) -> boolean();
+ (thread_pool_size) -> non_neg_integer();
+ (trace_control_word) -> non_neg_integer();
+ (update_cpu_info) -> changed | unchanged;
+ (version) -> string();
+ (wordsize | {wordsize, internal} | {wordsize, external}) -> 4 | 8.
+system_info(_Item) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:universaltime_to_localtime(Universaltime) -> Localtime when
+ Localtime :: calendar:datetime(),
+ Universaltime :: calendar:datetime().
+universaltime_to_localtime(_Universaltime) ->
+ erlang:nif_error(undefined).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% End of native code BIFs
+%%% Actual Erlang implementation of some BIF's follow
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
%%--------------------------------------------------------------------------
-spec apply(Fun, Args) -> term() when
@@ -71,6 +2182,7 @@
apply(Fun, Args) ->
erlang:apply(Fun, Args).
+%% Shadowed by erl_bif_types: erlang:apply/3
-spec apply(Module, Function, Args) -> term() when
Module :: module(),
Function :: atom(),
@@ -82,42 +2194,42 @@ apply(Mod, Name, Args) ->
-spec spawn(Fun) -> pid() when
Fun :: function().
-spawn(F) when is_function(F) ->
- spawn(erlang, apply, [F, []]);
-spawn({M,F}=MF) when is_atom(M), is_atom(F) ->
- spawn(erlang, apply, [MF, []]);
+spawn(F) when erlang:is_function(F) ->
+ erlang:spawn(erlang, apply, [F, []]);
+spawn({M,F}=MF) when erlang:is_atom(M), erlang:is_atom(F) ->
+ erlang:spawn(erlang, apply, [MF, []]);
spawn(F) ->
erlang:error(badarg, [F]).
-spec spawn(Node, Fun) -> pid() when
Node :: node(),
Fun :: function().
-spawn(N, F) when N =:= node() ->
- spawn(F);
-spawn(N, F) when is_function(F) ->
- spawn(N, erlang, apply, [F, []]);
-spawn(N, {M,F}=MF) when is_atom(M), is_atom(F) ->
- spawn(N, erlang, apply, [MF, []]);
+spawn(N, F) when N =:= erlang:node() ->
+ erlang:spawn(F);
+spawn(N, F) when erlang:is_function(F) ->
+ erlang:spawn(N, erlang, apply, [F, []]);
+spawn(N, {M,F}=MF) when erlang:is_atom(M), erlang:is_atom(F) ->
+ erlang:spawn(N, erlang, apply, [MF, []]);
spawn(N, F) ->
erlang:error(badarg, [N, F]).
-spec spawn_link(Fun) -> pid() when
Fun :: function().
-spawn_link(F) when is_function(F) ->
- spawn_link(erlang, apply, [F, []]);
-spawn_link({M,F}=MF) when is_atom(M), is_atom(F) ->
- spawn_link(erlang, apply, [MF, []]);
+spawn_link(F) when erlang:is_function(F) ->
+ erlang:spawn_link(erlang, apply, [F, []]);
+spawn_link({M,F}=MF) when erlang:is_atom(M), erlang:is_atom(F) ->
+ erlang:spawn_link(erlang, apply, [MF, []]);
spawn_link(F) ->
erlang:error(badarg, [F]).
-spec spawn_link(Node, Fun) -> pid() when
Node :: node(),
Fun :: function().
-spawn_link(N, F) when N =:= node() ->
+spawn_link(N, F) when N =:= erlang:node() ->
spawn_link(F);
-spawn_link(N, F) when is_function(F) ->
+spawn_link(N, F) when erlang:is_function(F) ->
spawn_link(N, erlang, apply, [F, []]);
-spawn_link(N, {M,F}=MF) when is_atom(M), is_atom(F) ->
+spawn_link(N, {M,F}=MF) when erlang:is_atom(M), erlang:is_atom(F) ->
spawn_link(N, erlang, apply, [MF, []]);
spawn_link(N, F) ->
erlang:error(badarg, [N, F]).
@@ -126,7 +2238,7 @@ spawn_link(N, F) ->
-spec spawn_monitor(Fun) -> {pid(), reference()} when
Fun :: function().
-spawn_monitor(F) when is_function(F, 0) ->
+spawn_monitor(F) when erlang:is_function(F, 0) ->
erlang:spawn_opt({erlang,apply,[F,[]],[monitor]});
spawn_monitor(F) ->
erlang:error(badarg, [F]).
@@ -135,7 +2247,9 @@ spawn_monitor(F) ->
Module :: module(),
Function :: atom(),
Args :: [term()].
-spawn_monitor(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
+spawn_monitor(M, F, A) when erlang:is_atom(M),
+ erlang:is_atom(F),
+ erlang:is_list(A) ->
erlang:spawn_opt({M,F,A,[monitor]});
spawn_monitor(M, F, A) ->
erlang:error(badarg, [M,F,A]).
@@ -148,9 +2262,9 @@ spawn_monitor(M, F, A) ->
| {min_heap_size, Size :: non_neg_integer()}
| {min_bin_vheap_size, VSize :: non_neg_integer()},
Level :: low | normal | high.
-spawn_opt(F, O) when is_function(F) ->
+spawn_opt(F, O) when erlang:is_function(F) ->
spawn_opt(erlang, apply, [F, []], O);
-spawn_opt({M,F}=MF, O) when is_atom(M), is_atom(F) ->
+spawn_opt({M,F}=MF, O) when erlang:is_atom(M), erlang:is_atom(F) ->
spawn_opt(erlang, apply, [MF, []], O);
spawn_opt({M,F,A}, O) -> % For (undocumented) backward compatibility
spawn_opt(M, F, A, O);
@@ -166,11 +2280,11 @@ spawn_opt(F, O) ->
| {min_heap_size, Size :: non_neg_integer()}
| {min_bin_vheap_size, VSize :: non_neg_integer()},
Level :: low | normal | high.
-spawn_opt(N, F, O) when N =:= node() ->
+spawn_opt(N, F, O) when N =:= erlang:node() ->
spawn_opt(F, O);
-spawn_opt(N, F, O) when is_function(F) ->
+spawn_opt(N, F, O) when erlang:is_function(F) ->
spawn_opt(N, erlang, apply, [F, []], O);
-spawn_opt(N, {M,F}=MF, O) when is_atom(M), is_atom(F) ->
+spawn_opt(N, {M,F}=MF, O) when erlang:is_atom(M), erlang:is_atom(F) ->
spawn_opt(N, erlang, apply, [MF, []], O);
spawn_opt(N, F, O) ->
erlang:error(badarg, [N, F, O]).
@@ -182,9 +2296,14 @@ spawn_opt(N, F, O) ->
Module :: module(),
Function :: atom(),
Args :: [term()].
-spawn(N,M,F,A) when N =:= node(), is_atom(M), is_atom(F), is_list(A) ->
- spawn(M,F,A);
-spawn(N,M,F,A) when is_atom(N), is_atom(M), is_atom(F) ->
+spawn(N,M,F,A) when N =:= erlang:node(),
+ erlang:is_atom(M),
+ erlang:is_atom(F),
+ erlang:is_list(A) ->
+ erlang:spawn(M,F,A);
+spawn(N,M,F,A) when erlang:is_atom(N),
+ erlang:is_atom(M),
+ erlang:is_atom(F) ->
case is_well_formed_list(A) of
true ->
ok;
@@ -192,9 +2311,9 @@ spawn(N,M,F,A) when is_atom(N), is_atom(M), is_atom(F) ->
erlang:error(badarg, [N, M, F, A])
end,
case catch gen_server:call({net_kernel,N},
- {spawn,M,F,A,group_leader()},
+ {spawn,M,F,A,erlang:group_leader()},
infinity) of
- Pid when is_pid(Pid) ->
+ Pid when erlang:is_pid(Pid) ->
Pid;
Error ->
case remote_spawn_error(Error, {no_link, N, M, F, A, []}) of
@@ -212,9 +2331,14 @@ spawn(N,M,F,A) ->
Module :: module(),
Function :: atom(),
Args :: [term()].
-spawn_link(N,M,F,A) when N =:= node(), is_atom(M), is_atom(F), is_list(A) ->
- spawn_link(M,F,A);
-spawn_link(N,M,F,A) when is_atom(N), is_atom(M), is_atom(F) ->
+spawn_link(N,M,F,A) when N =:= erlang:node(),
+ erlang:is_atom(M),
+ erlang:is_atom(F),
+ erlang:is_list(A) ->
+ erlang:spawn_link(M,F,A);
+spawn_link(N,M,F,A) when erlang:is_atom(N),
+ erlang:is_atom(M),
+ erlang:is_atom(F) ->
case is_well_formed_list(A) of
true ->
ok;
@@ -222,9 +2346,9 @@ spawn_link(N,M,F,A) when is_atom(N), is_atom(M), is_atom(F) ->
erlang:error(badarg, [N, M, F, A])
end,
case catch gen_server:call({net_kernel,N},
- {spawn_link,M,F,A,group_leader()},
+ {spawn_link,M,F,A,erlang:group_leader()},
infinity) of
- Pid when is_pid(Pid) ->
+ Pid when erlang:is_pid(Pid) ->
Pid;
Error ->
case remote_spawn_error(Error, {link, N, M, F, A, []}) of
@@ -268,11 +2392,13 @@ spawn_opt(M, F, A, Opts) ->
| {min_heap_size, Size :: non_neg_integer()}
| {min_bin_vheap_size, VSize :: non_neg_integer()},
Level :: low | normal | high.
-spawn_opt(N, M, F, A, O) when N =:= node(),
- is_atom(M), is_atom(F), is_list(A),
- is_list(O) ->
+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) ->
spawn_opt(M, F, A, O);
-spawn_opt(N, M, F, A, O) when is_atom(N), is_atom(M), is_atom(F) ->
+spawn_opt(N, M, F, A, O) when erlang:is_atom(N),
+ erlang:is_atom(M),
+ erlang:is_atom(F) ->
case {is_well_formed_list(A), is_well_formed_list(O)} of
{true, true} ->
ok;
@@ -291,9 +2417,9 @@ spawn_opt(N, M, F, A, O) when is_atom(N), is_atom(M), is_atom(F) ->
{no_link,[]},
O),
case catch gen_server:call({net_kernel,N},
- {spawn_opt,M,F,A,NO,L,group_leader()},
+ {spawn_opt,M,F,A,NO,L,erlang:group_leader()},
infinity) of
- Pid when is_pid(Pid) ->
+ Pid when erlang:is_pid(Pid) ->
Pid;
Error ->
case remote_spawn_error(Error, {L, N, M, F, A, NO}) of
@@ -331,11 +2457,11 @@ is_well_formed_list(_) ->
crasher(Node,Mod,Fun,Args,[],Reason) ->
error_logger:warning_msg("** Can not start ~w:~w,~w on ~w **~n",
[Mod,Fun,Args,Node]),
- exit(Reason);
+ erlang:exit(Reason);
crasher(Node,Mod,Fun,Args,Opts,Reason) ->
error_logger:warning_msg("** Can not start ~w:~w,~w (~w) on ~w **~n",
[Mod,Fun,Args,Opts,Node]),
- exit(Reason).
+ erlang:exit(Reason).
-spec erlang:yield() -> 'true'.
yield() ->
@@ -356,7 +2482,7 @@ disconnect_node(Node) ->
Item :: arity | env | index | name
| module | new_index | new_uniq | pid | type | uniq,
Info :: term().
-fun_info(Fun) when is_function(Fun) ->
+fun_info(Fun) when erlang:is_function(Fun) ->
Keys = [type,env,arity,name,uniq,index,new_uniq,new_index,module,pid],
fun_info_1(Keys, Fun, []).
@@ -388,11 +2514,9 @@ send_nosuspend(Pid, Msg, Opts) ->
_ -> false
end.
--spec erlang:localtime_to_universaltime({Date1, Time1}) -> {Date2, Time2} when
- Date1 :: calendar:date(),
- Date2 :: calendar:date(),
- Time1 :: calendar:time(),
- Time2 :: calendar:time().
+-spec erlang:localtime_to_universaltime(Localtime) -> Universaltime when
+ Localtime :: calendar:datetime(),
+ Universaltime :: calendar:datetime().
localtime_to_universaltime(Localtime) ->
erlang:localtime_to_universaltime(Localtime, undefined).
@@ -412,25 +2536,25 @@ suspend_process(P) ->
%% reactivate the command.
%%
--spec dlink(pid() | port()) -> 'true'.
+-spec erlang:dlink(pid() | port()) -> 'true'.
dlink(Pid) ->
- case net_kernel:connect(node(Pid)) of
- true -> link(Pid);
- false -> erlang:dist_exit(self(), noconnection, Pid), true
+ case net_kernel:connect(erlang:node(Pid)) of
+ true -> erlang:link(Pid);
+ false -> erlang:dist_exit(erlang:self(), noconnection, Pid), true
end.
%% Can this ever happen?
--spec dunlink(identifier()) -> 'true'.
+-spec erlang:dunlink(identifier()) -> 'true'.
dunlink(Pid) ->
- case net_kernel:connect(node(Pid)) of
- true -> unlink(Pid);
+ case net_kernel:connect(erlang:node(Pid)) of
+ true -> erlang:unlink(Pid);
false -> true
end.
dmonitor_node(Node, Flag, []) ->
case net_kernel:connect(Node) of
true -> erlang:monitor_node(Node, Flag, []);
- false -> self() ! {nodedown, Node}, true
+ false -> erlang:self() ! {nodedown, Node}, true
end;
dmonitor_node(Node, Flag, Opts) ->
@@ -438,31 +2562,31 @@ dmonitor_node(Node, Flag, Opts) ->
true ->
case net_kernel:passive_cnct(Node) of
true -> erlang:monitor_node(Node, Flag, Opts);
- false -> self() ! {nodedown, Node}, true
+ false -> erlang:self() ! {nodedown, Node}, true
end;
_ ->
dmonitor_node(Node,Flag,[])
end.
dgroup_leader(Leader, Pid) ->
- case net_kernel:connect(node(Pid)) of
- true -> group_leader(Leader, Pid);
+ case net_kernel:connect(erlang:node(Pid)) of
+ true -> erlang:group_leader(Leader, Pid);
false -> true %% bad arg ?
end.
dexit(Pid, Reason) ->
- case net_kernel:connect(node(Pid)) of
- true -> exit(Pid, Reason);
+ case net_kernel:connect(erlang:node(Pid)) of
+ true -> erlang:exit(Pid, Reason);
false -> true
end.
-dsend(Pid, Msg) when is_pid(Pid) ->
- case net_kernel:connect(node(Pid)) of
+dsend(Pid, Msg) when erlang:is_pid(Pid) ->
+ case net_kernel:connect(erlang:node(Pid)) of
true -> erlang:send(Pid, Msg);
false -> Msg
end;
-dsend(Port, Msg) when is_port(Port) ->
- case net_kernel:connect(node(Port)) of
+dsend(Port, Msg) when erlang:is_port(Port) ->
+ case net_kernel:connect(erlang:node(Port)) of
true -> erlang:send(Port, Msg);
false -> Msg
end;
@@ -473,13 +2597,13 @@ dsend({Name, Node}, Msg) ->
ignored -> Msg % Not distributed.
end.
-dsend(Pid, Msg, Opts) when is_pid(Pid) ->
- case net_kernel:connect(node(Pid)) of
+dsend(Pid, Msg, Opts) when erlang:is_pid(Pid) ->
+ case net_kernel:connect(erlang:node(Pid)) of
true -> erlang:send(Pid, Msg, Opts);
false -> ok
end;
-dsend(Port, Msg, Opts) when is_port(Port) ->
- case net_kernel:connect(node(Port)) of
+dsend(Port, Msg, Opts) when erlang:is_port(Port) ->
+ case net_kernel:connect(erlang:node(Port)) of
true -> erlang:send(Port, Msg, Opts);
false -> ok
end;
@@ -490,21 +2614,23 @@ dsend({Name, Node}, Msg, Opts) ->
ignored -> ok % Not distributed.
end.
--spec dmonitor_p('process', pid() | {atom(),atom()}) -> reference().
+-spec erlang:dmonitor_p('process', pid() | {atom(),atom()}) -> reference().
dmonitor_p(process, ProcSpec) ->
%% ProcSpec = pid() | {atom(),atom()}
%% ProcSpec CANNOT be an atom because a locally registered process
%% is never handled here.
Node = case ProcSpec of
- {S,N} when is_atom(S), is_atom(N), N =/= node() -> N;
- _ when is_pid(ProcSpec) -> node(ProcSpec)
+ {S,N} when erlang:is_atom(S),
+ erlang:is_atom(N),
+ N =/= erlang:node() -> N;
+ _ when erlang:is_pid(ProcSpec) -> erlang:node(ProcSpec)
end,
case net_kernel:connect(Node) of
true ->
erlang:monitor(process, ProcSpec);
false ->
- Ref = make_ref(),
- self() ! {'DOWN', Ref, process, ProcSpec, noconnection},
+ Ref = erlang:make_ref(),
+ erlang:self() ! {'DOWN', Ref, process, ProcSpec, noconnection},
Ref
end.
@@ -512,7 +2638,7 @@ dmonitor_p(process, ProcSpec) ->
%% Trap function used when modified timing has been enabled.
%%
--spec delay_trap(Result, timeout()) -> Result.
+-spec erlang:delay_trap(Result, timeout()) -> Result.
delay_trap(Result, 0) -> erlang:yield(), Result;
delay_trap(Result, Timeout) -> receive after Timeout -> Result end.
@@ -526,12 +2652,12 @@ delay_trap(Result, Timeout) -> receive after Timeout -> Result end.
-spec erlang:set_cookie(Node, Cookie) -> true when
Node :: node(),
Cookie :: atom().
-set_cookie(Node, C) when Node =/= nonode@nohost, is_atom(Node) ->
- case is_atom(C) of
+set_cookie(Node, C) when Node =/= nonode@nohost, erlang:is_atom(Node) ->
+ case erlang:is_atom(C) of
true ->
auth:set_cookie(Node, C);
false ->
- error(badarg)
+ erlang:error(badarg)
end.
-spec erlang:get_cookie() -> Cookie | nocookie when
@@ -545,7 +2671,8 @@ get_cookie() ->
integer_to_list(I, 10) ->
erlang:integer_to_list(I);
integer_to_list(I, Base)
- when is_integer(I), is_integer(Base), Base >= 2, Base =< 1+$Z-$A+10 ->
+ when erlang:is_integer(I), erlang:is_integer(Base),
+ Base >= 2, Base =< 1+$Z-$A+10 ->
if I < 0 ->
[$-|integer_to_list(-I, Base, [])];
true ->
@@ -575,9 +2702,10 @@ integer_to_list(I0, Base, R0) ->
list_to_integer(L, 10) ->
erlang:list_to_integer(L);
list_to_integer(L, Base)
- when is_list(L), is_integer(Base), Base >= 2, Base =< 1+$Z-$A+10 ->
+ when erlang:is_list(L), erlang:is_integer(Base),
+ Base >= 2, Base =< 1+$Z-$A+10 ->
case list_to_integer_sign(L, Base) of
- I when is_integer(I) ->
+ I when erlang:is_integer(I) ->
I;
Fault ->
erlang:error(Fault, [L,Base])
@@ -587,7 +2715,7 @@ list_to_integer(L, Base) ->
list_to_integer_sign([$-|[_|_]=L], Base) ->
case list_to_integer(L, Base, 0) of
- I when is_integer(I) ->
+ I when erlang:is_integer(I) ->
-I;
I ->
I
@@ -600,13 +2728,13 @@ list_to_integer_sign(_, _) ->
badarg.
list_to_integer([D|L], Base, I)
- when is_integer(D), D >= $0, D =< $9, D < Base+$0 ->
+ when erlang:is_integer(D), D >= $0, D =< $9, D < Base+$0 ->
list_to_integer(L, Base, I*Base + D-$0);
list_to_integer([D|L], Base, I)
- when is_integer(D), D >= $A, D < Base+$A-10 ->
+ when erlang:is_integer(D), D >= $A, D < Base+$A-10 ->
list_to_integer(L, Base, I*Base + D-$A+10);
list_to_integer([D|L], Base, I)
- when is_integer(D), D >= $a, D < Base+$a-10 ->
+ when erlang:is_integer(D), D >= $a, D < Base+$a-10 ->
list_to_integer(L, Base, I*Base + D-$a+10);
list_to_integer([], _, I) ->
I;
@@ -618,7 +2746,8 @@ list_to_integer(_, _, _) ->
%% erlang:demonitor(Ref, [flush]) traps to
%% erlang:flush_monitor_message(Ref, Res) when
%% it needs to flush a monitor message.
-flush_monitor_message(Ref, Res) when is_reference(Ref), is_atom(Res) ->
+flush_monitor_message(Ref, Res) when erlang:is_reference(Ref),
+ erlang:is_atom(Res) ->
receive {_, Ref, _, _, _} -> ok after 0 -> ok end,
Res.
@@ -644,7 +2773,7 @@ set_cpu_topology(CpuTopology) ->
cput_e2i_clvl({logical, _}, _PLvl) ->
#cpu.logical;
cput_e2i_clvl([E | _], PLvl) ->
- case element(1, E) of
+ case erlang:element(1, E) of
node -> case PLvl of
0 -> #cpu.node;
#cpu.processor -> #cpu.processor_node
@@ -713,7 +2842,7 @@ cput_e2i({thread, TL}, Nid, PId, #cpu{thread = T0} = CPU, PLvl, #cpu.thread,
cput_e2i(TL, Nid, PId, CPU#cpu{thread = T0+1}, #cpu.thread, Lvl, Res);
cput_e2i({logical, ID}, _Nid, PId, #cpu{processor=P, core=C, thread=T} = CPU,
PLvl, #cpu.logical, Res)
- when PLvl < #cpu.logical, is_integer(ID), 0 =< ID, ID < 65536 ->
+ when PLvl < #cpu.logical, erlang:is_integer(ID), 0 =< ID, ID < 65536 ->
[CPU#cpu{processor = case P of -1 -> PId+1; _ -> P end,
core = case C of -1 -> 0; _ -> C end,
thread = case T of -1 -> 0; _ -> T end,
@@ -738,9 +2867,9 @@ cput_i2e([], _Frst, _Lvl, _TM) ->
cput_i2e([#cpu{logical = LID}| _], _Frst, Lvl, _TM) when Lvl == #cpu.logical ->
{logical, LID};
cput_i2e([#cpu{} = I | Is], Frst, Lvl, TM) ->
- cput_i2e(element(Lvl, I), Frst, Is, [I], Lvl, TM).
+ cput_i2e(erlang:element(Lvl, I), Frst, Is, [I], Lvl, TM).
-cput_i2e(V, Frst, [I | Is], SameV, Lvl, TM) when V =:= element(Lvl, I) ->
+cput_i2e(V, Frst, [I | Is], SameV, Lvl, TM) when V =:= erlang:element(Lvl, I) ->
cput_i2e(V, Frst, Is, [I | SameV], Lvl, TM);
cput_i2e(-1, true, [], SameV, Lvl, TM) ->
cput_i2e(rvrs(SameV), true, Lvl+1, TM);
@@ -754,10 +2883,10 @@ cput_i2e(_V, _Frst, Is, SameV, Lvl, TM) ->
[{cput_i2e_tag(Lvl, TM), cput_i2e(rvrs(SameV), true, Lvl+1, TM)}
| cput_i2e(Is, false, Lvl, TM)].
-cput_i2e_tag_map() -> list_to_tuple([cpu | record_info(fields, cpu)]).
+cput_i2e_tag_map() -> erlang:list_to_tuple([cpu | record_info(fields, cpu)]).
cput_i2e_tag(Lvl, TM) ->
- case element(Lvl, TM) of processor_node -> node; Other -> Other end.
+ case erlang:element(Lvl, TM) of processor_node -> node; Other -> Other end.
rvrs([_] = L) -> L;
rvrs(Xs) -> rvrs(Xs, []).
@@ -776,7 +2905,7 @@ rvrs([X|Xs],Ys) -> rvrs(Xs, [X|Ys]).
%% functions in bif.c. Do not make
%% any changes to it without reading
%% the comment about them in bif.c!
--spec await_proc_exit(dst(), 'apply' | 'data' | 'reason', term()) -> term().
+-spec erlang:await_proc_exit(dst(), 'apply' | 'data' | 'reason', term()) -> term().
await_proc_exit(Proc, Op, Data) ->
Mon = erlang:monitor(process, Proc),
receive
@@ -814,7 +2943,9 @@ max(A, _) -> A.
%% erts_memory() in $ERL_TOP/erts/emulator/beam/erl_alloc.c
%%
--type memory_type() :: 'total' | 'processes' | 'processes_used' | 'system' | 'atom' | 'atom_used' | 'binary' | 'code' | 'ets' | 'low' | 'maximum'.
+-type memory_type() :: 'total' | 'processes' | 'processes_used' | 'system'
+ | 'atom' | 'atom_used' | 'binary' | 'code' | 'ets'
+ | 'low' | 'maximum'.
-define(CARRIER_ALLOCS, [mseg_alloc, sbmbc_alloc, sbmbc_low_alloc]).
-define(LOW_ALLOCS, [sbmbc_low_alloc, ll_low_alloc, std_low_alloc]).
@@ -833,7 +2964,9 @@ max(A, _) -> A.
low = 0,
maximum = 0}).
--spec memory() -> [{memory_type(), non_neg_integer()}].
+-spec erlang:memory() -> [{Type, Size}] when
+ Type :: memory_type(),
+ Size :: non_neg_integer().
memory() ->
case aa_mem_data(au_mem_data(?ALL_NEEDED_ALLOCS)) of
notsup ->
@@ -858,8 +2991,9 @@ memory() ->
{ets, Mem#memory.ets} | Tail]
end.
--spec memory(memory_type()|[memory_type()]) -> non_neg_integer() | [{memory_type(), non_neg_integer()}].
-memory(Type) when is_atom(Type) ->
+-spec erlang:memory(Type :: memory_type()) -> non_neg_integer();
+ (TypeList :: [memory_type()]) -> [{memory_type(), non_neg_integer()}].
+memory(Type) when erlang:is_atom(Type) ->
{AA, ALCU, ChkSup, BadArgZero} = need_mem_info(Type),
case get_mem_data(ChkSup, ALCU, AA) of
notsup ->
@@ -871,7 +3005,7 @@ memory(Type) when is_atom(Type) ->
_ -> Value
end
end;
-memory(Types) when is_list(Types) ->
+memory(Types) when erlang:is_list(Types) ->
{AA, ALCU, ChkSup, BadArgZeroList} = need_mem_info_list(Types),
case get_mem_data(ChkSup, ALCU, AA) of
notsup ->
@@ -1079,7 +3213,7 @@ au_mem_data(EMD, []) ->
EMD.
au_mem_data(Allocs) ->
- Ref = make_ref(),
+ Ref = erlang:make_ref(),
erlang:system_info({allocator_sizes, Ref, Allocs}),
receive_emd(Ref).
@@ -1165,11 +3299,11 @@ alloc_info(Allocs) ->
alloc_sizes(Allocs) ->
get_alloc_info(allocator_sizes, Allocs).
-get_alloc_info(Type, AAtom) when is_atom(AAtom) ->
+get_alloc_info(Type, AAtom) when erlang:is_atom(AAtom) ->
[{AAtom, Result}] = get_alloc_info(Type, [AAtom]),
Result;
-get_alloc_info(Type, AList) when is_list(AList) ->
- Ref = make_ref(),
+get_alloc_info(Type, AList) when erlang:is_list(AList) ->
+ Ref = erlang:make_ref(),
erlang:system_info({Type, Ref, AList}),
receive_allocator(Ref,
erlang:system_info(schedulers),
@@ -1206,7 +3340,7 @@ receive_allocator(Ref, N, Acc) ->
receive_allocator(Ref, N-1, insert_info(InfoList, Acc))
end.
--spec await_sched_wall_time_modifications(Ref, Result) -> boolean() when
+-spec erlang:await_sched_wall_time_modifications(Ref, Result) -> boolean() when
Ref :: reference(),
Result :: boolean().
@@ -1214,12 +3348,12 @@ await_sched_wall_time_modifications(Ref, Result) ->
sched_wall_time(Ref, erlang:system_info(schedulers)),
Result.
--spec gather_sched_wall_time_result(Ref) -> [{pos_integer(),
- non_neg_integer(),
- non_neg_integer()}] when
+-spec erlang:gather_sched_wall_time_result(Ref) -> [{pos_integer(),
+ non_neg_integer(),
+ non_neg_integer()}] when
Ref :: reference().
-gather_sched_wall_time_result(Ref) when is_reference(Ref) ->
+gather_sched_wall_time_result(Ref) when erlang:is_reference(Ref) ->
sched_wall_time(Ref, erlang:system_info(schedulers), []).
sched_wall_time(_Ref, 0) ->
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index c9c434dea0..1d1087c7f2 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -635,8 +635,16 @@ do_unload([M|Mods]) ->
catch erlang:purge_module(M),
do_unload(Mods);
do_unload([]) ->
+ purge_all_hipe_refs(),
ok.
+purge_all_hipe_refs() ->
+ case erlang:system_info(hipe_architecture) of
+ undefined -> ok;
+ _ -> hipe_bifs:remove_refs_from(all)
+ end.
+
+
sub([H|T],L) -> sub(T,del(H,L));
sub([],L) -> L.
@@ -1252,7 +1260,11 @@ get_arguments([]) ->
[].
to_strings([H|T]) when is_atom(H) -> [atom_to_list(H)|to_strings(T)];
-to_strings([H|T]) when is_binary(H) -> [binary_to_list(H)|to_strings(T)];
+to_strings([H|T]) when is_binary(H) -> [try
+ unicode:characters_to_list(H,file:native_name_encoding())
+ catch
+ _:_ -> binary_to_list(H)
+ end|to_strings(T)];
to_strings([]) -> [].
get_argument(Arg,Flags) ->
diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl
index 0fc56c8c8e..eafab1bae4 100644
--- a/erts/preloaded/src/prim_file.erl
+++ b/erts/preloaded/src/prim_file.erl
@@ -147,6 +147,32 @@
-define(POSIX_FADV_NOREUSE, 5).
+%%% BIFs
+
+-export([internal_name2native/1,
+ internal_native2name/1,
+ internal_normalize_utf8/1]).
+
+-type unicode_string() :: [unicode:unicode_char()].
+-type prim_file_name() :: unicode_string() | unicode:unicode_binary().
+
+-spec internal_name2native(prim_file_name()) -> binary().
+
+internal_name2native(_) ->
+ erlang:nif_error(undefined).
+
+-spec internal_native2name(binary()) -> prim_file_name().
+
+internal_native2name(_) ->
+ erlang:nif_error(undefined).
+
+-spec internal_normalize_utf8(unicode:unicode_binary()) -> unicode_string().
+
+internal_normalize_utf8(_) ->
+ erlang:nif_error(undefined).
+
+%%% End of BIFs
+
%%%-----------------------------------------------------------------
%%% Functions operating on a file through a handle. ?FD_DRV.
%%%
@@ -453,7 +479,7 @@ position(#file_descriptor{module = ?MODULE, data = {Port, _}}, At) ->
{error, Reason}
end.
-%% Returns {error, Reaseon} | ok.
+%% Returns {error, Reason} | ok.
truncate(#file_descriptor{module = ?MODULE, data = {Port, _}}) ->
drv_command(Port, <<?FILE_TRUNCATE>>).
@@ -648,25 +674,7 @@ set_cwd(Dir) ->
set_cwd(Port, Dir) when is_port(Port) ->
set_cwd_int(Port, Dir).
-set_cwd_int(Port, Dir0) ->
- Dir =
- (catch
- case os:type() of
- vxworks ->
- %% chdir on vxworks doesn't support
- %% relative paths
- %% must call get_cwd from here and use
- %% absname/2, since
- %% absname/1 uses file:get_cwd ...
- case get_cwd_int(Port, 0) of
- {ok, AbsPath} ->
- filename:absname(Dir0, AbsPath);
- _Badcwd ->
- Dir0
- end;
- _Else ->
- Dir0
- end),
+set_cwd_int(Port, Dir) ->
%% Dir is now either a string or an EXIT tuple.
%% An EXIT tuple will fail in the following catch.
drv_command(Port, [?FILE_CHDIR, pathname(Dir)]).
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index 846ae97ed2..b639b1efdd 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -184,7 +184,7 @@ close_pend_loop(S, N) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bind(S,IP,Port) when is_port(S), is_integer(Port), Port >= 0, Port =< 65535 ->
- case ctl_cmd(S,?INET_REQ_BIND,[?int16(Port),ip_to_bytes(IP)]) of
+ 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;
@@ -206,10 +206,10 @@ bindx(S, AddFlag, Addrs) ->
case getprotocol(S) of
sctp ->
%% Really multi-homed "bindx". Stringified args:
- %% [AddFlag, (Port, IP)+]:
+ %% [AddFlag, (AddrBytes see enc_value_2(addr,X))+]:
Args =
[?int8(AddFlag)|
- [[?int16(Port)|ip_to_bytes(IP)] ||
+ [enc_value(set, addr, {IP,Port}) ||
{IP, Port} <- Addrs]],
case ctl_cmd(S, ?SCTP_REQ_BINDX, Args) of
{ok,_} -> {ok, S};
@@ -1071,7 +1071,6 @@ enc_opt(deliver) -> ?INET_LOPT_DELIVER;
enc_opt(exit_on_close) -> ?INET_LOPT_EXITONCLOSE;
enc_opt(high_watermark) -> ?INET_LOPT_TCP_HIWTRMRK;
enc_opt(low_watermark) -> ?INET_LOPT_TCP_LOWTRMRK;
-enc_opt(bit8) -> ?INET_LOPT_BIT8;
enc_opt(send_timeout) -> ?INET_LOPT_TCP_SEND_TIMEOUT;
enc_opt(send_timeout_close) -> ?INET_LOPT_TCP_SEND_TIMEOUT_CLOSE;
enc_opt(delay_send) -> ?INET_LOPT_TCP_DELAY_SEND;
@@ -1125,7 +1124,6 @@ dec_opt(?INET_LOPT_DELIVER) -> deliver;
dec_opt(?INET_LOPT_EXITONCLOSE) -> exit_on_close;
dec_opt(?INET_LOPT_TCP_HIWTRMRK) -> high_watermark;
dec_opt(?INET_LOPT_TCP_LOWTRMRK) -> low_watermark;
-dec_opt(?INET_LOPT_BIT8) -> bit8;
dec_opt(?INET_LOPT_TCP_SEND_TIMEOUT) -> send_timeout;
dec_opt(?INET_LOPT_TCP_SEND_TIMEOUT_CLOSE) -> send_timeout_close;
dec_opt(?INET_LOPT_TCP_DELAY_SEND) -> delay_send;
@@ -1220,11 +1218,6 @@ type_opt_1(deliver) ->
type_opt_1(exit_on_close) -> bool;
type_opt_1(low_watermark) -> int;
type_opt_1(high_watermark) -> int;
-type_opt_1(bit8) ->
- {enum,[{clear, ?INET_BIT8_CLEAR},
- {set, ?INET_BIT8_SET},
- {on, ?INET_BIT8_ON},
- {off, ?INET_BIT8_OFF}]};
type_opt_1(send_timeout) -> time;
type_opt_1(send_timeout_close) -> bool;
type_opt_1(delay_send) -> bool;
diff --git a/erts/start_scripts/Makefile b/erts/start_scripts/Makefile
index 55584bb057..608679b016 100644
--- a/erts/start_scripts/Makefile
+++ b/erts/start_scripts/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/test/Makefile b/erts/test/Makefile
index 79be5497b0..6b409e2f1b 100644
--- a/erts/test/Makefile
+++ b/erts/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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
@@ -28,7 +28,6 @@ EBIN = .
# ----------------------------------------------------
MODULES= \
- autoimport_SUITE \
erlc_SUITE \
install_SUITE \
nt_SUITE \
@@ -39,14 +38,11 @@ MODULES= \
erlexec_SUITE \
z_SUITE
-ERL_XML = $(ERL_TOP)/erts/doc/src/erlang.xml
-ERL_XML_TARGET = autoimport_SUITE_data/erlang.xml
-
ERL_FILES= $(MODULES:%=%.erl)
TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
-EXTRA_FILES = install_SUITE_data/install_bin $(ERL_XML_TARGET)
+EXTRA_FILES = install_SUITE_data/install_bin
# ----------------------------------------------------
# Release directory specification
@@ -68,10 +64,6 @@ install_SUITE_data/install_bin: ../../make/install_bin
rm -f $@
cp -p $< $@
-$(ERL_XML_TARGET): $(ERL_XML)
- rm -f $@
- cp -p $< $@
-
clean:
rm -f $(TARGET_FILES) $(EXTRA_FILES)
rm -f core *~
@@ -87,7 +79,7 @@ release_spec:
release_tests_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)"
- $(INSTALL_DATA) system.spec system.dynspec system.spec.vxworks \
+ $(INSTALL_DATA) system.spec system.dynspec \
$(ERL_FILES) $(TARGET_FILES) "$(RELSYSDIR)"
chmod -R u+w "$(RELSYSDIR)"
tar cf - *_SUITE_data utils | (cd "$(RELSYSDIR)"; tar xf -)
diff --git a/erts/test/autoimport_SUITE.erl b/erts/test/autoimport_SUITE.erl
deleted file mode 100644
index 71ed5204b1..0000000000
--- a/erts/test/autoimport_SUITE.erl
+++ /dev/null
@@ -1,196 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1998-2011. 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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%% Purpose: Test erlang.xml re autoimports
--module(autoimport_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,
- init_per_testcase/2,end_per_testcase/2,
- autoimports/1]).
--define(TEST_TIMEOUT, ?t:seconds(180)).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
-
-all() ->
- [autoimports].
-
-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].
-
-end_per_testcase(_Func, Config) ->
- Dog = ?config(watchdog, Config),
- catch test_server:timetrap_cancel(Dog),
- ok.
-
-
-autoimports(suite) ->
- [];
-autoimports(doc) ->
- ["Check that erlang.xml documents autoimports correctly"];
-autoimports(Config) when is_list(Config) ->
- ?line XML = filename:join([?config(data_dir,Config),"erlang.xml"]),
- ?line case xml(XML) of
- [] ->
- ok;
- List ->
- lists:foreach(fun({[],F,A}) ->
- io:format("erlang:~s/~p is wrongly "
- "documented as ~s/~p~n",
- [F,A,F,A]);
- ({"erlang",F,A}) ->
- io:format("~s/~p is wrongly "
- "documented as "
- "erlang:~s/~p~n",
- [F,A,F,A])
- end,List),
- ?t:fail({wrong_autoimports,List})
- end.
-
-%%
-%% Ugly chunk of code to heuristically analyze the erlang.xml
-%% documentation file. Don't view this as an example...
-%%
-
-xml(XMLFile) ->
- {ok,File} = file:open(XMLFile,[read]),
- xskip_to_funcs(file:read_line(File),File),
- DocData = xloop(file:read_line(File),File),
- true = DocData =/= [],
- file:close(File),
- analyze(DocData).
-
-%% Skip lines up to and including the <funcs> tag.
-xskip_to_funcs({ok,Line},File) ->
- case re:run(Line,"\\<funcs\\>",[{capture,none}]) of
- nomatch ->
- xskip_to_funcs(file:read_line(File),File);
- match ->
- ok
- end.
-
-xloop({ok,Line},File) ->
- case re:run(Line,"\\<name\\>",[{capture,none}]) of
- nomatch ->
- xloop(file:read_line(File),File);
- match ->
- X = re:replace(Line,"\\).*",")",[{return,list}]),
- Y = re:replace(X,".*\\>","",[{return,list}]),
- Mod = get_module(Y),
- Rest1 = fstrip(Mod++":",Y),
- Func = get_function(Rest1),
- Rest2 = fstrip(Func++"(", Rest1),
- Argc = count_args(Rest2,1,0,false),
- [{Mod,Func,Argc} |
- xloop(file:read_line(File),File)]
- end;
-xloop(_,_) ->
- [].
-
-analyze([{[],Func,Arity}|T]) ->
- case erl_internal:bif(list_to_atom(Func),Arity) of
- true ->
- analyze(T);
- false ->
- [{[],Func,Arity} |
- analyze(T)]
- end;
-analyze([{"erlang",Func,Arity}|T]) ->
- case erl_internal:bif(list_to_atom(Func),Arity) of
- true ->
- [{"erlang",Func,Arity}|analyze(T)];
- false ->
- analyze(T)
- end;
-analyze([_|T]) ->
- analyze(T);
-analyze([]) ->
- [].
-
-
-count_args([],_,N,false) ->
- N;
-count_args([],_,N,true) ->
- N+1;
-count_args(_,0,N,true) ->
- N+1;
-count_args(_,0,N,false) ->
- N;
-count_args([Par|T],Level,N,Got) when (Par =:= 40) or
- (Par =:= 123) or (Par =:= 91) ->
- count_args(T,Level+1,N,(Level =:= 1) or Got);
-count_args([41|T],1,N,true) ->
- count_args(T,0,N+1,false);
-count_args([Par|T],Level,N, Got) when (Par =:= 41) or
- (Par =:= 125) or (Par =:= 93) ->
- count_args(T,Level-1,N,Got);
-count_args([$,|T],1,N,true) ->
- count_args(T,1,N+1,false);
-count_args([$ |T],A,B,C) ->
- count_args(T,A,B,C);
-count_args([_|T],1,N,_) ->
- count_args(T,1,N,true);
-count_args([_|T],A,B,C) ->
- count_args(T,A,B,C).
-
-fstrip([],X) ->
- X;
-fstrip(_,[]) ->
- [];
-fstrip([H|T1],[H|T2]) ->
- fstrip(T1,T2);
-fstrip(_,L) ->
- L.
-
-get_module(X) ->
- get_module(X,[]).
-get_module([],_) ->
- [];
-get_module([$:|_],Acc) ->
- lists:reverse(Acc);
-get_module([40|_],_) -> %(
- [];
-get_module([H|T],Acc) ->
- get_module(T,[H|Acc]).
-
-get_function(X) ->
- get_function(X,[]).
-get_function([],_) ->
- [];
-get_function([40|_],Acc) -> %(
- lists:reverse(Acc);
-get_function([H|T],Acc) ->
- get_function(T,[H|Acc]).
diff --git a/erts/test/erl_print_SUITE_data/Makefile.src b/erts/test/erl_print_SUITE_data/Makefile.src
index 96d71c7a98..3d58669c18 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-2011. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. 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/test/erl_print_SUITE_data/erl_print_tests.c b/erts/test/erl_print_SUITE_data/erl_print_tests.c
index 82b0c21132..acb213cd3a 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-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2012. 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/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl
index ada09db975..ab774dbc4f 100644
--- a/erts/test/erlc_SUITE.erl
+++ b/erts/test/erlc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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/test/erlexec_SUITE_data/Makefile.src b/erts/test/erlexec_SUITE_data/Makefile.src
index 970a905c32..2a8decaa4b 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-2009. All Rights Reserved.
+# Copyright Ericsson AB 2008-2012. 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/test/ethread_SUITE_data/Makefile.src b/erts/test/ethread_SUITE_data/Makefile.src
index f93a31178b..bad133c467 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-2009. All Rights Reserved.
+# Copyright Ericsson AB 2004-2012. 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/test/install_SUITE.erl b/erts/test/install_SUITE.erl
index 559d95007c..f1d8dc2587 100644
--- a/erts/test/install_SUITE.erl
+++ b/erts/test/install_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl
index 332733e075..b7ceb0a3fd 100644
--- a/erts/test/otp_SUITE.erl
+++ b/erts/test/otp_SUITE.erl
@@ -84,13 +84,14 @@ undefined_functions(Config) when is_list(Config) ->
"ExcludedFrom = ~p:_/_,"
"Undef - Undef | ExcludedFrom",
[UndefS,ExcludeFrom]),
- ?line {ok,Undef0} = xref:q(Server, lists:flatten(Q)),
- ?line Undef1 = hipe_filter(Undef0),
- ?line Undef2 = ssl_crypto_filter(Undef1),
- ?line Undef3 = edoc_filter(Undef2),
+ {ok,Undef0} = xref:q(Server, lists:flatten(Q)),
+ Undef1 = hipe_filter(Undef0),
+ Undef2 = ssl_crypto_filter(Undef1),
+ Undef3 = edoc_filter(Undef2),
Undef4 = eunit_filter(Undef3),
Undef5 = dialyzer_filter(Undef4),
- Undef = wx_filter(Undef5),
+ Undef6 = wx_filter(Undef5),
+ Undef = gs_filter(Undef6),
case Undef of
[] -> ok;
@@ -181,6 +182,7 @@ dialyzer_filter(Undef) ->
({_,{dialyzer_codeserver,_,_}}) -> false;
({_,{dialyzer_contracts,_,_}}) -> false;
({_,{dialyzer_cl_parse,_,_}}) -> false;
+ ({_,{dialyzer_timing,_,_}}) -> false;
({_,{dialyzer_plt,_,_}}) -> false;
({_,{dialyzer_succ_typings,_,_}}) -> false;
({_,{dialyzer_utils,_,_}}) -> false;
@@ -201,6 +203,16 @@ wx_filter(Undef) ->
_ -> Undef
end.
+gs_filter(Undef) ->
+ case code:lib_dir(gs) of
+ {error,bad_name} ->
+ filter(fun({_,{gs,_,_}}) -> false;
+ ({_,{gse,_,_}}) -> false;
+ ({_,{tool_utils,_,_}}) -> false;
+ (_) -> true
+ end, Undef);
+ _ -> Undef
+ end.
deprecated_not_in_obsolete(Config) when is_list(Config) ->
?line Server = ?config(xref_server, Config),
diff --git a/erts/test/run_erl_SUITE.erl b/erts/test/run_erl_SUITE.erl
index 6838c8924d..de05e6f206 100644
--- a/erts/test/run_erl_SUITE.erl
+++ b/erts/test/run_erl_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. 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/test/system.spec.vxworks b/erts/test/system.spec.vxworks
deleted file mode 100644
index 378adf56ac..0000000000
--- a/erts/test/system.spec.vxworks
+++ /dev/null
@@ -1,2 +0,0 @@
-{topcase, {dir, "../system_test"}}.
-{skip,{erlc_SUITE, "Not on VxWorks, erlc is a HOST tool."}}
diff --git a/erts/test/utils/gccifier.c b/erts/test/utils/gccifier.c
index a1019f9a72..7e4ffc7281 100644
--- a/erts/test/utils/gccifier.c
+++ b/erts/test/utils/gccifier.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2012. 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/test/utils/gccifier.sh b/erts/test/utils/gccifier.sh
index 4c6ba35db2..978aecf424 100755
--- a/erts/test/utils/gccifier.sh
+++ b/erts/test/utils/gccifier.sh
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2009. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. 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/vsn.mk b/erts/vsn.mk
index da0f03b24f..a420781e9f 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -17,8 +17,8 @@
# %CopyrightEnd%
#
-VSN = 5.9.2
-SYSTEM_VSN = R15B02
+VSN = 5.10
+SYSTEM_VSN = R16B
# Port number 4365 in 4.2
# Port number 4366 in 4.3
diff --git a/lib/.gitignore b/lib/.gitignore
index 56b1ed2b84..4125111ebd 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -7,6 +7,7 @@
/common_test/doc/src/ct_rpc.xml
/common_test/doc/src/ct_snmp.xml
/common_test/doc/src/ct_ssh.xml
+/common_test/doc/src/ct_netconfc.xml
/common_test/doc/src/ct_telnet.xml
/common_test/doc/src/unix_telnet.xml
diff --git a/lib/Makefile b/lib/Makefile
index 3753bd165b..64b17a3cca 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -30,7 +30,7 @@ ifdef BUILD_ALL
diameter \
cosTransactions cosEvent cosTime cosNotification \
cosProperty cosFileTransfer cosEventDomain et megaco webtool \
- xmerl edoc eunit ssh inviso typer erl_docgen \
+ xmerl edoc eunit ssh typer erl_docgen \
percept eldap dialyzer hipe
EXTRA_FILE := $(wildcard EXTRA-APPLICATIONS)
EXTRA_APPLICATIONS := $(if $(EXTRA_FILE),$(shell cat $(EXTRA_FILE)))
diff --git a/lib/appmon/doc/src/Makefile b/lib/appmon/doc/src/Makefile
index 2bf560ee6b..7c93abe6d8 100644
--- a/lib/appmon/doc/src/Makefile
+++ b/lib/appmon/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/appmon/priv/Makefile b/lib/appmon/priv/Makefile
index 8920923c98..356fe9b842 100644
--- a/lib/appmon/priv/Makefile
+++ b/lib/appmon/priv/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/appmon/src/Makefile b/lib/appmon/src/Makefile
index 9ee7c92a4d..06e61b7cc8 100644
--- a/lib/appmon/src/Makefile
+++ b/lib/appmon/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/asn1/c_src/Makefile b/lib/asn1/c_src/Makefile
index 903cf32838..dc926947af 100644
--- a/lib/asn1/c_src/Makefile
+++ b/lib/asn1/c_src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2012. 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
@@ -66,13 +66,8 @@ NIF_SHARED_OBJ_FILE = $(LIBDIR)/asn1_erl_nif.dll
CLIB_FLAGS =
LN=cp
else
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-NIF_SHARED_OBJ_FILE = $(LIBDIR)/asn1_erl_nif.eld
-CLIB_FLAGS =
-else
NIF_SHARED_OBJ_FILE = $(LIBDIR)/asn1_erl_nif.so
CLIB_FLAGS = -lc
-endif
LN= ln -s
endif
diff --git a/lib/asn1/doc/src/Makefile b/lib/asn1/doc/src/Makefile
index 9ef5750353..3b3e1bd8f9 100644
--- a/lib/asn1/doc/src/Makefile
+++ b/lib/asn1/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml
index b5422c9083..5ca86130a1 100644
--- a/lib/asn1/doc/src/notes.xml
+++ b/lib/asn1/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,75 @@
<p>This document describes the changes made to the asn1 application.</p>
+<section><title>Asn1 1.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Encoding and decoding of integer ranges can now be done
+ with an upper bound larger than the previous limit of
+ 16^10. The new upper bound in per encoding and decodings
+ for constrained whole numbers is 2^2040 (close to 16^508)</p>
+ <p>
+ Own Id: OTP-10128</p>
+ </item>
+ <item>
+ <p>
+ Per encoding/decoding now works correctly for single
+ value subtyping of an integer type where a subtype is a
+ predefined value. Previously a predefined value could
+ cause a non-valid range-check in the generated Erlang
+ code for per encoding/decoding due to a bug in the
+ constraint checking.</p>
+ <p>
+ Own Id: OTP-10139</p>
+ </item>
+ <item>
+ <p>
+ Fix typo error in selected decode function (Thanks to
+ Artem Teslenko)</p>
+ <p>
+ Own Id: OTP-10152</p>
+ </item>
+ <item>
+ <p>
+ Better error indication when detecting unexpected tags
+ during decoding of BER encoded data.</p>
+ <p>
+ Own Id: OTP-10186</p>
+ </item>
+ <item>
+ <p>
+ asn1rt_check: Fix transform_to_EXTERNAL1990 for binary
+ input (Thanks to Harald Welte)</p>
+ <p>
+ Own Id: OTP-10233</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add support for multiple ExtensionAdditionGroups</p>
+ <p>
+ Own Id: OTP-10058</p>
+ </item>
+ <item>
+ <p>
+ Add support for extensible enumeration types in n2n
+ generated functions.</p>
+ <p>
+ Own Id: OTP-10144</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Asn1 1.7</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl
index 99755a95a6..8e971a1c76 100644
--- a/lib/asn1/src/asn1ct.erl
+++ b/lib/asn1/src/asn1ct.erl
@@ -833,7 +833,7 @@ generate({true,{M,_Module,GenTOrV}},OutFile,EncodingRule,Options) ->
asn1ct_table:new(check_functions),
%% create decoding function names and taglists for partial decode
- case specialized_decode_prepare(EncodingRule,M,GenTOrV,Options) of
+ case (catch specialized_decode_prepare(EncodingRule,M,GenTOrV,Options)) of
{error, Reason} -> warning("Error in configuration file: ~n~p~n",
[Reason], Options,
"Error in configuration file");
diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl
index 494a2eddd9..59e82b7a57 100644
--- a/lib/asn1/src/asn1ct_check.erl
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -4177,7 +4177,7 @@ check_constraint(S,{'SizeConstraint',Lb}) ->
check_constraint(S,{'SingleValue', L}) when is_list(L) ->
F = fun(A) -> resolv_value(S,A) end,
- {'SingleValue',lists:map(F,L)};
+ {'SingleValue',lists:sort(lists:map(F,L))};
check_constraint(S,{'SingleValue', V}) when is_integer(V) ->
Val = resolv_value(S,V),
diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl
index 5f5138ef23..8de41a4dd4 100644
--- a/lib/asn1/src/asn1ct_constructed_per.erl
+++ b/lib/asn1/src/asn1ct_constructed_per.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -119,9 +119,7 @@ gen_encode_constructed(Erule,Typename,D) when is_record(D,type) ->
"end,",nl]),
asn1ct_name:new(val)
end,
- lists:foreach(ExtGroupFun,ExtGroupPosLenList);
- _ -> % no extensionAdditionGroup
- ok
+ lists:foreach(ExtGroupFun,ExtGroupPosLenList)
end,
asn1ct_name:new(tmpval),
emit(["Extensions = ?RT_PER:fixextensions(",{asis,Ext},",",
diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl
index fda4e1c6d9..64a3555f62 100644
--- a/lib/asn1/src/asn1ct_gen.erl
+++ b/lib/asn1/src/asn1ct_gen.erl
@@ -129,28 +129,39 @@ pgen_types(Rtmod,Erules,N2nConvEnums,Module,[H|T]) ->
end,
pgen_types(Rtmod,Erules,N2nConvEnums,Module,T).
+%% Enumerated type with extension marker
pgen_n2nconversion(_Erules,#typedef{name=TypeName,typespec=#type{def={'ENUMERATED',{NN1,NN2}}}}) ->
NN = NN1 ++ NN2,
- pgen_name2numfunc(TypeName,NN),
- pgen_num2namefunc(TypeName,NN);
+ pgen_name2numfunc(TypeName,NN, extension_marker),
+ pgen_num2namefunc(TypeName,NN, extension_marker);
+%% Without extension marker
+pgen_n2nconversion(_Erules,#typedef{name=TypeName,typespec=#type{def={'ENUMERATED',NN}}}) ->
+ pgen_name2numfunc(TypeName,NN, no_extension_marker),
+ pgen_num2namefunc(TypeName,NN, no_extension_marker);
pgen_n2nconversion(_Erules,_) ->
true.
-pgen_name2numfunc(_TypeName,[]) ->
+pgen_name2numfunc(_TypeName,[], _) ->
true;
-pgen_name2numfunc(TypeName,[{Atom,Number}]) ->
+pgen_name2numfunc(TypeName,[{Atom,Number}], extension_marker) ->
+ emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,";",nl]),
+ emit(["name2num_",TypeName,"({asn1_enum, Num}) -> Num.",nl,nl]);
+pgen_name2numfunc(TypeName,[{Atom,Number}], _) ->
emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,".",nl,nl]);
-pgen_name2numfunc(TypeName,[{Atom,Number}|NNRest]) ->
+pgen_name2numfunc(TypeName,[{Atom,Number}|NNRest], EM) ->
emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,";",nl]),
- pgen_name2numfunc(TypeName,NNRest).
+ pgen_name2numfunc(TypeName,NNRest, EM).
-pgen_num2namefunc(_TypeName,[]) ->
+pgen_num2namefunc(_TypeName,[], _) ->
true;
-pgen_num2namefunc(TypeName,[{Atom,Number}]) ->
+pgen_num2namefunc(TypeName,[{Atom,Number}], extension_marker) ->
+ emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},";",nl]),
+ emit(["num2name_",TypeName,"(ExtensionNum) -> {asn1_enum, ExtensionNum}.",nl,nl]);
+pgen_num2namefunc(TypeName,[{Atom,Number}], _) ->
emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},".",nl,nl]);
-pgen_num2namefunc(TypeName,[{Atom,Number}|NNRest]) ->
+pgen_num2namefunc(TypeName,[{Atom,Number}|NNRest], EM) ->
emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},";",nl]),
- pgen_num2namefunc(TypeName,NNRest).
+ pgen_num2namefunc(TypeName,NNRest, EM).
pgen_objects(_,_,_,[]) ->
true;
diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
index 597fb0030b..3ccfca3784 100644
--- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
@@ -419,7 +419,7 @@ gen_decode_selected(Erules,Type,FuncName) ->
" {Tlv,_} = ?RT_BER:decode(Bin2",asn1ct_gen:nif_parameter(),"),",nl]),
emit("{ok,"),
gen_decode_selected_type(Erules,Type),
- emit(["};",nl," Err -> exit({error,{selctive_decode,Err}})",nl,
+ emit(["};",nl," Err -> exit({error,{selective_decode,Err}})",nl,
" end.",nl]).
gen_decode_selected_type(_Erules,TypeDef) ->
diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl
index 5f42eacbdc..bd5b81991d 100644
--- a/lib/asn1/src/asn1ct_gen_per.erl
+++ b/lib/asn1/src/asn1ct_gen_per.erl
@@ -321,19 +321,13 @@ effective_constr(_,[]) ->
[];
effective_constr('SingleValue',List) ->
SVList = lists:flatten(lists:map(fun(X)->element(2,X)end,List)),
- % sort and remove duplicates
- SortedSVList = lists:sort(SVList),
- RemoveDup = fun([],_) ->[];
- ([H],_) -> [H];
- ([H,H|T],F) -> F([H|T],F);
- ([H|T],F) -> [H|F(T,F)]
- end,
-
- case RemoveDup(SortedSVList,RemoveDup) of
+ %% Sort and remove duplicates before generating SingleValue or ValueRange
+ %% In case of ValueRange, also check for 'MIN and 'MAX'
+ case lists:usort(SVList) of
[N] ->
[{'SingleValue',N}];
- L when is_list(L) ->
- [{'ValueRange',{hd(L),lists:last(L)}}]
+ L when is_list(L) ->
+ [{'ValueRange',{least_Lb(L),greatest_Ub(L)}}]
end;
effective_constr('ValueRange',List) ->
LBs = lists:map(fun({_,{Lb,_}})-> Lb end,List),
diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl
index eda0faad3c..16eec92847 100644
--- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl
+++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl
@@ -670,18 +670,13 @@ effective_constr(_,[]) ->
[];
effective_constr('SingleValue',List) ->
SVList = lists:flatten(lists:map(fun(X)->element(2,X)end,List)),
- % sort and remove duplicates
- RemoveDup = fun([],_) ->[];
- ([H],_) -> [H];
- ([H,H|T],F) -> F([H|T],F);
- ([H|T],F) -> [H|F(T,F)]
- end,
-
- case RemoveDup(SVList,RemoveDup) of
+ %% Sort and remove duplicates before generating SingleValue or ValueRange
+ %% In case of ValueRange, also check for 'MIN and 'MAX'
+ case lists:usort(SVList) of
[N] ->
[{'SingleValue',N}];
- L when is_list(L) ->
- [{'ValueRange',{hd(L),lists:last(L)}}]
+ L when is_list(L) ->
+ [{'ValueRange',{least_Lb(L),greatest_Ub(L)}}]
end;
effective_constr('ValueRange',List) ->
LBs = lists:map(fun({_,{Lb,_}})-> Lb end,List),
diff --git a/lib/asn1/src/asn1rt_ber_bin_v2.erl b/lib/asn1/src/asn1rt_ber_bin_v2.erl
index 17e66f77c9..9ff5017c68 100644
--- a/lib/asn1/src/asn1rt_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1rt_ber_bin_v2.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2012. 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
@@ -610,8 +610,8 @@ match_tags(Vlist = [{T,_V}|_], [T]) ->
Vlist;
match_tags(Tlv, []) ->
Tlv;
-match_tags({Tag,_V},[T|_Tt]) ->
- {error,{asn1,{wrong_tag,{Tag,T}}}}.
+match_tags(Tlv = {Tag,_V},[T|_Tt]) ->
+ exit({error,{asn1,{wrong_tag,{{expected,T},{got,Tag,Tlv}}}}}).
cindex(Ix,Val,Cname) ->
diff --git a/lib/asn1/src/asn1rt_check.erl b/lib/asn1/src/asn1rt_check.erl
index d9856901b8..35b993fc71 100644
--- a/lib/asn1/src/asn1rt_check.erl
+++ b/lib/asn1/src/asn1rt_check.erl
@@ -311,7 +311,8 @@ transform_to_EXTERNAL1990([Data_val_desc,Data_value],Acc)
when is_binary(Data_value)->
list_to_tuple(lists:reverse([{'single-ASN1-type',Data_value},
Data_val_desc|Acc]));
-transform_to_EXTERNAL1990([Data_value],Acc) when is_list(Data_value)->
+transform_to_EXTERNAL1990([Data_value],Acc)
+ when is_list(Data_value); is_binary(Data_value) ->
list_to_tuple(lists:reverse([{'octet-aligned',Data_value}|Acc])).
diff --git a/lib/asn1/src/asn1rt_per_bin.erl b/lib/asn1/src/asn1rt_per_bin.erl
index a124c7553d..5772f09bf4 100644
--- a/lib/asn1/src/asn1rt_per_bin.erl
+++ b/lib/asn1/src/asn1rt_per_bin.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2012. 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
@@ -18,7 +18,6 @@
%%
%%
-module(asn1rt_per_bin).
-
%% encoding / decoding of PER aligned
-include("asn1_records.hrl").
@@ -57,7 +56,7 @@
encode_NumericString/2, decode_NumericString/2,
encode_ObjectDescriptor/2, decode_ObjectDescriptor/1
]).
--export([complete_bytes/1, getbits/2, getoctets/2]).
+-export([complete_bytes/1, getbits/2, getoctets/2, minimum_bits/1]).
-define('16K',16384).
-define('32K',32768).
@@ -695,21 +694,28 @@ encode_constrained_number({Lb,Ub},Val) when Val >= Lb, Ub >= Val ->
{octets,[Val2]};
Range =< 65536 ->
{octets,<<Val2:16>>};
- Range =< 16#1000000 ->
- Octs = eint_positive(Val2),
- [{bits,2,length(Octs)-1},{octets,Octs}];
- Range =< 16#100000000 ->
- Octs = eint_positive(Val2),
- [{bits,2,length(Octs)-1},{octets,Octs}];
- Range =< 16#10000000000 ->
- Octs = eint_positive(Val2),
- [{bits,3,length(Octs)-1},{octets,Octs}];
+ Range =< (1 bsl (255*8)) ->
+ Octs = binary:encode_unsigned(Val2),
+ RangeOcts = binary:encode_unsigned(Range - 1),
+ OctsLen = erlang:byte_size(Octs),
+ RangeOctsLen = erlang:byte_size(RangeOcts),
+ LengthBitsNeeded = minimum_bits(RangeOctsLen - 1),
+ [{bits, LengthBitsNeeded, OctsLen - 1}, {octets, Octs}];
true ->
exit({not_supported,{integer_range,Range}})
end;
encode_constrained_number(Range,Val) ->
exit({error,{asn1,{integer_range,Range,value,Val}}}).
+%% For some reason the minimum bits needed in the length field in encoding of
+%% constrained whole numbers must always be atleast 2?
+minimum_bits(N) when N < 4 -> 2;
+minimum_bits(N) when N < 8 -> 3;
+minimum_bits(N) when N < 16 -> 4;
+minimum_bits(N) when N < 32 -> 5;
+minimum_bits(N) when N < 64 -> 6;
+minimum_bits(N) when N < 128 -> 7;
+minimum_bits(_N) -> 8.
decode_constrained_number(Buffer,{Lb,Ub}) ->
Range = Ub - Lb + 1,
@@ -738,18 +744,12 @@ decode_constrained_number(Buffer,{Lb,Ub}) ->
getoctets(Buffer,1);
Range =< 65536 ->
getoctets(Buffer,2);
- Range =< 16#1000000 ->
- {Len,Bytes2} = decode_length(Buffer,{1,3}),
- {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
- {dec_pos_integer(Octs),Bytes3};
- Range =< 16#100000000 ->
- {Len,Bytes2} = decode_length(Buffer,{1,4}),
- {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
- {dec_pos_integer(Octs),Bytes3};
- Range =< 16#10000000000 ->
- {Len,Bytes2} = decode_length(Buffer,{1,5}),
- {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
- {dec_pos_integer(Octs),Bytes3};
+ Range =< (1 bsl (255*8)) ->
+ OList = binary:bin_to_list(binary:encode_unsigned(Range - 1)),
+ RangeOctLen = length(OList),
+ {Len, Bytes} = decode_length(Buffer, {1, RangeOctLen}),
+ {Octs, RestBytes} = getoctets_as_list(Bytes, Len),
+ {binary:decode_unsigned(binary:list_to_bin(Octs)), RestBytes};
true ->
exit({not_supported,{integer_range,Range}})
end,
@@ -803,8 +803,6 @@ decode_unconstrained_number(Bytes) ->
{Ints,Bytes3} = getoctets_as_list(Bytes2,Len),
{dec_integer(Ints),Bytes3}.
-dec_pos_integer(Ints) ->
- decpint(Ints, 8 * (length(Ints) - 1)).
dec_integer(Ints) when hd(Ints) band 255 =< 127 -> %% Positive number
decpint(Ints, 8 * (length(Ints) - 1));
dec_integer(Ints) -> %% Negative
diff --git a/lib/asn1/src/asn1rt_per_bin_rt2ct.erl b/lib/asn1/src/asn1rt_per_bin_rt2ct.erl
index 750b59aba6..1df757a47f 100644
--- a/lib/asn1/src/asn1rt_per_bin_rt2ct.erl
+++ b/lib/asn1/src/asn1rt_per_bin_rt2ct.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2012. 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
@@ -18,7 +18,6 @@
%%
%%
-module(asn1rt_per_bin_rt2ct).
-
%% encoding / decoding of PER aligned
-include("asn1_records.hrl").
@@ -605,19 +604,13 @@ encode_constrained_number({Lb,Ub},Val) when Val >= Lb, Ub >= Val ->
Range =< 65536 ->
% Size = {octets,<<Val2:16>>};
[20,2,<<Val2:16>>];
- Range =< 16#1000000 ->
- Octs = eint_positive(Val2),
-% [{bits,2,length(Octs)-1},{octets,Octs}];
- Len = length(Octs),
- [10,2,Len-1,20,Len,Octs];
- Range =< 16#100000000 ->
- Octs = eint_positive(Val2),
- Len = length(Octs),
- [10,2,Len-1,20,Len,Octs];
- Range =< 16#10000000000 ->
- Octs = eint_positive(Val2),
- Len = length(Octs),
- [10,3,Len-1,20,Len,Octs];
+ Range =< (1 bsl (255*8)) ->
+ Octs = binary:encode_unsigned(Val2),
+ RangeOcts = binary:encode_unsigned(Range - 1),
+ OctsLen = erlang:byte_size(Octs),
+ RangeOctsLen = erlang:byte_size(RangeOcts),
+ LengthBitsNeeded = asn1rt_per_bin:minimum_bits(RangeOctsLen - 1),
+ [10,LengthBitsNeeded,OctsLen-1,20,OctsLen,Octs];
true ->
exit({not_supported,{integer_range,Range}})
end;
@@ -661,18 +654,12 @@ decode_constrained_number(Buffer,{Lb,_Ub},Range) ->
getoctets(Buffer,1);
Range =< 65536 ->
getoctets(Buffer,2);
- Range =< 16#1000000 ->
- {Len,Bytes2} = decode_length(Buffer,{1,3}),
- {Octs,Bytes3} = getoctets_as_bin(Bytes2,Len),
- {dec_pos_integer(Octs),Bytes3};
- Range =< 16#100000000 ->
- {Len,Bytes2} = decode_length(Buffer,{1,4}),
- {Octs,Bytes3} = getoctets_as_bin(Bytes2,Len),
- {dec_pos_integer(Octs),Bytes3};
- Range =< 16#10000000000 ->
- {Len,Bytes2} = decode_length(Buffer,{1,5}),
- {Octs,Bytes3} = getoctets_as_bin(Bytes2,Len),
- {dec_pos_integer(Octs),Bytes3};
+ Range =< (1 bsl (255*8)) ->
+ OList = binary:bin_to_list(binary:encode_unsigned(Range - 1)),
+ RangeOctLen = length(OList),
+ {Len, Bytes} = decode_length(Buffer, {1, RangeOctLen}),
+ {Octs, RestBytes} = getoctets_as_bin(Bytes, Len),
+ {binary:decode_unsigned(Octs), RestBytes};
true ->
exit({not_supported,{integer_range,Range}})
end,
@@ -736,17 +723,12 @@ decode_unconstrained_number(Bytes) ->
{Ints,Bytes3} = getoctets_as_bin(Bytes2,Len),
{dec_integer(Ints),Bytes3}.
-
-dec_pos_integer(Ints) ->
- decpint(Ints).
dec_integer(Bin = <<0:1,_:7,_/binary>>) ->
decpint(Bin);
dec_integer(<<_:1,B:7,BitStr/bitstring>>) ->
Size = bit_size(BitStr),
<<I:Size>> = BitStr,
(-128 + B) bsl bit_size(BitStr) bor I.
-
-
decpint(Bin) ->
Size = bit_size(Bin),
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index 3b9a7532c0..b0c37d79e7 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -248,7 +248,7 @@ init_per_testcase(Func, Config) ->
[{case_dir, CaseDir}, {watchdog, Dog}|Config].
end_per_testcase(_Func, Config) ->
- true = code:del_path(?config(case_dir, Config)),
+ code:del_path(?config(case_dir, Config)),
test_server:timetrap_cancel(?config(watchdog, Config)).
%%------------------------------------------------------------------------------
@@ -773,6 +773,7 @@ per_open_type(Config, Rule, Opts) ->
testConstraints(Config) -> test(Config, fun testConstraints/3).
testConstraints(Config, Rule, Opts) ->
asn1_test_lib:compile("Constraints", Config, [Rule|Opts]),
+ asn1_test_lib:compile("LargeConstraints", Config, [Rule|Opts]),
testConstraints:int_constraints(Rule).
@@ -1236,6 +1237,27 @@ testName2Number(Config) ->
0 = 'S1AP-IEs':name2num_CauseMisc('control-processing-overload'),
'unknown-PLMN' = 'S1AP-IEs':num2name_CauseMisc(5),
+
+ %% OTP-10144
+ %% Test that n2n option generates name2num and num2name functions supporting
+ %% values not within the extension root if the enumeration type has an
+ %% extension marker.
+ N2NOptionsExt = [{n2n, 'NoExt'}, {n2n, 'Ext'}, {n2n, 'Ext2'}],
+ asn1_test_lib:compile("EnumN2N", Config, N2NOptionsExt),
+ %% Previously, name2num and num2name was not generated if the type didn't
+ %% have an extension marker:
+ 0 = 'EnumN2N':name2num_NoExt('blue'),
+ 2 = 'EnumN2N':name2num_NoExt('green'),
+ blue = 'EnumN2N':num2name_NoExt(0),
+ green = 'EnumN2N':num2name_NoExt(2),
+
+ %% Test enumeration extension:
+ 7 = 'EnumN2N':name2num_Ext2('orange'),
+ orange = 'EnumN2N':num2name_Ext2(7),
+ %% 7 is not defined in Ext, only in Ext2.
+ {asn1_enum, 7} = 'EnumN2N':num2name_Ext(7),
+ 7 = 'EnumN2N':name2num_Ext({asn1_enum, 7}),
+ 42 = 'EnumN2N':name2num_Ext2({asn1_enum, 42}),
ok.
ticket_7407(Config) ->
diff --git a/lib/asn1/test/asn1_SUITE_data/Constraints.py b/lib/asn1/test/asn1_SUITE_data/Constraints.py
index de48c4c2ca..87243121f7 100644
--- a/lib/asn1/test/asn1_SUITE_data/Constraints.py
+++ b/lib/asn1/test/asn1_SUITE_data/Constraints.py
@@ -4,9 +4,14 @@ BEGIN
-- Single Value
SingleValue ::= INTEGER (1)
SingleValue2 ::= INTEGER (1..20)
+predefined INTEGER ::= 1
+SingleValue3 ::= INTEGER (predefined | 5 | 10)
Range2to19 ::= INTEGER (1<..<20)
Range10to20 ::= INTEGER (10..20)
ContainedSubtype ::= INTEGER (INCLUDES Range10to20)
+-- Some ranges for additional constrained number testing.
+LongLong ::= INTEGER (0..18446744073709551615)
+Range256to65536 ::= INTEGER (256..65536)
FixedSize ::= OCTET STRING (SIZE(10))
FixedSize2 ::= OCTET STRING (SIZE(10|20))
VariableSize ::= OCTET STRING (SIZE(1..10))
diff --git a/lib/asn1/test/asn1_SUITE_data/EnumN2N.asn1 b/lib/asn1/test/asn1_SUITE_data/EnumN2N.asn1
new file mode 100644
index 0000000000..a724f2f3f5
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/EnumN2N.asn1
@@ -0,0 +1,25 @@
+EnumN2N DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
+
+NoExt ::= ENUMERATED {
+ blue(0),
+ red(1),
+ green(2)
+}
+
+Ext ::= ENUMERATED {
+ blue(0),
+ red(1),
+ green(2),
+ ...
+}
+
+Ext2 ::= ENUMERATED {
+ blue(0),
+ red(1),
+ green(2),
+ ...,
+ orange(7)
+}
+
+END
diff --git a/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn b/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn
index cacef8b922..55124f9449 100644
--- a/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn
+++ b/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn
@@ -1,7 +1,7 @@
--
-- %CopyrightBegin%
--
--- Copyright Ericsson AB 2001-2010. All Rights Reserved.
+-- Copyright Ericsson AB 2001-2012. 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/lib/asn1/test/asn1_SUITE_data/LargeConstraints.py b/lib/asn1/test/asn1_SUITE_data/LargeConstraints.py
new file mode 100644
index 0000000000..68c7616b62
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/LargeConstraints.py
@@ -0,0 +1,9 @@
+LargeConstraints DEFINITIONS ::=
+BEGIN
+
+-- Maximum number that can be encoded as a constrained whole number: 1 bsl (255*8)
+-- The number of octets needed to represent a number cannot be more than 255
+-- As the length field is encoded as a 8-bit bitfield.
+RangeMax ::= INTEGER (1..126238304966058622268417487065116999845484776053576109500509161826268184136202698801551568013761380717534054534851164138648904527931605160527688095259563605939964364716019515983399209962459578542172100149937763938581219604072733422507180056009672540900709554109516816573779593326332288314873251559077853068444977864803391962580800682760017849589281937637993445539366428356761821065267423102149447628375691862210717202025241630303118559188678304314076943801692528246980959705901641444238894928620825482303431806955690226308773426829503900930529395181208739591967195841536053143145775307050594328881077553168201547776)
+
+END
diff --git a/lib/asn1/test/testConstraints.erl b/lib/asn1/test/testConstraints.erl
index 1ce68ec522..543c106e8a 100644
--- a/lib/asn1/test/testConstraints.erl
+++ b/lib/asn1/test/testConstraints.erl
@@ -52,8 +52,6 @@ int_constraints(Rules) ->
?line {error,_Reason2} =
asn1_wrapper:encode('Constraints','SingleValue',1000)
end,
-
-
%%==========================================================
%% SingleValue2 ::= INTEGER (1..20)
@@ -86,7 +84,21 @@ int_constraints(Rules) ->
asn1_wrapper:encode('Constraints','SingleValue',1000)
end,
+ %%==========================================================
+ %% SingleValue3 ::= INTEGER (Predefined | 5 | 10)
+ %% Testcase for OTP-10139. A single value subtyping of an integer type
+ %% where one value is predefined.
+ %%==========================================================
+ ?line {ok,BytesSV3} = asn1_wrapper:encode('Constraints','SingleValue3',1),
+ ?line {ok,1} = asn1_wrapper:decode('Constraints','SingleValue3',
+ lists:flatten(BytesSV3)),
+ ?line {ok,BytesSV3_2} = asn1_wrapper:encode('Constraints','SingleValue3',5),
+ ?line {ok,5} = asn1_wrapper:decode('Constraints','SingleValue3',
+ lists:flatten(BytesSV3_2)),
+ ?line {ok,BytesSV3_3} = asn1_wrapper:encode('Constraints','SingleValue3',10),
+ ?line {ok,10} = asn1_wrapper:decode('Constraints','SingleValue3',
+ lists:flatten(BytesSV3_3)),
%%==========================================================
%% Range2to19 ::= INTEGER (1<..<20)
@@ -116,7 +128,65 @@ int_constraints(Rules) ->
?line {error,_Reason6} =
asn1_wrapper:encode('Constraints','Range2to19',20)
end,
+
+ %%==========================================================
+ %% Tests for Range above 16^4 up to maximum supported by asn1 assuming the
+ %% octet length field is encoded on max 8 bits
+ %%==========================================================
+ LastNumWithoutLengthEncoding = 65536,
+ ?line {ok,BytesFoo} = asn1_wrapper:encode('Constraints','Range256to65536',
+ LastNumWithoutLengthEncoding),
+ ?line {ok,LastNumWithoutLengthEncoding} =
+ asn1_wrapper:decode('Constraints','Range256to65536',lists:flatten(BytesFoo)),
+
+ FirstNumWithLengthEncoding = 65537,
+ ?line {ok,BytesBar} = asn1_wrapper:encode('LargeConstraints','RangeMax',
+ FirstNumWithLengthEncoding),
+ ?line {ok,FirstNumWithLengthEncoding} =
+ asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBar)),
+
+ FirstNumOver16_6 = 16777217,
+ ?line {ok, BytesBaz} =
+ asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_6),
+ ?line {ok, FirstNumOver16_6} =
+ asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBaz)),
+
+ FirstNumOver16_8 = 4294967297,
+ ?line {ok, BytesQux} =
+ asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_8),
+ ?line {ok, FirstNumOver16_8} =
+ asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesQux)),
+
+ FirstNumOver16_10 = 1099511627776,
+ ?line {ok, BytesBur} =
+ asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_10),
+ ?line {ok, FirstNumOver16_10} =
+ asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBur)),
+
+ FirstNumOver16_10 = 1099511627776,
+ ?line {ok, BytesBur} =
+ asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_10),
+ ?line {ok, FirstNumOver16_10} =
+ asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBur)),
+
+ HalfMax = 1 bsl (128*8),
+ ?line {ok, BytesHalfMax} =
+ asn1_wrapper:encode('LargeConstraints','RangeMax', HalfMax),
+ ?line {ok, HalfMax} =
+ asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesHalfMax)),
+
+ Max = 1 bsl (255*8),
+ ?line {ok, BytesMax} =
+ asn1_wrapper:encode('LargeConstraints','RangeMax', Max),
+ ?line {ok, Max} =
+ asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesMax)),
+ %% Random number within longlong range
+ LongLong = 12672809400538808320,
+ ?line {ok, BytesLongLong} =
+ asn1_wrapper:encode('Constraints','LongLong', LongLong),
+ ?line {ok, LongLong} =
+ asn1_wrapper:decode('Constraints','LongLong',lists:flatten(BytesLongLong)),
%%==========================================================
%% Constraint Combinations (Duboisson p. 285)
diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk
index 04f4b22421..81288496e9 100644
--- a/lib/asn1/vsn.mk
+++ b/lib/asn1/vsn.mk
@@ -1,2 +1,2 @@
#next version number to use is 2.0
-ASN1_VSN = 1.7
+ASN1_VSN = 1.8
diff --git a/lib/common_test/doc/src/Makefile b/lib/common_test/doc/src/Makefile
index 2ec6952710..99161ce68a 100644
--- a/lib/common_test/doc/src/Makefile
+++ b/lib/common_test/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. 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
@@ -46,7 +46,8 @@ CT_MODULES = \
ct_rpc \
ct_snmp \
unix_telnet \
- ct_slave
+ ct_slave \
+ ct_netconfc
CT_XML_FILES = $(CT_MODULES:=.xml)
@@ -123,7 +124,7 @@ $(HTMLDIR)/%.gif: %.gif
docs: pdf html man
-$(CT_XML_FILES):
+$(CT_XML_FILES): %.xml: ../../src/%.erl
escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -preprocess true -i $(XMERL_DIR)/include \
-i ../../../test_server/include -i ../../include \
-i ../../../../erts/lib/kernel/include -i ../../../../lib/kernel/include \
diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml
index 6babdb93af..b6d4a633cb 100644
--- a/lib/common_test/doc/src/common_test_app.xml
+++ b/lib/common_test/doc/src/common_test_app.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2003</year><year>2011</year>
+ <year>2003</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -99,11 +99,11 @@
be executed by Common Test. A test case is represented by an atom,
the name of the test case function. A test case group is
represented by a <c>group</c> tuple, where <c>GroupName</c>,
- an atom, is the name of the group (defined in <c>groups/0</c>).
+ an atom, is the name of the group (defined in <c><seealso marker="#Module:groups-0">groups/0</seealso></c>).
Execution properties for groups may also be specified, both
for a top level group and for any of its sub-groups.
Group execution properties specified here, will override
- properties in the group definition (see <c>groups/0</c>).
+ properties in the group definition (see <c><seealso marker="#Module:groups-0">groups/0</seealso></c>).
(With value <c>default</c>, the group definition properties
will be used).</p>
@@ -162,7 +162,7 @@
<v> Func = atom()</v>
<v> Args = list()</v>
<v> Fun = fun()</v>
- <v> Required = Key | {Key,SubKeys}</v>
+ <v> Required = Key | {Key,SubKeys} | {Key,SubKey} | {Key,SubKey,SubKeys}</v>
<v> Key = atom()</v>
<v> SubKeys = SubKey | [SubKey]</v>
<v> SubKey = atom()</v>
@@ -184,8 +184,8 @@
test cases in the suite).</p>
<p>The <c>timetrap</c> tag sets the maximum time each
- test case is allowed to execute (including <c>init_per_testcase/2</c>
- and <c>end_per_testcase/2</c>). If the timetrap time is
+ test case is allowed to execute (including <c><seealso marker="#Module:init_per_testcase-2">init_per_testcase/2</seealso></c>
+ and <c><seealso marker="#Module:end_per_testcase-2">end_per_testcase/2</seealso></c>). If the timetrap time is
exceeded, the test case fails with reason
<c>timetrap_timeout</c>. A <c>TimeFunc</c> function can be used to
set a new timetrap by returning a <c>TimeVal</c>. It may also be
@@ -201,11 +201,11 @@
in any of the configuration files, all test cases are skipped. For more
information about the 'require' functionality, see the
reference manual for the function
- <c>ct:require/[1,2]</c>.</p>
+ <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c>.</p>
<p>With <c>userdata</c>, it is possible for the user to
specify arbitrary test suite related information which can be
- read by calling <c>ct:userdata/2</c>.</p>
+ read by calling <c><seealso marker="ct#userdata-2">ct:userdata/2</seealso></c>.</p>
<p>The <c>ct_hooks</c> tag specifies which
<seealso marker="ct_hooks_chapter">Common Test Hooks</seealso>
@@ -264,7 +264,7 @@
<p>This function is called as the last test case in the
suite. It is meant to be used for cleaning up after
- <c>init_per_suite/1</c>.
+ <c><seealso marker="#Module:init_per_suite-1">init_per_suite/1</seealso></c>.
For information on <c>save_config</c>, please see
<seealso marker="dependencies_chapter#save_config">Dependencies
between Test Cases and Suites</seealso> in the User's Guide.</p>
@@ -289,7 +289,7 @@
<v> Func = atom()</v>
<v> Args = list()</v>
<v> Fun = fun()</v>
- <v> Required = Key | {Key,SubKeys}</v>
+ <v> Required = Key | {Key,SubKeys} | {Key,Subkey} | {Key,Subkey,SubKeys}</v>
<v> Key = atom()</v>
<v> SubKeys = SubKey | [SubKey]</v>
<v> SubKey = atom()</v>
@@ -309,13 +309,14 @@
<p>This is the test case group info function. It is supposed to
return a list of tagged tuples that specify various properties
related to the execution of a test case group (i.e. its test cases
- and sub-groups). Properties set by <c>groups/1</c> override
+ and sub-groups). Properties set by
+ <c><seealso marker="#Module:group-1">group/1</seealso></c> override
properties with the same key that have been previously set by
- <c>suite/0</c>.</p>
+ <c><seealso marker="#Module:suite-0">suite/0</seealso></c>.</p>
<p>The <c>timetrap</c> tag sets the maximum time each
- test case is allowed to execute (including <c>init_per_testcase/2</c>
- and <c>end_per_testcase/2</c>). If the timetrap time is
+ test case is allowed to execute (including <c><seealso marker="#Module:init_per_testcase-2">init_per_testcase/2</seealso></c>
+ and <c><seealso marker="#Module:end_per_testcase-2">end_per_testcase/2</seealso></c>). If the timetrap time is
exceeded, the test case fails with reason
<c>timetrap_timeout</c>. A <c>TimeFunc</c> function can be used to
set a new timetrap by returning a <c>TimeVal</c>. It may also be
@@ -330,11 +331,11 @@
in any of the configuration files, all test cases in this group are skipped.
For more information about the 'require' functionality, see the
reference manual for the function
- <c>ct:require/[1,2]</c>.</p>
+ <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c>.</p>
<p>With <c>userdata</c>, it is possible for the user to
specify arbitrary test case group related information which can be
- read by calling <c>ct:userdata/2</c>.</p>
+ read by calling <c><seealso marker="ct#userdata-2">ct:userdata/2</seealso></c>.</p>
<p>The <c>ct_hooks</c> tag specifies which
<seealso marker="ct_hooks_chapter">Common Test Hooks</seealso>
@@ -367,7 +368,7 @@
test case group. It typically contains initializations which are
common for all test cases and sub-groups in the group, and which
shall only be performed once. <c>GroupName</c> is the name of the
- group, as specified in the group definition (see <c>groups/0</c>). The
+ group, as specified in the group definition (see <c><seealso marker="#Module:groups-0">groups/0</seealso></c>). The
<c>Config</c> parameter is the configuration data which can be modified
here. The return value of this function is given as <c>Config</c>
to all test cases and sub-groups in the group. If <c>{skip,Reason}</c>
@@ -396,10 +397,10 @@
<p> OPTIONAL </p>
<p>This function is called after the execution of a test case group is finished.
- It is meant to be used for cleaning up after <c>init_per_group/2</c>.
+ It is meant to be used for cleaning up after <c><seealso marker="#Module:init_per_group-2">init_per_group/2</seealso></c>.
By means of <c>{return_group_result,Status}</c>, it is possible to return a
status value for a nested sub-group. The status can be retrieved in
- <c>end_per_group/2</c> for the group on the level above. The status will also
+ <c><seealso marker="#Module:end_per_group-2">end_per_group/2</seealso></c> for the group on the level above. The status will also
be used by Common Test for deciding if execution of a group should proceed in
case the property <c>sequence</c> or <c>repeat_until_*</c> is set.</p>
@@ -450,7 +451,7 @@
<p> OPTIONAL </p>
<p> This function is called after each test case, and can be used
- to clean up after <c>init_per_testcase/2</c> and the test case.
+ to clean up after <c><seealso marker="#Module:init_per_testcase-2">init_per_testcase/2</seealso></c> and the test case.
Any return value (besides <c>{fail,Reason}</c> and <c>{save_config,SaveConfig}</c>)
is ignored. By returning <c>{fail,Reason}</c>, <c>TestCase</c> will be marked as
failed (even though it was actually successful in the sense that it returned
@@ -476,7 +477,7 @@
<v> Func = atom()</v>
<v> Args = list()</v>
<v> Fun = fun()</v>
- <v> Required = Key | {Key,SubKeys}</v>
+ <v> Required = Key | {Key,SubKeys} | {Key,Subkey} | {Key,Subkey,SubKeys}</v>
<v> Key = atom()</v>
<v> SubKeys = SubKey | [SubKey]</v>
<v> SubKey = atom()</v>
@@ -492,15 +493,15 @@
<p>This is the test case info function. It is supposed to
return a list of tagged tuples that specify various properties
related to the execution of this particular test case.
- Properties set by <c>Testcase/0</c> override
+ Properties set by <c><seealso marker="#Module:Testcase-0">Testcase/0</seealso></c> override
properties that have been previously set for the test case
- by <c>group/1</c> or <c>suite/0</c>.</p>
+ by <c><seealso marker="#Module:group-1">group/1</seealso></c> or <c><seealso marker="#Module:suite-0">suite/0</seealso></c>.</p>
<p>The <c>timetrap</c> tag sets the maximum time the
test case is allowed to execute. If the timetrap time is
exceeded, the test case fails with reason
- <c>timetrap_timeout</c>. <c>init_per_testcase/2</c>
- and <c>end_per_testcase/2</c> are included in the
+ <c>timetrap_timeout</c>. <c><seealso marker="#Module:init_per_testcase-2">init_per_testcase/2</seealso></c>
+ and <c><seealso marker="#Module:end_per_testcase-2">end_per_testcase/2</seealso></c> are included in the
timetrap time. A <c>TimeFunc</c> function can be used to
set a new timetrap by returning a <c>TimeVal</c>. It may also be
used to trigger a timetrap timeout by, at some point, returning a
@@ -514,15 +515,15 @@
configuration files, the test case is skipped. For more
information about the 'require' functionality, see the
reference manual for the function
- <c>ct:require/[1,2]</c>.</p>
+ <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c>.</p>
<p>If <c>timetrap</c> and/or <c>require</c> is not set, the
- default values specified by <c>suite/0</c> (or
- <c>group/1</c>) will be used.</p>
+ default values specified by <c><seealso marker="#Module:suite-0">suite/0</seealso></c> (or
+ <c><seealso marker="#Module:group-1">group/1</seealso></c>) will be used.</p>
<p>With <c>userdata</c>, it is possible for the user to
specify arbitrary test case related information which can be
- read by calling <c>ct:userdata/3</c>.</p>
+ read by calling <c><seealso marker="ct#userdata-3">ct:userdata/3</seealso></c>.</p>
<p>Other tuples than the ones defined will simply be ignored.</p>
@@ -550,7 +551,7 @@
<p>This is the implementation of a test case. Here you must
call the functions you want to test, and do whatever you
need to check the result. If something fails, make sure the
- function causes a runtime error, or call <c>ct:fail/1/2</c>
+ function causes a runtime error, or call <c><seealso marker="ct#fail-1">ct:fail/1/2</seealso></c>
(which also causes the test case process to terminate).</p>
<p>Elements from the <c>Config</c> list can e.g. be read
diff --git a/lib/common_test/doc/src/config_file_chapter.xml b/lib/common_test/doc/src/config_file_chapter.xml
index 6a860bb58b..d90adf8d7b 100644
--- a/lib/common_test/doc/src/config_file_chapter.xml
+++ b/lib/common_test/doc/src/config_file_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -29,6 +29,8 @@
<file>config_file_chapter.xml</file>
</header>
+ <marker id="top"></marker>
+
<section>
<title>General</title>
@@ -78,7 +80,7 @@
test is skipped (unless a default value has been specified, see the
<seealso marker="write_test_chapter#info_function">test case info
function</seealso> chapter for details). There is also a function
- <c>ct:require/[1,2]</c> which can be called from a test case
+ <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c> which can be called from a test case
in order to check if a specific variable is available. The return
value from this function must be checked explicitly and appropriate
action be taken depending on the result (e.g. to skip the test case
@@ -88,7 +90,7 @@
info-list should look like this:
<c>{require,CfgVarName}</c> or <c>{require,AliasName,CfgVarName}</c>.
The arguments <c>AliasName</c> and <c>CfgVarName</c> are the same as the
- arguments to <c>ct:require/[1,2]</c> which are described in the
+ arguments to <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c> which are described in the
reference manual for <seealso marker="ct">ct</seealso>.
<c>AliasName</c> becomes an alias for the configuration variable,
and can be used as reference to the configuration data value.
@@ -101,7 +103,8 @@
(or test case) and improve readability.</item>
</list>
<p>To read the value of a config variable, use the function
- <c>get_config/[1,2,3]</c> which is also described in the reference
+ <c><seealso marker="ct#get_config-1">get_config/1/2/3</seealso></c>
+ which is also described in the reference
manual for <seealso marker="ct">ct</seealso>.</p>
<p>Example:</p>
<pre>
@@ -118,7 +121,7 @@
<section>
<title>Using configuration variables defined in multiple files</title>
<p>If a configuration variable is defined in multiple files and you
- want to access all possible values, you may use the <c>ct:get_config/3</c>
+ want to access all possible values, you may use the <c><seealso marker="ct#get_config-3">ct:get_config/3</seealso></c>
function and specify <c>all</c> in the options list. The values will then
be returned in a list and the order of the elements corresponds to the order
that the config files were specified at startup. Please see
@@ -130,7 +133,7 @@
<marker id="encrypted_config_files"></marker>
<p>It is possible to encrypt configuration files containing sensitive data
if these files must be stored in open and shared directories.</p>
- <p>Call <c>ct:encrypt_config_file/[2,3]</c> to have Common Test encrypt a
+ <p>Call <c><seealso marker="ct#encrypt_config_file-2">ct:encrypt_config_file/2/3</seealso></c> to have Common Test encrypt a
specified file using the DES3 function in the OTP <c>crypto</c> application.
The encrypted file can then be used as a regular configuration file,
in combination with other encrypted files or normal text files. The key
@@ -139,7 +142,7 @@
<c>decrypt_file</c> flag/option, or a key file in a predefined location.</p>
<p>Common Test also provides decryption functions,
- <c>ct:decrypt_config_file/[2,3]</c>, for recreating the original text
+ <c><seealso marker="ct#decrypt_config_file-2">ct:decrypt_config_file/2/3</seealso></c>, for recreating the original text
files.</p>
<p>Please see the <seealso marker="ct">ct</seealso> reference manual for
@@ -149,8 +152,8 @@
<section>
<title>Opening connections by using configuration data</title>
<p>There are two different methods for opening a connection
- by means of the support functions in e.g. <c>ct_ssh</c>, <c>ct_ftp</c>,
- and <c>ct_telnet</c>:</p>
+ by means of the support functions in e.g. <c><seealso marker="ct_ssh">ct_ssh</seealso></c>, <c><seealso marker="ct_ftp">ct_ftp</seealso></c>,
+ and <c><seealso marker="ct_telnet">ct_telnet</seealso></c>:</p>
<list>
<item>Using a configuration target name (an alias) as reference.</item>
<item>Using the configuration variable as reference.</item>
@@ -295,7 +298,7 @@
<pre>
[{ftp_host, [{ftp, "targethost"}, {username, "tester"}, {password, "letmein"}]},
- {lm_directory, "/test/loadmodules"}]</pre>
+ {lm_directory, "/test/loadmodules"}]</pre>
</section>
diff --git a/lib/common_test/doc/src/cover_chapter.xml b/lib/common_test/doc/src/cover_chapter.xml
index b7162cb542..803a71de07 100644
--- a/lib/common_test/doc/src/cover_chapter.xml
+++ b/lib/common_test/doc/src/cover_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2006</year><year>2011</year>
+ <year>2006</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -100,7 +100,7 @@
<p><c>$ ct_run -dir $TESTOBJS/db -cover $TESTOBJS/db/config/db.coverspec</c></p>
<p>You may also pass the cover specification file name in a
- call to <c>ct:run_test/1</c>, by adding a <c>{cover,CoverSpec}</c>
+ call to <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>, by adding a <c>{cover,CoverSpec}</c>
tuple to the <c>Opts</c> argument. Also, you can of course
enable code coverage in your test specifications (read
more in the chapter about
diff --git a/lib/common_test/doc/src/ct_hooks.xml b/lib/common_test/doc/src/ct_hooks.xml
index b98c04a850..b3e713c77f 100644
--- a/lib/common_test/doc/src/ct_hooks.xml
+++ b/lib/common_test/doc/src/ct_hooks.xml
@@ -5,7 +5,7 @@
<erlref>
<header>
<copyright>
- <year>2010</year><year>2011</year>
+ <year>2010</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -111,11 +111,12 @@
</func>
<func>
- <name>Module:pre_init_per_suite(SuiteName, Config, CTHState) -&gt;
+ <name>Module:pre_init_per_suite(SuiteName, InitData, CTHState) -&gt;
Result</name>
<fsummary>Called before init_per_suite</fsummary>
<type>
<v>SuiteName = atom()</v>
+ <v>InitData = Config | SkipOrFail</v>
<v>Config = NewConfig = [{Key,Value}]</v>
<v>CTHState = NewCTHState = term()</v>
<v>Result = {Return, NewCTHState}</v>
@@ -140,7 +141,8 @@
<p><c>SuiteName</c> is the name of the suite to be run.</p>
- <p><c>Config</c> is the original config list of the test suite.</p>
+ <p><c>InitData</c> is the original config list of the test suite, or
+ a <c>SkipOrFail</c> tuple if a previous CTH has returned this.</p>
<p><c>CTHState</c> is the current internal state of the CTH.</p>
@@ -212,11 +214,12 @@
</func>
<func>
- <name>Module:pre_init_per_group(GroupName, Config, CTHState) -&gt;
+ <name>Module:pre_init_per_group(GroupName, InitData, CTHState) -&gt;
Result</name>
<fsummary>Called before init_per_group</fsummary>
<type>
<v>GroupName = atom()</v>
+ <v>InitData = Config | SkipOrFail</v>
<v>Config = NewConfig = [{Key,Value}]</v>
<v>CTHState = NewCTHState = term()</v>
<v>Result = {NewConfig | SkipOrFail, NewCTHState}</v>
@@ -269,11 +272,12 @@
</func>
<func>
- <name>Module:pre_init_per_testcase(TestcaseName, Config, CTHState) -&gt;
+ <name>Module:pre_init_per_testcase(TestcaseName, InitData, CTHState) -&gt;
Result</name>
<fsummary>Called before init_per_testcase</fsummary>
<type>
<v>TestcaseName = atom()</v>
+ <v>InitData = Config | SkipOrFail</v>
<v>Config = NewConfig = [{Key,Value}]</v>
<v>CTHState = NewCTHState = term()</v>
<v>Result = {NewConfig | SkipOrFail, NewCTHState}</v>
@@ -330,11 +334,12 @@
</func>
<func>
- <name>Module:pre_end_per_group(GroupName, Config, CTHState) -&gt;
+ <name>Module:pre_end_per_group(GroupName, EndData, CTHState) -&gt;
Result</name>
<fsummary>Called before end_per_group</fsummary>
<type>
<v>GroupName = atom()</v>
+ <v>EndData = Config | SkipOrFail</v>
<v>Config = NewConfig = [{Key,Value}]</v>
<v>CTHState = NewCTHState = term()</v>
<v>Result = {NewConfig | SkipOrFail, NewCTHState}</v>
@@ -387,11 +392,12 @@
</func>
<func>
- <name>Module:pre_end_per_suite(SuiteName, Config, CTHState) -&gt;
+ <name>Module:pre_end_per_suite(SuiteName, EndData, CTHState) -&gt;
Result</name>
<fsummary>Called before end_per_suite</fsummary>
<type>
<v>SuiteName = atom()</v>
+ <v>EndData = Config | SkipOrFail</v>
<v>Config = NewConfig = [{Key,Value}]</v>
<v>CTHState = NewCTHState = term()</v>
<v>Result = {NewConfig | SkipOrFail, NewCTHState}</v>
diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml
index 1dbb841fb0..86237f5fc1 100644
--- a/lib/common_test/doc/src/ct_hooks_chapter.xml
+++ b/lib/common_test/doc/src/ct_hooks_chapter.xml
@@ -192,12 +192,12 @@
<section>
<title>External configuration data and Logging</title>
<p>It's possible in the CTH to read configuration data values
- by calling <c>ct:get_config/1/2/3</c> (as explained in the
+ by calling <c><seealso marker="ct#get_config-1">ct:get_config/1/2/3</seealso></c> (as explained in the
<seealso marker="config_file_chapter#require_config_data">
External configuration data</seealso>
chapter). The config variables in question must, as always, first have been
<c>required</c> by means of a suite-, group-, or test case info function,
- or the <c>ct:require/1/2</c> function. Note that the latter can also be used
+ or the <c><seealso marker="ct#require-1">ct:require/1/2</seealso></c> function. Note that the latter can also be used
in CT hook functions.</p>
<p>The CT hook functions may call any of the logging functions available
in the <c>ct</c> interface to print information to the log files, or to
@@ -252,6 +252,13 @@
{ok, Handle} -&gt;
{[{db_handle, Handle} | Config], CTHState#state{ handle = Handle }}
end.</code>
+ <note>If using multiple CTHs, the first part of the return tuple will be
+ used as input for the next CTH. So in the case above the next CTH might
+ get <c>{fail,Reason}</c> as the second parameter. If you have many CTHs
+ which interact, it might be a good idea to not let each CTH return
+ <c>fail</c> or <c>skip</c>. Instead return that an action should be taken
+ through the <c>Config</c> list and implement a CTH which at the end takes
+ the correct action. </note>
</section>
@@ -453,7 +460,7 @@ terminate(State) ->
<cell>Captures all test results and outputs them as surefire XML into
a file. The file which is created is by default called junit_report.xml.
The name can be by setting the path option for this hook. e.g.
- <code>-ct_hooks cth_surefix [{path,"/tmp/report.xml"}]</code>
+ <code>-ct_hooks cth_surefire [{path,"/tmp/report.xml"}]</code>
Surefire XML can forinstance be used by Jenkins to display test
results.</cell>
</row>
diff --git a/lib/common_test/doc/src/ct_master_chapter.xml b/lib/common_test/doc/src/ct_master_chapter.xml
index f4f0ecad62..9e848e99bb 100644
--- a/lib/common_test/doc/src/ct_master_chapter.xml
+++ b/lib/common_test/doc/src/ct_master_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2006</year><year>2010</year>
+ <year>2006</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -124,7 +124,8 @@
<p><c>NodeRef = NodeAlias | node() | master</c></p>
<p>A <c>NodeAlias</c> (<c>atom()</c>) is used in a test specification as a
- reference to a node name (so the actual node name only needs to be declared once).
+ reference to a node name (so the actual node name only needs to be declared once,
+ which can of course also be achieved using constants).
The alias is declared with a <c>node</c> term:</p>
<p><c>{node, NodeAlias, NodeName}</c></p>
@@ -141,30 +142,32 @@
CT Master:</p>
<pre>
- {node, node1, ct_node@host_x}.
- {node, node2, ct_node@host_y}.
-
- {logdir, master, "/home/test/master_logs"}.
- {logdir, "/home/test/logs"}.
+ {define, 'Top', "/home/test"}.
+ {define, 'T1', "'Top'/t1"}.
+ {define, 'T2', "'Top'/t2"}.
+ {define, 'T3', "'Top'/t3"}.
+ {define, 'CfgFile', "config.cfg"}.
+ {define, 'Node', ct_node}.
+
+ {node, node1, 'Node@host_x'}.
+ {node, node2, 'Node@host_y'}.
+
+ {logdir, master, "'Top'/master_logs"}.
+ {logdir, "'Top'/logs"}.
- {config, node1, "/home/test/t1/cfg/config.cfg"}.
- {config, node2, "/home/test/t2/cfg/config.cfg"}.
- {config, "/home/test/t3/cfg/config.cfg"}.
+ {config, node1, "'T1'/'CfgFile'"}.
+ {config, node2, "'T2'/'CfgFile'"}.
+ {config, "'T3'/'CfgFile'"}.
- {alias, t1, "/home/test/t1"}.
- {alias, t2, "/home/test/t2"}.
- {alias, t3, "/home/test/t3"}.
+ {suites, node1, 'T1', all}.
+ {skip_suites, node1, 'T1', [t1B_SUITE,t1D_SUITE], "Not implemented"}.
+ {skip_cases, node1, 'T1', t1A_SUITE, [test3,test4], "Irrelevant"}.
+ {skip_cases, node1, 'T1', t1C_SUITE, [test1], "Ignore"}.
- {suites, node1, t1, all}.
- {skip_suites, node1, t1, [t1B_SUITE,t1D_SUITE], "Not implemented"}.
- {skip_cases, node1, t1, t1A_SUITE, [test3,test4], "Irrelevant"}.
- {skip_cases, node1, t1, t1C_SUITE, [test1], "Ignore"}.
+ {suites, node2, 'T2', [t2B_SUITE,t2C_SUITE]}.
+ {cases, node2, 'T2', t2A_SUITE, [test4,test1,test7]}.
- {suites, node2, t2, [t2B_SUITE,t2C_SUITE]}.
- {cases, node2, t2, t2A_SUITE, [test4,test1,test7]}.
-
- {skip_suites, t3, all, "Not implemented"}.
- </pre>
+ {skip_suites, 'T3', all, "Not implemented"}.</pre>
<p>This example specifies the same tests as the original example. But
now if started with a call to <c>ct_master:run(TestSpecName)</c>, the
@@ -190,10 +193,6 @@
name as the Common Test node in question (typically <c>ct@somehost</c> if started
with the <c>ct_run</c> program), will be performed. Tests without explicit
node association will always be performed too of course!</p>
-
- <note><p>It is recommended that absolute paths are used for log directories,
- config files and test directory aliases in the test specifications so that
- current working directory settings are not important.</p></note>
</section>
<section>
diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml
index 078b9b958c..9cc5495af7 100644
--- a/lib/common_test/doc/src/ct_run.xml
+++ b/lib/common_test/doc/src/ct_run.xml
@@ -36,6 +36,8 @@
OS command line.
</comsummary>
+ <marker id="top"></marker>
+
<description>
<p>The <c>ct_run</c> program is automatically installed with Erlang/OTP
and Common Test (please see the Installation chapter in the Common
@@ -46,7 +48,7 @@
particular mode.</p>
<p>There is an interface function that corresponds to this program,
- called <c>ct:run_test/1</c>, for starting Common Test from the Erlang
+ called <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>, for starting Common Test from the Erlang
shell (or an Erlang program). Please see the <c>ct</c> man page for
details.</p>
@@ -72,6 +74,10 @@
following <c>-erl_args</c> on the command line. These directories are added
to the code path normally (i.e. on specified form)</p>
+ <p>Exit status is set before the program ends. Value <c>0</c> indicates a successful
+ test result, <c>1</c> indicates one or more failed or auto-skipped test cases, and
+ <c>2</c> indicates test execution failure.</p>
+
<p>If <c>ct_run</c> is called with option:</p>
<pre>-help</pre>
<p>it prints all valid start flags to stdout.</p>
@@ -88,11 +94,13 @@
[-step [config | keep_inactive]]
[-config ConfigFile1 ConfigFile2 .. ConfigFileN]
[-userconfig CallbackModule1 ConfigString1 and CallbackModule2
- ConfigString2 and .. and CallbackModuleN ConfigStringN]
+ ConfigString2 and .. CallbackModuleN ConfigStringN]
[-decrypt_key Key] | [-decrypt_file KeyFile]
[-label Label]
[-logdir LogDir]
[-logopts LogOpts]
+ [-verbosity GenVLevel | [Category1 VLevel1 and
+ Category2 VLevel2 and .. CategoryN VLevelN]]
[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]
[-stylesheet CSSFile]
[-cover CoverCfgFile]
@@ -107,7 +115,11 @@
[-repeat N [-force_stop]] |
[-duration HHMMSS [-force_stop]] |
[-until [YYMoMoDD]HHMMSS [-force_stop]]
- [-basic_html]</pre>
+ [-basic_html]
+ [-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
+ CTHModuleN CTHOptsN]
+ [-exit_status ignore_config]
+ </pre>
</section>
<section>
<title>Run tests using test specification</title>
@@ -120,6 +132,8 @@
[-label Label]
[-logdir LogDir]
[-logopts LogOpts]
+ [-verbosity GenVLevel | [Category1 VLevel1 and
+ Category2 VLevel2 and .. CategoryN VLevelN]]
[-allow_user_terms]
[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]
[-stylesheet CSSFile]
@@ -135,7 +149,11 @@
[-repeat N [-force_stop]] |
[-duration HHMMSS [-force_stop]] |
[-until [YYMoMoDD]HHMMSS [-force_stop]]
- [-basic_html]</pre>
+ [-basic_html]
+ [-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
+ CTHModuleN CTHOptsN]
+ [-exit_status ignore_config]
+ </pre>
</section>
<section>
<title>Run tests in web based GUI</title>
@@ -147,6 +165,8 @@
[-userconfig CallbackModule1 ConfigString1 and CallbackModule2
ConfigString2 and .. and CallbackModuleN ConfigStringN]
[-logopts LogOpts]
+ [-verbosity GenVLevel | [Category1 VLevel1 and
+ Category2 VLevel2 and .. CategoryN VLevelN]]
[-decrypt_key Key] | [-decrypt_file KeyFile]
[-include InclDir1 InclDir2 .. InclDirN]
[-no_auto_compile]
diff --git a/lib/common_test/doc/src/event_handler_chapter.xml b/lib/common_test/doc/src/event_handler_chapter.xml
index a5886b9687..10a9b52d39 100644
--- a/lib/common_test/doc/src/event_handler_chapter.xml
+++ b/lib/common_test/doc/src/event_handler_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2006</year><year>2011</year>
+ <year>2006</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -64,7 +64,7 @@
<marker id="usage"></marker>
<title>Usage</title>
<p>Event handlers may be installed by means of an <c>event_handler</c>
- start flag (<c>ct_run</c>) or option (<c>ct:run_test/1</c>), where the
+ start flag (<c>ct_run</c>) or option (<c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>), where the
argument specifies the names of one or more event handler modules.
Example:</p>
<p><c>$ ct_run -suite test/my_SUITE -event_handler handlers/my_evh1
@@ -78,7 +78,7 @@
example).</p>
<p>An event_handler tuple in the argument <c>Opts</c> has the following
- definition (see also <c>ct:run_test/1</c> in the reference manual):</p>
+ definition (see also <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c> in the reference manual):</p>
<pre>
{event_handler,EventHandlers}
@@ -205,7 +205,7 @@
{error,{RunTimeError,StackTrace}} |
{timetrap_timeout,integer()} |
{failed,{Suite,end_per_testcase,FailInfo}}</c>, reason for failure.</p>
- <p><c>RequireInfo = {not_available,atom()}</c>, why require has failed.</p>
+ <p><c>RequireInfo = {not_available,atom() | tuple()}</c>, why require has failed.</p>
<p><c>FailInfo = {timetrap_timeout,integer()} |
{RunTimeError,StackTrace} |
UserTerm</c>,
@@ -233,7 +233,7 @@
reason for auto skipping <c>Func</c>.</p>
<p><c>FailReason = {Suite,ConfigFunc,FailInfo}} |
{Suite,FailedCaseInSequence}</c>, reason for failure.</p>
- <p><c>RequireInfo = {not_available,atom()}</c>, why require has failed.</p>
+ <p><c>RequireInfo = {not_available,atom() | tuple()}</c>, why require has failed.</p>
<p><c>ConfigFunc = init_per_suite | init_per_group</c></p>
<p><c>FailInfo = {timetrap_timeout,integer()} |
{RunTimeError,StackTrace} |
@@ -308,7 +308,7 @@
manager can look like.</p>
<note><p>To ensure that printouts to standard out (or printouts made with
- <c>ct:log/2/3</c> or <c>ct:pal/2/3</c>) get written to the test case log
+ <c><seealso marker="ct#log-2">ct:log/2/3</seealso></c> or <c><seealso marker="ct:pal-2">ct:pal/2/3</seealso></c>) get written to the test case log
file, and not to the Common Test framework log, you can syncronize
with the Common Test server by matching on the <c>tc_start</c> and <c>tc_done</c>
events. In the period between these events, all IO gets directed to the
diff --git a/lib/common_test/doc/src/example_chapter.xml b/lib/common_test/doc/src/example_chapter.xml
index f269dba2cd..2333f92989 100644
--- a/lib/common_test/doc/src/example_chapter.xml
+++ b/lib/common_test/doc/src/example_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2010</year>
+ <year>2003</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -28,7 +28,8 @@
<rev></rev>
<file>example_chapter.xml</file>
</header>
-
+
+ <marker id="top"></marker>
<section>
<title>Test suite example</title>
diff --git a/lib/common_test/doc/src/getting_started_chapter.xml b/lib/common_test/doc/src/getting_started_chapter.xml
index 039578dd2e..3cf04bb1a2 100644
--- a/lib/common_test/doc/src/getting_started_chapter.xml
+++ b/lib/common_test/doc/src/getting_started_chapter.xml
@@ -90,7 +90,7 @@
<p>As you can understand from the illustration above, Common Test requires
that a test case generates a runtime error to indicate failure (e.g.
by causing a bad match error or by calling <c>exit/1</c>, preferrably
- through the <c>ct:fail/1,2</c> help function). A succesful execution is
+ through the <c><seealso marker="ct#fail-1">ct:fail/1,2</seealso></c> help function). A succesful execution is
indicated by means of a normal return from the test case function.
</p>
</section>
@@ -214,11 +214,56 @@
<section>
<title>What happens next?</title>
- <p>
- You will find detailed information about the basics introduced here in this
- chapter in the following chapters in the User's Guide, as well as
- presentations of many more useful features. Have fun!
- </p>
+
+ <p>Well, you might already be asking yourself questions such as:</p>
+
+ <list>
+ <item>"How and where can I specify variable data for my tests that mustn't
+ be hard-coded in the test suites (such as host names, addresses,
+ user login data, etc)?" The
+ <seealso marker="config_file_chapter#top">External Configuration Data</seealso>
+ chapter will give you that information.
+ </item>
+ <item>"Is there a way to declare a number of different tests and run them
+ in one session without having to write my own scripts? And can such
+ declarations be used for regression testing?" The
+ <seealso marker="run_test_chapter#test_specifications">Test Specifications</seealso>
+ chapter answers these questions.
+ </item>
+ <item>"Can test cases and/or test runs be automatically repeated?" Learn more about
+ <seealso marker="write_test_chapter#test_case_groups">Test Case Groups</seealso>
+ and also read about start flags/options in the
+ <seealso marker="run_test_chapter#ct_run">Running Tests</seealso> chapter and
+ the Reference Manual.
+ </item>
+ <item>"Will Common Test execute my test cases in sequence or in parallel?" The
+ <seealso marker="write_test_chapter#test_case_groups">Test Case Groups</seealso>
+ section in the Running Tests chapter will give you the answer.
+ </item>
+ <item>"What's the syntax for timetraps (mentioned above), and how do I set them?"
+ This is explained in the
+ <seealso marker="write_test_chapter#timetraps">Timetrap Timeouts</seealso>
+ part of the Writing Test Suites chapter.
+ </item>
+ <item>"What functions are available for logging and printing?" Check the
+ <seealso marker="write_test_chapter#logging">Logging</seealso>
+ section in the Writing Test Suites chapter.
+ </item>
+ <item>"I need data files for my tests. Where do I store them preferrably?"
+ You should read about
+ <seealso marker="write_test_chapter#data_priv_dir">Data and Private
+ Directories</seealso> for information about this.
+ </item>
+ <item>"May I start with a test suite example, please?"
+ <seealso marker="example_chapter#top">Sure!</seealso>
+ </item>
+ </list>
+ <p>You will probably want to get started on your own first test suites now, while
+ at the same time digging deeper into the Common Test User's Guide and Reference Manual.
+ You will find that there's lots more to learn about the things that have been introduced
+ in this chapter. You will of course also be presented many more useful features, such as the
+ ones listed above. Have fun!
+ </p>
</section>
</chapter>
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index 050b8774ca..abe8cb2041 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,236 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.6.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The interactive mode (ct_run -shell) would not start
+ properly. This error has been fixed.</p>
+ <p>
+ Own Id: OTP-10414</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Common_Test 1.6.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ If a CT hook function caused a crash, this could in some
+ situations cause Common Test to terminate due to an
+ illegal IO operation. This error has been corrected.</p>
+ <p>
+ Own Id: OTP-10050 Aux Id: seq12039 </p>
+ </item>
+ <item>
+ <p>
+ The Common Test documentation states that timetraps are
+ never active during execution of CT hook functions. This
+ was only true for post hook functions, not for pre hook
+ functions. The code for CT hooks has been modified to
+ behave according to the documentation.</p>
+ <p>
+ Own Id: OTP-10069</p>
+ </item>
+ <item>
+ <p>
+ If a CT hook function would call the exit/1 or throw/1
+ BIF (possibly indirectly, e.g. as a result of a timeout
+ in gen_server:call/3), Common Test would hang. This
+ problem has been fixed.</p>
+ <p>
+ Own Id: OTP-10072 Aux Id: seq12053 </p>
+ </item>
+ <item>
+ <p>
+ The documentation has been updated with information about
+ how to deal with chaining of hooks which return
+ fail/skip.</p>
+ <p>
+ Own Id: OTP-10077 Aux Id: seq12048 </p>
+ </item>
+ <item>
+ <p>
+ When ct_hooks called the id/1 functions of multiple
+ hooks, it would reverse the order of the hooks and call
+ the proceeding init/2 calls in the wrong order. This has
+ been fixed.</p>
+ <p>
+ Own Id: OTP-10135</p>
+ </item>
+ <item>
+ <p>
+ The surefire hook now correctly handles autoskipped
+ initialization and test functions.</p>
+ <p>
+ Own Id: OTP-10158</p>
+ </item>
+ <item>
+ <p>
+ The ct:get_status/0 function failed to report status if a
+ parallel test case group was running at the time of the
+ call. This has been fixed and the return value for the
+ function has been updated. Please see the ct reference
+ manual for details.</p>
+ <p>
+ Own Id: OTP-10172</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The support for "silent connections" has been updated to
+ include ssh. Also, a silent_connections term has been
+ added to the set of test specification terms.</p>
+ <p>
+ Own Id: OTP-9625 Aux Id: seq11918 </p>
+ </item>
+ <item>
+ <p>
+ It is now possible to specify an arbitrarily large tuple
+ as the requires config data when using require and
+ ct:get_config. See the ct:get_config and ct:require
+ reference manual pages for details about which keys are
+ allowed.</p>
+ <p>
+ This change introduces a backwards incompatability in the
+ <c>ct:require/2</c> interface. Previously when doing
+ <c>ct:require(a_name,{key,subkey})</c>, a_name would be
+ associated with key. This has been changed to that
+ <c>a_name</c> is associated with <c>subkey</c>. This
+ change also effects using <c>require</c> in an
+ suite/group/testcase info function.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-9626 Aux Id: seq11920 </p>
+ </item>
+ <item>
+ <p>
+ The ct_run program now sets the OS process exit status
+ before it ends. Value 0 indicates a successful test
+ result, 1 indicates one or more failed or auto-skipped
+ test cases, and 2 indicates test execution failure.</p>
+ <p>
+ Own Id: OTP-9865 Aux Id: OTP-10087 </p>
+ </item>
+ <item>
+ <p>
+ It is now possible to sort the HTML tables by clicking on
+ the header elements. In order to reset a sorted table,
+ the browser window should simply be refreshed. This
+ feature requires that the browser supports javascript,
+ and has javascript execution enabled. If the 'ct_run
+ -basic_html' flag is used, no javascript code is included
+ in the generated HTML code.</p>
+ <p>
+ Own Id: OTP-9896 Aux Id: seq12034, OTP-9835 </p>
+ </item>
+ <item>
+ <p>
+ A netconf client, ct_netconfc, is added to common_test.
+ It supports basic netconf functionality over SSH. In
+ order to allow testing of both success and failure cases,
+ it is intentionally written to allow non-standard
+ behavior.</p>
+ <p>
+ Own Id: OTP-10025</p>
+ </item>
+ <item>
+ <p>
+ The test specification term {define,Constant,Value} has
+ been introduced, which makes it possible to replace
+ constant names (atom()) with values (term()) in arbitrary
+ test specification terms. The 'define' makes the (now
+ deprecated) 'alias' term obsolete. More details,
+ including examples, can be found in the Test
+ Specifications chapter in the Common Test User's Guide.</p>
+ <p>
+ Own Id: OTP-10049</p>
+ </item>
+ <item>
+ <p>
+ Verbosity levels for log printouts has been added. This
+ makes it possible to specify preferred verbosity for
+ different categories of log printouts, as well as general
+ printouts (such as standard IO), to allow control over
+ which strings get printed and which get ignored. New
+ versions of the Common Test logging functions, ct:log,
+ ct:pal and ct:print, have been introduced, with a new
+ Importance argument added. The Importance value is
+ compared to the verbosity level at runtime. More
+ information can be found in the chapter about Logging in
+ the Common Test User's Guide.</p>
+ <p>
+ Own Id: OTP-10067 Aux Id: seq12050 </p>
+ </item>
+ <item>
+ <p>
+ The return values of ct:run_test/1 and ct:run_testspec/1
+ have been changed from an uninformative 'ok' (independent
+ of the test outcome) to a value,
+ {Ok,Failed,{UserSkipped,AutoSkipped}} (all integers),
+ that presents the final test case result, or a value,
+ {error,Reason}, that informs about fatal test execution
+ failure. See details in the reference manual for ct.</p>
+ <p>
+ Own Id: OTP-10087 Aux Id: OTP-9865 </p>
+ </item>
+ <item>
+ <p>
+ The test specification syntax has been updated with new
+ and missing terms, such as 'define', 'verbosity',
+ 'auto_compile', 'stylesheet', 'silent_connections',
+ 'basic_html' and 'release_shell'. See the Test
+ Specification chapter in the Common Test User's Guide for
+ details.</p>
+ <p>
+ Own Id: OTP-10089 Aux Id: OTP-10049 </p>
+ </item>
+ <item>
+ <p>
+ It is now possible to pause execution of a test case, by
+ calling the ct:break/1/2 function. Execution is resumed
+ with a call to ct:continue/0/1. Break/continue also works
+ for test cases executing in parallel. See the ct
+ reference manual for details.</p>
+ <p>
+ Own Id: OTP-10127</p>
+ </item>
+ <item>
+ <p>
+ It is now possible to send user defined events from a
+ testcase which will be picked up by the installed event
+ handler.</p>
+ <p>
+ Own Id: OTP-10157</p>
+ </item>
+ <item>
+ <p>
+ A new start option, release_shell, for ct:run_test/1, has
+ been added, which makes Common Test release the shell
+ process after the test suite compilation phase is
+ finished. For details, see the Running Tests chapter in
+ the User's Guide.</p>
+ <p>
+ Own Id: OTP-10248 Aux Id: OTP-10127 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.6.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/common_test/doc/src/ref_man.xml b/lib/common_test/doc/src/ref_man.xml
index a9fdef7359..6fede88434 100644
--- a/lib/common_test/doc/src/ref_man.xml
+++ b/lib/common_test/doc/src/ref_man.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>2003</year><year>2011</year>
+ <year>2003</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -71,6 +71,7 @@
<xi:include href="ct_cover.xml"/>
<xi:include href="ct_ftp.xml"/>
<xi:include href="ct_ssh.xml"/>
+ <xi:include href="ct_netconfc.xml"/>
<xi:include href="ct_rpc.xml"/>
<xi:include href="ct_snmp.xml"/>
<xi:include href="ct_telnet.xml"/>
diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml
index 30486d3eec..ea62df27cc 100644
--- a/lib/common_test/doc/src/run_test_chapter.xml
+++ b/lib/common_test/doc/src/run_test_chapter.xml
@@ -178,6 +178,8 @@
<item><c><![CDATA[-basic_html]]></c>, switches off html enhancements that might not be compatible with older browsers.</item>
<item><c><![CDATA[-logopts <opts>]]></c>, makes it possible to modify aspects of the logging behaviour, see
<seealso marker="run_test_chapter#logopts">Log options</seealso> below.</item>
+ <item><c><![CDATA[-verbosity <levels>]]></c>, sets <seealso marker="write_test_chapter#logging">verbosity levels
+ for printouts</seealso>.</item>
</list>
<note><p>Directories passed to Common Test may have either relative or absolute paths.</p></note>
@@ -196,60 +198,73 @@
the current working directory of the Erlang Runtime System during the test run!</p>
</note>
- <p>For more information about the <c>ct_run</c> program, see the
- <seealso marker="install_chapter#general">Installation</seealso> chapter.
- </p>
- </section>
-
- <section>
- <title>Running tests from the Web based GUI</title>
-
- <p>The web based GUI, VTS, is started with the <c>ct_run</c>
- program. From the GUI you can load config files, and select
- directories, suites and cases to run. You can also state the
- config files, directories, suites and cases on the command line
- when starting the web based GUI.
- </p>
-
+ <p>The <c>ct_run</c> program sets the exit status before shutting down. The following values
+ are defined:</p>
<list>
- <item><c>ct_run -vts</c></item>
- <item><c><![CDATA[ct_run -vts -config <configfilename>]]></c></item>
- <item><c><![CDATA[ct_run -vts -config <configfilename> -suite <suitewithfullpath>
- -case <casename>]]></c></item>
+ <item><c>0</c> indicates a successful testrun, i.e. one without failed or auto-skipped test cases.</item>
+ <item><c>1</c> indicates that one or more test cases have failed, or have been auto-skipped.</item>
+ <item><c>2</c> indicates that the test execution has failed because of e.g. compilation errors, an
+ illegal return value from an info function, etc.</item>
</list>
+ <p>If auto-skipped test cases should not affect the exit status, you may change the default
+ behaviour using start flag:</p>
+ <pre>-exit_status ignore_config</pre>
- <p>From the GUI you can run tests and view the result and the logs.
+ <p>For more information about the <c>ct_run</c> program, see the
+ <seealso marker="ct_run#top">Reference Manual</seealso> and the
+ <seealso marker="install_chapter#general">Installation</seealso> chapter.
</p>
-
- <p>Note that <c>ct_run -vts</c> will try to open the Common Test start
- page in an existing web browser window or start the browser if it is
- not running. Which browser should be started may be specified with
- the browser start command option:</p>
- <p><c><![CDATA[ct_run -vts -browser <browser_start_cmd>]]></c></p>
- <p>Example:</p>
- <p><c><![CDATA[$ ct_run -vts -browser 'firefox&']]></c></p>
- <p>Note that the browser must run as a separate OS process or VTS will hang!</p>
- <p>If no specific browser start command is specified, Firefox will
- be the default browser on Unix platforms and Internet Explorer on Windows.
- If Common Test fails to start a browser automatically, or <c>'none'</c> is
- specified as the value for -browser (i.e. <c>-browser none</c>), start your
- favourite browser manually and type in the URL that Common Test
- displays in the shell.</p>
</section>
-
+
<section>
<title>Running tests from the Erlang shell or from an Erlang program</title>
<p>Common Test provides an Erlang API for running tests. The main (and most
flexible) function for specifying and executing tests is called
- <c>ct:run_test/1</c>. This function takes the same start parameters as
- the <c>ct_run</c> program described above, only the flags are instead
+ <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>.
+ This function takes the same start parameters as
+ the <c><seealso marker="run_test_chapter#ct_run">ct_run</seealso></c>
+ program described above, only the flags are instead
given as options in a list of key-value tuples. E.g. a test specified
with <c>ct_run</c> like:</p>
+
<p><c>$ ct_run -suite ./my_SUITE -logdir ./results</c></p>
- <p>is with <c>ct:run_test/1</c> specified as:</p>
+ <p>is with <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c> specified as:</p>
<p><c>1> ct:run_test([{suite,"./my_SUITE"},{logdir,"./results"}]).</c></p>
- <p>For detailed documentation, please see the <c>ct</c> manual page.</p>
+
+ <p>The function returns the test result, represented by the tuple:
+ <c>{Ok,Failed,{UserSkipped,AutoSkipped}}</c>, where each element is an
+ integer. If test execution fails, the function returns the tuple:
+ <c>{error,Reason}</c>, where the term <c>Reason</c> explains the
+ failure.</p>
+
+ <section>
+ <title>Releasing the Erlang shell</title>
+ <p>During execution of tests, started with
+ <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>,
+ the Erlang shell process, controlling stdin, will remain the top
+ level process of the Common Test system of processes. The result
+ is that the Erlang shell is not available for interaction during
+ the test run. If this is not desirable, maybe because the shell is needed
+ for debugging purposes or for interaction with the SUT during test
+ execution, you may set the <c>release_shell</c> start option to
+ <c>true</c> (in the call to <c>ct:run_test/1</c> or by
+ using the corresponding test specification term, see below). This will
+ make Common Test release the shell immediately after the test suite
+ compilation stage. To accomplish this, a test runner process
+ is spawned to take control of the test execution, and the effect is that
+ <c>ct:run_test/1</c> returns the pid of this process rather than the
+ test result - which instead is printed to tty at the end of the test run.</p>
+ <note><p>Note that in order to use the
+ <c><seealso marker="ct#break-1">ct:break/1/2</seealso></c> and
+ <c><seealso marker="ct#continue-0">ct:continue/0/1</seealso></c> functions,
+ <c>release_shell</c> <em>must</em> be set to <c>true</c>.</p></note>
+ </section>
+
+ <p>For detailed documentation about
+ <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>,
+ please see the
+ <c><seealso marker="ct#run_test-1">ct</seealso></c> manual page.</p>
</section>
<section>
@@ -266,9 +281,9 @@
for trying out various operations during test suite development.</p>
<p>To invoke the interactive shell mode, you can start an Erlang shell
- manually and call <c>ct:install/1</c> to install any configuration
+ manually and call <c><seealso marker="ct#install-1">ct:install/1</seealso></c> to install any configuration
data you might need (use <c>[]</c> as argument otherwise), then
- call <c>ct:start_interactive/0</c> to start Common Test. If you use
+ call <c><seealso marker="ct#start_interactive-0">ct:start_interactive/0</seealso></c> to start Common Test. If you use
the <c>ct_run</c> program, you may start the Erlang shell and Common Test
in the same go by using the <c>-shell</c> and, optionally, the <c>-config</c>
and/or <c>-userconfig</c> flag. Examples:
@@ -287,7 +302,8 @@
<p>If any functions using "required config data" (e.g. ct_telnet or
ct_ftp functions) are to be called from the erlang shell, config
- data must first be required with <c>ct:require/[1,2]</c>. This is
+ data must first be required with <c><seealso marker="ct#require-1">
+ ct:require/1/2</seealso></c>. This is
equivalent to a <c>require</c> statement in the <seealso
marker="write_test_chapter#suite">Test Suite Info
Function</seealso> or in the <seealso
@@ -314,11 +330,11 @@
is not supported.</p>
<p>If you wish to exit the interactive mode (e.g. to start an
- automated test run with <c>ct:run_test/1</c>), call the function
- <c>ct:stop_interactive/0</c>. This shuts down the
+ automated test run with <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>), call the function
+ <c><seealso marker="ct#stop_interactive-0">ct:stop_interactive/0</seealso></c>. This shuts down the
running <c>ct</c> application. Associations between
configuration names and data created with <c>require</c> are
- consequently deleted. <c>ct:start_interactive/0</c> will get you
+ consequently deleted. <c><seealso marker="ct#start_interactive-0">ct:start_interactive/0</seealso></c> will get you
back into interactive mode, but the previous state is not restored.</p>
</section>
@@ -326,7 +342,7 @@
<title>Step by step execution of test cases with the Erlang Debugger</title>
<p>By means of <c>ct_run -step [opts]</c>, or by passing the
- <c>{step,Opts}</c> option to <c>ct:run_test/1</c>, it is possible
+ <c>{step,Opts}</c> option to <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>, it is possible
to get the Erlang Debugger started automatically and use its
graphical interface to investigate the state of the current test
case and to execute it step by step and/or set execution breakpoints.</p>
@@ -352,31 +368,34 @@
<marker id="test_specifications"></marker>
<section>
- <title>Using test specifications</title>
+ <title>Test Specifications</title>
<p>The most flexible way to specify what to test, is to use a so
called test specification. A test specification is a sequence of
- Erlang terms. The terms may be declared in a text file or passed
- to the test server at runtime as a list
- (see <c>run_testspec/1</c> in the manual page
- for <c>ct</c>). There are two general types of terms:
- configuration terms and test specification terms.</p>
+ Erlang terms. The terms are normally declared in a text file (see
+ <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c>), but
+ may also be passed to Common Test on the form of a list (see
+ <c><seealso marker="ct#run_testspec-1">ct:run_testspec/1</seealso></c>).
+ There are two general types of terms: configuration terms and test
+ specification terms.</p>
<p>With configuration terms it is possible to e.g. label the test
run (similar to <c>ct_run -label</c>), evaluate arbitrary expressions
- before starting a test, import configuration
- data (similar to
- <c>ct_run -config/-userconfig</c>), specify HTML log directories (similar
- to
- <c>ct_run -logdir</c>), give aliases to test nodes and test
- directories (to make a specification easier to read and
- maintain), enable code coverage analysis (see
- the <seealso marker="cover_chapter#cover">Code Coverage
- Analysis</seealso> chapter) and specify event_handler plugins
- (see the <seealso marker="event_handler_chapter#event_handling">
- Event Handling</seealso> chapter). There is also a term for
- specifying include directories that should be passed on to the
- compiler when automatic compilation is performed (similar
- to <c>ct_run -include</c>, see above).</p>
+ before starting the test, import configuration data (similar to
+ <c>ct_run -config/-userconfig</c>), specify the top level HTML log
+ directory (similar to <c>ct_run -logdir</c>), enable code coverage
+ analysis (similar to <c>ct_run -cover</c>), install Common Test Hooks
+ (similar to <c>ct_run -ch_hooks</c>), install event_handler plugins
+ (similar to <c>ct_run -event_handler</c>), specify include directories
+ that should be passed to the compiler for automatic compilation
+ (similar to <c>ct_run -include</c>), disable the auto compilation
+ feature (similar to <c>ct_run -no_auto_compile</c>), set verbosity
+ levels (similar to <c>ct_run -verbosity</c>), and more.</p>
+ <p>Configuration terms can be combined with <c>ct_run</c> start flags,
+ or <c>ct:run_test/1</c> options. The result will for some flags/options
+ and terms be that the values are merged (e.g. configuration files,
+ include directories, verbosity levels, silent connections), and for
+ others that the start flags/options override the test specification
+ terms (e.g. log directory, label, style sheet, auto compilation).</p>
<p>With test specification terms it is possible to state exactly
which tests should run and in which order. A test term specifies
either one or more suites, one or more test case groups, or one
@@ -391,11 +410,12 @@
S, is a test of all cases in S. However, if a term specifying
test case X and Y in S is merged with a term specifying case Z
in S, the result is a test of X, Y and Z in S. To disable this
- behaviour, it is possible in test specification to set the
- <c>merge_tests</c> term to <c>false</c>.</p>
+ behaviour, i.e. to instead perform each test sequentially in a "script-like"
+ manner, the term <c>merge_tests</c> can be set to <c>false</c> in
+ the test specification.</p>
<p>A test term can also specify one or more test suites, groups,
or test cases to be skipped. Skipped suites, groups and cases
- are not executed and show up in the HTML test log files as
+ are not executed and show up in the HTML log files as
SKIPPED.</p>
<p>When a test case group is specified, the resulting test
executes the
@@ -425,15 +445,30 @@
<p>Below is the test specification syntax. Test specifications can
be used to run tests both in a single test host environment and
in a distributed Common Test environment (Large Scale
- Testing). The node parameters in the init term are only
+ Testing). The node parameters in the <c>init</c> term are only
relevant in the latter (see the
<seealso marker="ct_master_chapter#test_specifications">Large
- Scale Testing</seealso> chapter for information). For details on
- the event_handler term, see the
+ Scale Testing</seealso> chapter for information). For more information
+ about the various terms, please see the corresponding sections in the
+ User's Guide, such as e.g. the
+ <seealso marker="run_test_chapter#ct_run"><c>ct_run</c>
+ program</seealso> for an overview of available start flags
+ (since most flags have a corresponding configuration term), and
+ more detailed explanation of e.g.
+ <seealso marker="write_test_chapter#logging">Logging</seealso>
+ (for the <c>verbosity</c>, <c>stylesheet</c> and <c>basic_html</c> terms),
+ <seealso marker="config_file_chapter#top">External Configuration Data</seealso>
+ (for the <c>config</c> and <c>userconfig</c> terms),
<seealso marker="event_handler_chapter#event_handling">Event
- Handling</seealso> chapter.</p>
+ Handling</seealso> (for the <c>event_handler</c> term),
+ <seealso marker="ct_hooks_chapter#installing">Common Test Hooks</seealso>
+ (for the <c>ct_hooks</c> term), etc.</p>
<p>Config terms:</p>
<pre>
+ {merge_tests, Bool}.
+
+ {define, Constant, Value}.
+
{node, NodeAlias, Node}.
{init, InitOptions}.
@@ -442,6 +477,15 @@
{label, Label}.
{label, NodeRefs, Label}.
+ {verbosity, VerbosityLevels}.
+ {verbosity, NodeRefs, VerbosityLevels}.
+
+ {stylesheet, CSSFile}.
+ {stylesheet, NodeRefs, CSSFile}.
+
+ {silent_connections, ConnTypes}.
+ {silent_connections, NodeRefs, ConnTypes}.
+
{multiply_timetraps, N}.
{multiply_timetraps, NodeRefs, N}.
@@ -454,19 +498,23 @@
{include, IncludeDirs}.
{include, NodeRefs, IncludeDirs}.
+ {auto_compile, Bool},
+ {auto_compile, NodeRefs, Bool},
+
{config, ConfigFiles}.
+ {config, ConfigDir, ConfigBaseNames}.
{config, NodeRefs, ConfigFiles}.
+ {config, NodeRefs, ConfigDir, ConfigBaseNames}.
{userconfig, {CallbackModule, ConfigStrings}}.
{userconfig, NodeRefs, {CallbackModule, ConfigStrings}}.
- {alias, DirAlias, Dir}.
-
- {merge_tests, Bool}.
-
{logdir, LogDir}.
{logdir, NodeRefs, LogDir}.
+ {logopts, LogOpts}.
+ {logopts, NodeRefs, LogOpts}.
+
{create_priv_dir, PrivDirOption}.
{create_priv_dir, NodeRefs, PrivDirOption}.
@@ -479,83 +527,176 @@
{ct_hooks, NodeRefs, CTHModules}.
{enable_builtin_hooks, Bool}.
- </pre>
+
+ {basic_html, Bool}.
+ {basic_html, NodeRefs, Bool}.
+
+ {release_shell, Bool}.</pre>
+
<p>Test terms:</p>
<pre>
- {suites, DirRef, Suites}.
- {suites, NodeRefs, DirRef, Suites}.
+ {suites, Dir, Suites}.
+ {suites, NodeRefs, Dir, Suites}.
- {groups, DirRef, Suite, Groups}.
- {groups, NodeRefsDirRef, Suite, Groups}.
+ {groups, Dir, Suite, Groups}.
+ {groups, NodeRefs, Dir, Suite, Groups}.
- {groups, DirRef, Suite, GroupSpec, {cases,Cases}}.
- {groups, NodeRefsDirRef, Suite, GroupSpec, {cases,Cases}}.
+ {groups, Dir, Suite, GroupSpec, {cases,Cases}}.
+ {groups, NodeRefs, Dir, Suite, GroupSpec, {cases,Cases}}.
- {cases, DirRef, Suite, Cases}.
- {cases, NodeRefs, DirRef, Suite, Cases}.
+ {cases, Dir, Suite, Cases}.
+ {cases, NodeRefs, Dir, Suite, Cases}.
- {skip_suites, DirRef, Suites, Comment}.
- {skip_suites, NodeRefs, DirRef, Suites, Comment}.
+ {skip_suites, Dir, Suites, Comment}.
+ {skip_suites, NodeRefs, Dir, Suites, Comment}.
- {skip_groups, DirRef, Suite, GroupNames, Comment}.
- {skip_groups, NodeRefs, DirRef, Suite, GroupNames, Comment}.
+ {skip_groups, Dir, Suite, GroupNames, Comment}.
+ {skip_groups, NodeRefs, Dir, Suite, GroupNames, Comment}.
- {skip_cases, DirRef, Suite, Cases, Comment}.
- {skip_cases, NodeRefs, DirRef, Suite, Cases, Comment}.
- </pre>
+ {skip_cases, Dir, Suite, Cases, Comment}.
+ {skip_cases, NodeRefs, Dir, Suite, Cases, Comment}.</pre>
+
<p>Types:</p>
<pre>
- NodeAlias = atom()
- InitOptions = term()
- Node = node()
- NodeRef = NodeAlias | Node | master
- NodeRefs = all_nodes | [NodeRef] | NodeRef
- N = integer()
- Bool = true | false
- CoverSpecFile = string()
- IncludeDirs = string() | [string()]
- ConfigFiles = string() | [string()]
- DirAlias = atom()
- Dir = string()
- LogDir = string()
- PrivDirOption = auto_per_run | auto_per_tc | manual_per_tc
- EventHandlers = atom() | [atom()]
- InitArgs = [term()]
- CTHModules = [CTHModule | {CTHModule, CTHInitArgs} | {CTHModule, CTHInitArgs, CTHPriority}]
- CTHModule = atom()
- CTHInitArgs = term()
- DirRef = DirAlias | Dir
- Suites = atom() | [atom()] | all
- Suite = atom()
- Groups = GroupSpec | [GroupSpec] | all
- GroupSpec = GroupName | {GroupName,Properties} | {GroupName,Properties,GroupSpec}
- GroupName = atom()
- GroupNames = GroupName | [GroupName]
- Cases = atom() | [atom()] | all
- Comment = string() | ""
- </pre>
- <p>Example:</p>
+ Bool = true | false
+ Constant = atom()
+ Value = term()
+ NodeAlias = atom()
+ Node = node()
+ NodeRef = NodeAlias | Node | master
+ NodeRefs = all_nodes | [NodeRef] | NodeRef
+ InitOptions = term()
+ Label = atom() | string()
+ VerbosityLevels = integer() | [{Category,integer()}]
+ Category = atom()
+ CSSFile = string()
+ ConnTypes = all | [atom()]
+ N = integer()
+ CoverSpecFile = string()
+ IncludeDirs = string() | [string()]
+ ConfigFiles = string() | [string()]
+ ConfigDir = string()
+ ConfigBaseNames = string() | [string()]
+ CallbackModule = atom()
+ ConfigStrings = string() | [string()]
+ LogDir = string()
+ LogOpts = [term()]
+ PrivDirOption = auto_per_run | auto_per_tc | manual_per_tc
+ EventHandlers = atom() | [atom()]
+ InitArgs = [term()]
+ CTHModules = [CTHModule | {CTHModule, CTHInitArgs} | {CTHModule, CTHInitArgs, CTHPriority}]
+ CTHModule = atom()
+ CTHInitArgs = term()
+ Dir = string()
+ Suites = atom() | [atom()] | all
+ Suite = atom()
+ Groups = GroupSpec | [GroupSpec] | all
+ GroupSpec = GroupName | {GroupName,Properties} | {GroupName,Properties,GroupSpec}
+ GroupName = atom()
+ GroupNames = GroupName | [GroupName]
+ Cases = atom() | [atom()] | all
+ Comment = string() | ""</pre>
+
+ <p>The difference between the <c>config</c> terms above, is that with
+ <c>ConfigDir</c>, <c>ConfigBaseNames</c> is a list of base names,
+ i.e. without directory paths. <c>ConfigFiles</c> must be full names,
+ including paths. E.g, these two terms have the same meaning:</p>
<pre>
- {logdir, "/home/test/logs"}.
+ {config, ["/home/testuser/tests/config/nodeA.cfg",
+ "/home/testuser/tests/config/nodeB.cfg"]}.
+
+ {config, "/home/testuser/tests/config", ["nodeA.cfg","nodeB.cfg"]}.</pre>
+
+ <note><p>Any relative paths specified in the test specification, will be
+ relative to the directory which contains the test specification file, if
+ <c>ct_run -spec TestSpecFile ...</c> or
+ <c>ct:run:test([{spec,TestSpecFile},...])</c>
+ executes the test. The path will be relative to the top level log directory, if
+ <c>ct:run:testspec(TestSpec)</c> executes the test.</p></note>
- {config, "/home/test/t1/cfg/config.cfg"}.
- {config, "/home/test/t2/cfg/config.cfg"}.
- {config, "/home/test/t3/cfg/config.cfg"}.
+ <p>The <c>define</c> term introduces a constant, which is used to
+ replace the name <c>Constant</c> with <c>Value</c>, wherever it's found in
+ the test specification. This replacement happens during an initial iteration
+ through the test specification. Constants may be used anywhere in the test
+ specification, e.g. in arbitrary lists and tuples, and even in strings
+ and inside the value part of other constant definitions! A constant can
+ also be part of a node name, but that is the only place where a constant
+ can be part of an atom.</p>
+
+ <note><p>For the sake of readability, the name of the constant must always
+ begin with an upper case letter, or a <c>$</c>, <c>?</c>, or <c>_</c>.
+ This also means that it must always be single quoted (obviously, since
+ the constant name is actually an atom, not text).</p></note>
+
+ <p>The main benefit of constants is that they can be used to reduce the size
+ (and avoid repetition) of long strings, such as file paths. Compare these
+ terms:</p>
+
+ <pre>
+ %% 1a. no constant
+ {config, "/home/testuser/tests/config", ["nodeA.cfg","nodeB.cfg"]}.
+ {suites, "/home/testuser/tests/suites", all}.
- {alias, t1, "/home/test/t1"}.
- {alias, t2, "/home/test/t2"}.
- {alias, t3, "/home/test/t3"}.
+ %% 1b. with constant
+ {define, 'TESTDIR', "/home/testuser/tests"}.
+ {config, "'TESTDIR'/config", ["nodeA.cfg","nodeB.cfg"]}.
+ {suites, "'TESTDIR'/suites", all}.
+
+ %% 2a. no constants
+ {config, [testnode@host1, testnode@host2], "../config", ["nodeA.cfg","nodeB.cfg"]}.
+ {suites, [testnode@host1, testnode@host2], "../suites", [x_SUITE, y_SUITE]}.
+
+ %% 2b. with constants
+ {define, 'NODE', testnode}.
+ {define, 'NODES', ['NODE'@host1, 'NODE'@host2]}.
+ {config, 'NODES', "../config", ["nodeA.cfg","nodeB.cfg"]}.
+ {suites, 'NODES', "../suites", [x_SUITE, y_SUITE]}.</pre>
+
+ <p>Constants make the test specification term <c>alias</c>, in previous
+ versions of Common Test, redundant. This term has been deprecated but will
+ remain supported in upcoming Common Test releases. Replacing <c>alias</c>
+ terms with <c>define</c> is strongly recommended though! Here's an example
+ of such a replacement:</p>
+
+ <pre>
+ %% using the old alias term
+ {config, "/home/testuser/tests/config/nodeA.cfg"}.
+ {alias, suite_dir, "/home/testuser/tests/suites"}.
+ {groups, suite_dir, x_SUITE, group1}.
+
+ %% replacing with constants
+ {define, 'TestDir', "/home/testuser/tests"}.
+ {define, 'CfgDir', "'TestDir'/config"}.
+ {define, 'SuiteDir', "'TestDir'/suites"}.
+ {config, 'CfgDir', "nodeA.cfg"}.
+ {groups, 'SuiteDir', x_SUITE, group1}.</pre>
+
+ <p>Actually, constants could well replace the <c>node</c> term too, but
+ this still has declarative value, mainly when used in combination
+ with <c>NodeRefs == all_nodes</c> (see types above).</p>
+
+ <p>Here follows a simple test specification example:</p>
+ <pre>
+ {define, 'Top', "/home/test"}.
+ {define, 'T1', "'Top'/t1"}.
+ {define, 'T2', "'Top'/t2"}.
+ {define, 'T3', "'Top'/t3"}.
+ {define, 'CfgFile', "config.cfg"}.
+
+ {logdir, "'Top'/logs"}.
- {suites, t1, all}.
- {skip_suites, t1, [t1B_SUITE,t1D_SUITE], "Not implemented"}.
- {skip_cases, t1, t1A_SUITE, [test3,test4], "Irrelevant"}.
- {skip_cases, t1, t1C_SUITE, [test1], "Ignore"}.
+ {config, ["'T1'/'CfgFile'", "'T2'/'CfgFile'", "'T3'/'CfgFile'"]}.
- {suites, t2, [t2B_SUITE,t2C_SUITE]}.
- {cases, t2, t2A_SUITE, [test4,test1,test7]}.
+ {suites, 'T1', all}.
+ {skip_suites, 'T1', [t1B_SUITE,t1D_SUITE], "Not implemented"}.
+ {skip_cases, 'T1', t1A_SUITE, [test3,test4], "Irrelevant"}.
+ {skip_cases, 'T1', t1C_SUITE, [test1], "Ignore"}.
- {skip_suites, t3, all, "Not implemented"}.
- </pre>
+ {suites, 'T2', [t2B_SUITE,t2C_SUITE]}.
+ {cases, 'T2', t2A_SUITE, [test4,test1,test7]}.
+
+ {skip_suites, 'T3', all, "Not implemented"}.</pre>
+
<p>The example specifies the following:</p>
<list>
<item>The specified logdir directory will be used for storing
@@ -563,8 +704,6 @@
date and time).</item>
<item>The variables in the specified test system config files will be
imported for the test.</item>
- <item>Aliases are given for three test system directories. The suites in
- this example are stored in "/home/test/tX/test".</item>
<item>The first test to run includes all suites for system t1. Excluded from
the test are however the t1B and t1D suites. Also test cases test3 and
test4 in t1A as well as the test1 case in t1C are excluded from
@@ -575,9 +714,9 @@
<item>Lastly, all suites for systems t3 are to be completely skipped and this
should be explicitly noted in the log files.</item>
</list>
- <p>It is possible to specify initialization options for nodes defined in the
- test specification. Currently, there are options to start the node and/or to
- evaluate any function on the node.
+ <p>With the <c>init</c> term it's possible to specify initialization options
+ for nodes defined in the test specification. Currently, there are options
+ to start the node and/or to evaluate any function on the node.
See the <seealso marker="ct_master_chapter#ct_slave">Automatic startup of
the test target nodes</seealso> chapter for details.</p>
<p>It is possible for the user to provide a test specification that
@@ -586,11 +725,49 @@
<c>ct_run</c>. This forces Common Test to ignore unrecognizable terms.
Note that in this mode, Common Test is not able to check the specification
for errors as efficiently as if the scanner runs in default mode.
- If <c>ct:run_test/1</c> is used for starting the tests, the relaxed scanner
+ If <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c> is used for starting the tests, the relaxed scanner
mode is enabled by means of the tuple: <c>{allow_user_terms,true}</c></p>
</section>
+
+ <section>
+ <title>Running tests from the Web based GUI</title>
+
+ <p>The web based GUI, VTS, is started with the
+ <c><seealso marker="run_test_chapter#ct_run">ct_run</seealso></c>
+ program. From the GUI you can load config files, and select
+ directories, suites and cases to run. You can also state the
+ config files, directories, suites and cases on the command line
+ when starting the web based GUI.
+ </p>
+
+ <list>
+ <item><c>ct_run -vts</c></item>
+ <item><c><![CDATA[ct_run -vts -config <configfilename>]]></c></item>
+ <item><c><![CDATA[ct_run -vts -config <configfilename> -suite <suitewithfullpath>
+ -case <casename>]]></c></item>
+ </list>
+
+ <p>From the GUI you can run tests and view the result and the logs.
+ </p>
+
+ <p>Note that <c>ct_run -vts</c> will try to open the Common Test start
+ page in an existing web browser window or start the browser if it is
+ not running. Which browser should be started may be specified with
+ the browser start command option:</p>
+ <p><c><![CDATA[ct_run -vts -browser <browser_start_cmd>]]></c></p>
+ <p>Example:</p>
+ <p><c><![CDATA[$ ct_run -vts -browser 'firefox&']]></c></p>
+ <p>Note that the browser must run as a separate OS process or VTS will hang!</p>
+ <p>If no specific browser start command is specified, Firefox will
+ be the default browser on Unix platforms and Internet Explorer on Windows.
+ If Common Test fails to start a browser automatically, or <c>'none'</c> is
+ specified as the value for -browser (i.e. <c>-browser none</c>), start your
+ favourite browser manually and type in the URL that Common Test
+ displays in the shell.</p>
+ </section>
<section>
+ <marker id="log_files"></marker>
<title>Log files</title>
<p>As the execution of the test suites proceed, events are logged in
@@ -718,17 +895,30 @@
<p>instead of each <c>x</c> printed on a new line, which is the default behaviour.</p>
</section>
+ <section>
+ <marker id="table_sorting"></marker>
+ <title>Sorting HTML table columns</title>
+ <p>By clicking the name in the column header of any table (e.g. "Ok", "Case", "Time", etc),
+ the table rows are sorted in whatever order makes sense for the type of value (e.g.
+ numerical for "Ok" or "Time", and alphabetical for "Case"). The sorting is performed
+ by means of JavaScript code, automatically inserted into the HTML log files. Common Test
+ uses the <url href="http://jquery.com">jQuery</url> library and the
+ <url href="http://tablesorter.com">tablesorter</url> plugin, with customized sorting
+ functions, for this implementation.</p>
+ </section>
</section>
<section>
<marker id="html_stylesheet"></marker>
<title>HTML Style Sheets</title>
- <p>Common Test uses a CSS file to control the look of the HTML
- files generated during test runs. If, for some reason, the
- log files are not displayed correctly in the HTML browser of your
- choice, or you prefer the "pre Common Test v1.6 look"
- of the log files (i.e. not using CSS), use the start flag/option
- <c>basic_html</c> to revert to the old style.</p>
+ <p>Common Test uses an HTML Style Sheet (CSS file) to control the look of
+ the HTML log files generated during test runs. If, for some reason, the
+ log files are not displayed correctly in the browser of your
+ choice, or you prefer a more primitive ("pre Common Test v1.6") look
+ of the logs, use the start flag/option:</p>
+ <pre>basic_html</pre>
+ <p>This disables the use of Style Sheets, as well as JavaScripts (see
+ table sorting above).</p>
<p>Common Test includes an <em>optional</em> feature to allow
user HTML style sheets for customizing printouts. The
@@ -881,75 +1071,82 @@
<section>
<marker id="silent_connections"></marker>
<title>Silent Connections</title>
- <p>The protocol handling processes in Common Test, implemented by ct_telnet, ct_ftp etc,
- do verbose printing to the test case logs. This can be switched off by means
- of the <c>-silent_connections</c> flag:</p>
+ <p>The protocol handling processes in Common Test, implemented by ct_telnet,
+ ct_ssh, ct_ftp etc, do verbose printing to the test case logs. This can be switched off
+ by means of the <c>-silent_connections</c> flag:</p>
<pre>
ct_run -silent_connections [conn_types]
</pre>
- <p>where <c>conn_types</c> specifies <c>telnet, ftp, rpc</c> and/or <c>snmp</c>.</p>
+ <p>where <c>conn_types</c> specifies <c>ssh, telnet, ftp, rpc</c> and/or <c>snmp</c>.</p>
<p>Example:</p>
<pre>
- ct_run ... -silent_connections telnet ftp</pre>
- <p>switches off logging for telnet and ftp connections.</p>
+ ct_run ... -silent_connections ssh telnet</pre>
+ <p>switches off logging for ssh and telnet connections.</p>
<pre>
ct_run ... -silent_connections</pre>
<p>switches off logging for all connection types.</p>
- <p>Basic and important information such as opening and closing a connection,
- fatal communication error and reconnection attempts will always be printed even
- if logging has been suppressed for the connection type in question. However, operations
- such as sending and receiving data may be performed silently.</p>
+ <p>Fatal communication error and reconnection attempts will always be printed even
+ if logging has been suppressed for the connection type in question. However, operations
+ such as sending and receiving data will be performed silently.</p>
<p>It is possible to also specify <c>silent_connections</c> in a test suite. This is
accomplished by returning a tuple, <c>{silent_connections,ConnTypes}</c>, in the
<c>suite/0</c> or test case info list. If <c>ConnTypes</c> is a list of atoms
- (<c>telnet, ftp, rpc</c> and/or <c>snmp</c>), output for any corresponding connections
+ (<c>ssh, telnet, ftp, rpc</c> and/or <c>snmp</c>), output for any corresponding connections
will be suppressed. Full logging is per default enabled for any connection of type not
specified in <c>ConnTypes</c>. Hence, if <c>ConnTypes</c> is the empty list, logging
is enabled for all connections.</p>
- <p>The <c>silent_connections</c> setting returned from a test case info function overrides,
- for the test case in question, any setting made with <c>suite/0</c> (which is the setting
- used for all cases in the suite). Example:</p>
+ <p>Example:</p>
<pre>
-module(my_SUITE).
+
+ suite() -> [..., {silent_connections,[telnet,ssh]}, ...].
+
...
- suite() -> [..., {silent_connections,[telnet,ftp]}, ...].
- ...
+
my_testcase1() ->
- [{silent_connections,[ftp]}].
+ [{silent_connections,[ssh]}].
+
my_testcase1(_) ->
- ...
+ ...
+
my_testcase2(_) ->
- ...
+ ...
</pre>
<p>In this example, <c>suite/0</c> tells Common Test to suppress
- printouts from telnet and ftp connections. This is valid for
+ printouts from telnet and ssh connections. This is valid for
all test cases. However, <c>my_testcase1/0</c> specifies that
- for this test case, only ftp should be silent. The result is
+ for this test case, only ssh should be silent. The result is
that <c>my_testcase1</c> will get telnet info (if any) printed
- in the log, but not ftp info. <c>my_testcase2</c> will get no
+ in the log, but not ssh info. <c>my_testcase2</c> will get no
info from either connection printed.</p>
- <p>The <c>-silent_connections</c> tag (or
- <c>silent_connections</c> tagged tuple in the call to
- <c>ct:run_test/1</c>) overrides any settings in the test
- suite.</p>
+ <p><c>silent_connections</c> may also be specified with a term
+ in a test specification
+ (see <seealso marker="run_test_chapter#test_specifications">Test
+ Specifications</seealso>). Connections provided with the
+ <c>silent_connections</c> start flag/option, will be merged with
+ any connections listed in the test specification.</p>
+
+ <p>The <c>silent_connections</c> start flag/option and test
+ specification term, overrides any settings made by the info functions
+ inside the test suite.</p>
- <p>Note that in the current Common Test version, the
+ <note><p>Note that in the current Common Test version, the
<c>silent_connections</c> feature only works for telnet
- connections. Support for other connection types will be added
- in future Common Test versions.</p>
+ and ssh connections! Support for other connection types will be added
+ in future Common Test versions.</p></note>
</section>
</chapter>
diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml
index 7b7e7af8ea..248d7de8b6 100644
--- a/lib/common_test/doc/src/write_test_chapter.xml
+++ b/lib/common_test/doc/src/write_test_chapter.xml
@@ -47,7 +47,7 @@
module for details about these functions.</p>
<p>The CT application also includes other modules named
- <c><![CDATA[ct_<something>]]></c> that
+ <c><![CDATA[ct_<component>]]></c> that
provide various support, mainly simplified use of communication
protocols such as rpc, snmp, ftp, telnet, etc.</p>
@@ -173,7 +173,7 @@
</p>
<p>The <c>end_per_testcase/2</c> function is called even after a
- test case terminates due to a call to <c>ct:abort_current_testcase/1</c>,
+ test case terminates due to a call to <c><seealso marker="ct#abort_current_testcase-1">ct:abort_current_testcase/1</seealso></c>,
or after a timetrap timeout. However, <c>end_per_testcase</c>
will then execute on a different process than the test case
function, and in this situation, <c>end_per_testcase</c> will
@@ -243,7 +243,8 @@
<note><p>The test case function argument <c>Config</c> should not be
confused with the information that can be retrieved from
- configuration files (using ct:get_config/[1,2]). The Config argument
+ configuration files (using <c><seealso marker="ct#get_config-1">
+ ct:get_config/1/2</seealso></c>). The Config argument
should be used for runtime configuration of the test suite and the
test cases, while configuration files should typically contain data
related to the SUT. These two types of configuration data are handled
@@ -302,7 +303,7 @@
<item>
<p>
Use this to specify arbitrary data related to the testcase. This
- data can be retrieved at any time using the <c>ct:userdata/3</c>
+ data can be retrieved at any time using the <c><seealso marker="ct#userdata-3">ct:userdata/3</seealso></c>
utility function.
</p>
</item>
@@ -338,7 +339,8 @@
<pre>
testcase2() ->
- [{require, unix_telnet, {unix, [telnet, username, password]}},
+ [{require, unix_telnet, unix},
+ {require, {unix, [telnet, username, password]}},
{default_config, unix, [{telnet, "my_telnet_host"},
{username, "aladdin"},
{password, "sesame"}]}}].</pre>
@@ -346,7 +348,8 @@
</taglist>
<p>See the <seealso marker="config_file_chapter#require_config_data">Config files</seealso>
- chapter and the <c>ct:require/[1,2]</c> function in the
+ chapter and the <c><seealso marker="ct#require-1">
+ ct:require/1/2</seealso></c> function in the
<seealso marker="ct">ct</seealso> reference manual for more information about
<c>require</c>.</p>
@@ -823,7 +826,7 @@
Common Test to create one dedicated private directory per
test case and execution instead. This is accomplished by means of
the flag/option: <c>create_priv_dir</c> (to be used with the
- <c>ct_run</c> program, the <c>ct:run_test/1</c> function, or
+ <c>ct_run</c> program, the <c><seealso marker="ct#run_test-1">ct:run_test/1</seealso></c> function, or
as test specification term). There are three possible values
for this option:
<list>
@@ -839,7 +842,7 @@
become very inefficient for test runs with many test cases and/or
repetitions. Therefore, in case the manual version is instead used, the
test case must tell Common Test to create priv_dir when it needs it.
- It does this by calling the function <c>ct:make_priv_dir/0</c>.
+ It does this by calling the function <c><seealso marker="ct#make_priv_dir-0">ct:make_priv_dir/0</seealso></c>.
</p>
<note><p>You should not depend on current working directory for
@@ -883,11 +886,11 @@
of its sub-groups. If a timetrap value is defined by <c>group/1</c>
for a sub-group, it overrides that of its higher level groups. Timetrap
values set by individual test cases (by means of the test case info
- function) overrides both group- and suite- level timetraps.</p>
+ function) override both group- and suite- level timetraps.</p>
<p>It is also possible to dynamically set/reset a timetrap during the
excution of a test case, or configuration function. This is done by calling
- <c>ct:timetrap/1</c>. This function cancels the current timetrap
+ <c><seealso marker="ct#timetrap-1">ct:timetrap/1</seealso></c>. This function cancels the current timetrap
and starts a new one (that stays active until timeout, or end of the
current function).</p>
@@ -900,12 +903,12 @@
<p>If a test case needs to suspend itself for a time that also gets
multipled by <c>multiply_timetraps</c> (and possibly also scaled up if
- <c>scale_timetraps</c> is enabled), the function <c>ct:sleep/1</c>
+ <c>scale_timetraps</c> is enabled), the function <c><seealso marker="ct#sleep-1">ct:sleep/1</seealso></c>
may be used (instead of e.g. <c>timer:sleep/1</c>).</p>
<p>A function (<c>fun/0</c> or <c>MFA</c>) may be specified as
timetrap value in the suite-, group- and test case info function, as
- well as argument to the <c>ct:timetrap/1</c> function. Examples:</p>
+ well as argument to the <c><seealso marker="ct#timetrap-1">ct:timetrap/1</seealso></c> function. Examples:</p>
<p><c>{timetrap,{my_test_utils,timetrap,[?MODULE,system_start]}}</c></p>
<p><c>ct:timetrap(fun() -> my_timetrap(TestCaseName, Config) end)</c></p>
@@ -932,6 +935,99 @@
</section>
<section>
+ <marker id="logging"></marker>
+ <title>Logging - categories and verbosity levels</title>
+ <p>Common Test provides three main functions for printing strings:</p>
+ <list>
+ <item><c>ct:log(Category, Importance, Format, Args)</c></item>
+ <item><c>ct:print(Category, Importance, Format, Args)</c></item>
+ <item><c>ct:pal(Category, Importance, Format, Args)</c></item>
+ </list>
+ <p>The <c>log/1/2/3/4</c> function will print a string to the test case
+ log file. The <c>print/1/2/3/4</c> function will print the string to screen,
+ and the <c>pal/1/2/3/4</c> function will print the same string both to file and
+ screen. (The functions are documented in the <c>ct</c> reference manual).</p>
+
+ <p>The optional <c>Category</c> argument may be used to categorize the
+ log printout, and categories can be used for two things:</p>
+ <list>
+ <item>To compare the importance of the printout to a specific
+ verbosity level, and</item>
+ <item>to format the printout according to a user specific HTML
+ Style Sheet (CSS).</item>
+ </list>
+
+ <p>The <c>Importance</c> argument specifies a level of importance
+ which, compared to a verbosity level (general and/or set per category),
+ determines if the printout should be visible or not. <c>Importance</c>
+ is an arbitrary integer in the range 0..99. Pre-defined constants
+ exist in the <c>ct.hrl</c> header file. The default importance level,
+ <c>?STD_IMPORTANCE</c> (used if the <c>Importance</c> argument is not
+ provided), is 50. This is also the importance used for standard IO, e.g.
+ from printouts made with <c>io:format/2</c>, <c>io:put_chars/1</c>, etc.</p>
+
+ <p><c>Importance</c> is compared to a verbosity level set by means of the
+ <c>verbosity</c> start flag/option. The verbosity level can be set per
+ category and/or generally. The default verbosity level, <c>?STD_VERBOSITY</c>,
+ is 50, i.e. all standard IO gets printed. If a lower verbosity level is set,
+ standard IO printouts will be ignored. Common Test performs the following test:</p>
+ <pre>Importance >= (100-VerbosityLevel)</pre>
+ <p>This also means that verbosity level 0 effectively turns all logging off
+ (with the exception of printouts made by Common Test itself).</p>
+
+ <p>The general verbosity level is not associated with any particular
+ category. This level sets the threshold for the standard IO printouts,
+ uncategorized <c>ct:log/print/pal</c> printouts, as well as
+ printouts for categories with undefined verbosity level.</p>
+
+ <p>Example:</p>
+ <pre>
+
+ Some printouts during test case execution:
+
+ io:format("1. Standard IO, importance = ~w~n", [?STD_IMPORTANCE]),
+ ct:log("2. Uncategorized, importance = ~w", [?STD_IMPORTANCE]),
+ ct:log(info, "3. Categorized info, importance = ~w", [?STD_IMPORTANCE]]),
+ ct:log(info, ?LOW_IMPORTANCE, "4. Categorized info, importance = ~w", [?LOW_IMPORTANCE]),
+ ct:log(error, "5. Categorized error, importance = ~w", [?HI_IMPORTANCE]),
+ ct:log(error, ?HI_IMPORTANCE, "6. Categorized error, importance = ~w", [?MAX_IMPORTANCE]),
+
+ If starting the test without specifying any verbosity levels:
+
+ $ ct_run ...
+
+ the following gets printed:
+
+ 1. Standard IO, importance = 50
+ 2. Uncategorized, importance = 50
+ 3. Categorized info, importance = 50
+ 5. Categorized error, importance = 75
+ 6. Categorized error, importance = 99
+
+ If starting the test with:
+
+ $ ct_run -verbosity 1 and info 75
+
+ the following gets printed:
+
+ 3. Categorized info, importance = 50
+ 4. Categorized info, importance = 25
+ 6. Categorized error, importance = 99
+ </pre>
+
+ <p>How categories can be mapped to CSS tags is documented in the
+ <seealso marker="run_test_chapter#html_stylesheet">Running Tests</seealso>
+ chapter.</p>
+
+ <p>The <c>Format</c> and <c>Args</c> arguments in <c>ct:log/print/pal</c> are
+ always passed on to the <c>io:format/3</c> function in <c>stdlib</c>
+ (please see the <c>io</c> manual page for details).</p>
+
+ <p>For more information about log files, please see the
+ <seealso marker="run_test_chapter#log_files">Running Tests</seealso> chapter.</p>
+ </section>
+
+ <section>
<title>Illegal dependencies</title>
<p>Even though it is highly efficient to write test suites with
@@ -941,7 +1037,6 @@
Erlang/OTP test suites.</p>
<list>
-
<item>Depending on current directory, and writing there:<br></br>
<p>This is a common error in test suites. It is assumed that
@@ -953,19 +1048,10 @@
</p>
</item>
- <item>Depending on the Clearcase (file version control system)
- paths and files:<br></br>
-
- <p>The test suites are stored in Clearcase but are not
- (necessarily) run within this environment. The directory
- structure may vary from test run to test run.
- </p>
- </item>
-
<item>Depending on execution order:<br></br>
- <p>During development of test suites, no assumption should be made
- (preferrably) about the execution order of the test cases or suites.
+ <p>During development of test suites, no assumption should preferrably
+ be made about the execution order of the test cases or suites.
E.g. a test case should not assume that a server it depends on,
has already been started by a previous test case. There are
several reasons for this:
diff --git a/lib/common_test/include/ct.hrl b/lib/common_test/include/ct.hrl
index 5a77108e1a..bde2709ad1 100644
--- a/lib/common_test/include/ct.hrl
+++ b/lib/common_test/include/ct.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. 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
@@ -19,3 +19,16 @@
-include_lib("test_server/include/test_server.hrl").
+%% the log level is used as argument to any CT logging function
+-define(MIN_IMPORTANCE, 0 ).
+-define(LOW_IMPORTANCE, 25).
+-define(STD_IMPORTANCE, 50).
+-define(HI_IMPORTANCE, 75).
+-define(MAX_IMPORTANCE, 99).
+
+%% verbosity thresholds to filter out logging printouts
+-define(MIN_VERBOSITY, 0 ). %% turn logging off
+-define(LOW_VERBOSITY, 25 ).
+-define(STD_VERBOSITY, 50 ).
+-define(HI_VERBOSITY, 75 ).
+-define(MAX_VERBOSITY, 100).
diff --git a/lib/common_test/priv/Makefile.in b/lib/common_test/priv/Makefile.in
index 8be6248e17..4372ab124e 100644
--- a/lib/common_test/priv/Makefile.in
+++ b/lib/common_test/priv/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. 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
@@ -60,6 +60,7 @@ FILES = vts.tool
SCRIPTS =
IMAGES = tile1.jpg
CSS = ct_default.css
+JS = jquery-latest.js jquery.tablesorter.min.js
#
# Rules
@@ -86,11 +87,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk
ifeq ($(XNIX),true)
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/priv"
- $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) "$(RELSYSDIR)/priv"
+ $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(JS) "$(RELSYSDIR)/priv"
else
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/priv"
- $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) "$(RELSYSDIR)/priv"
+ $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(JS) "$(RELSYSDIR)/priv"
endif
release_docs_spec:
@@ -107,6 +108,7 @@ else
FILES = vts.tool
IMAGES = tile1.jpg
CSS = ct_default.css
+JS = jquery-latest.js jquery.tablesorter.min.js
#
# Rules
@@ -126,7 +128,7 @@ include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/priv"
- $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) "$(RELSYSDIR)/priv"
+ $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(JS) "$(RELSYSDIR)/priv"
release_docs_spec:
diff --git a/lib/common_test/priv/ct_default.css b/lib/common_test/priv/ct_default.css
index 8ae6990cd8..1188f8f676 100644
--- a/lib/common_test/priv/ct_default.css
+++ b/lib/common_test/priv/ct_default.css
@@ -136,7 +136,18 @@ th {
}
thead th {
- background: #2C5755; text-align: center;
+ background: #3F3F3F; color: #fff;
+ font-family: arial, sans-serif; font-size: 120%;
+ letter-spacing: -0.5px;
+ font-weight: bold; text-align: center;
+ padding-right: .5em; vertical-align: top;
+ text-decoration: underline;
+}
+
+tfoot td {
+ font-family: arial, sans-serif; font-size: 110%;
+ letter-spacing: -0.5px;
+ font-weight: bold;
}
.odd td {
@@ -167,10 +178,6 @@ th a, td a:active {
color: #85ABD5;
}
-tfoot th, tfoot td {
- background: #3F3F3F; color: #fff;
-}
-
th + td {
padding-left: .5em;
}
diff --git a/lib/common_test/priv/jquery-latest.js b/lib/common_test/priv/jquery-latest.js
new file mode 100644
index 0000000000..ac7e7009dc
--- /dev/null
+++ b/lib/common_test/priv/jquery-latest.js
@@ -0,0 +1,154 @@
+/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
+ */
+(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
+e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
+j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
+"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
+true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
+Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
+(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
+a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
+"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
+function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
+c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
+L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
+"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
+d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
+a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
+!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
+true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
+parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
+s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
+applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
+else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
+a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
+w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
+cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
+i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
+" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
+this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
+e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
+c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
+a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
+function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
+k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
+C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
+null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
+e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
+f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
+if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
+"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
+a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
+isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
+{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
+if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
+e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
+"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
+d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
+!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
+toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
+u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
+function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
+if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
+t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
+g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
+for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
+1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
+relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
+l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
+h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
+CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
+g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
+text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
+setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
+h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
+m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
+"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
+h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
+!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
+h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
+q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
+if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
+(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
+function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
+gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
+c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
+{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
+"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
+d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
+a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
+1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
+a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
+""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
+this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
+u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
+1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
+return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
+""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
+c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
+c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
+function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
+Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
+"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
+a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
+a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
+"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
+serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
+function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
+global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
+e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
+"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
+false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
+false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
+c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
+d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
+g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
+1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
+"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
+if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
+this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
+"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
+animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
+j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
+this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
+"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
+c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
+this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
+this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
+e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
+c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
+function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
+this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
+k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
+f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
+c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
+d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
+f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
+"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
+e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
diff --git a/lib/common_test/priv/jquery.tablesorter.min.js b/lib/common_test/priv/jquery.tablesorter.min.js
new file mode 100644
index 0000000000..b8605df1e7
--- /dev/null
+++ b/lib/common_test/priv/jquery.tablesorter.min.js
@@ -0,0 +1,4 @@
+
+(function($){$.extend({tablesorter:new
+function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",cssChildRow:"expand-child",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,sortLocaleCompare:true,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'/\.|\,/g',onRenderHeader:null,selectorHeaders:'thead th',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}if(table.tBodies.length==0)return;var rows=table.tBodies[0].rows;if(rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,rows,-1,i);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,rows,rowIndex,cellIndex){var l=parsers.length,node=false,nodeValue=false,keepLooking=true;while(nodeValue==''&&keepLooking){rowIndex++;if(rows[rowIndex]){node=getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex);nodeValue=trimAndGetNodeText(table.config,node);if(table.config.debug){log('Checking if value was empty on row:'+rowIndex);}}else{keepLooking=false;}}for(var i=1;i<l;i++){if(parsers[i].is(nodeValue,table,node)){return parsers[i];}}return parsers[0];}function getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex){return rows[rowIndex].cells[cellIndex];}function trimAndGetNodeText(config,node){return $.trim(getElementText(config,node));}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=$(table.tBodies[0].rows[i]),cols=[];if(c.hasClass(table.config.cssChildRow)){cache.row[cache.row.length-1]=cache.row[cache.row.length-1].add(c);continue;}cache.row.push(c);for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c[0].cells[j]),table,c[0].cells[j]));}cols.push(cache.normalized.length);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){var text="";if(!node)return"";if(!config.supportsTextContent)config.supportsTextContent=node.textContent||false;if(config.textExtraction=="simple"){if(config.supportsTextContent){text=node.textContent;}else{if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){text=node.childNodes[0].innerHTML;}else{text=node.innerHTML;}}}else{if(typeof(config.textExtraction)=="function"){text=config.textExtraction(node);}else{text=$(node).text();}}return text;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){var pos=n[i][checkCell];rows.push(r[pos]);if(!table.config.appender){var l=r[pos].length;for(var j=0;j<l;j++){tableBody[0].appendChild(r[pos][j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false;var header_index=computeTableHeaderCellIndexes(table);$tableHeaders=$(table.config.selectorHeaders,table).each(function(index){this.column=header_index[this.parentNode.rowIndex+"-"+this.cellIndex];this.order=formatSortingOrder(table.config.sortInitialOrder);this.count=this.order;if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(checkHeaderOptionsSortingLocked(table,index))this.order=this.lockedOrder=checkHeaderOptionsSortingLocked(table,index);if(!this.sortDisabled){var $th=$(this).addClass(table.config.cssHeader);if(table.config.onRenderHeader)table.config.onRenderHeader.apply($th);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function computeTableHeaderCellIndexes(t){var matrix=[];var lookup={};var thead=t.getElementsByTagName('THEAD')[0];var trs=thead.getElementsByTagName('TR');for(var i=0;i<trs.length;i++){var cells=trs[i].cells;for(var j=0;j<cells.length;j++){var c=cells[j];var rowIndex=c.parentNode.rowIndex;var cellId=rowIndex+"-"+c.cellIndex;var rowSpan=c.rowSpan||1;var colSpan=c.colSpan||1
+var firstAvailCol;if(typeof(matrix[rowIndex])=="undefined"){matrix[rowIndex]=[];}for(var k=0;k<matrix[rowIndex].length+1;k++){if(typeof(matrix[rowIndex][k])=="undefined"){firstAvailCol=k;break;}}lookup[cellId]=firstAvailCol;for(var k=rowIndex;k<rowIndex+rowSpan;k++){if(typeof(matrix[k])=="undefined"){matrix[k]=[];}var matrixrow=matrix[k];for(var l=firstAvailCol;l<firstAvailCol+colSpan;l++){matrixrow[l]="x";}}}}return lookup;}function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function checkHeaderOptionsSortingLocked(table,i){if((table.config.headers[i])&&(table.config.headers[i].lockedOrder))return table.config.headers[i].lockedOrder;return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){return(v.toLowerCase()=="desc")?1:0;}else{return(v==1)?1:0;}}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(table.config.parsers[c].type=="text")?((order==0)?makeSortFunction("text","asc",c):makeSortFunction("text","desc",c)):((order==0)?makeSortFunction("numeric","asc",c):makeSortFunction("numeric","desc",c));var e="e"+i;dynamicExp+="var "+e+" = "+s;dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";if(table.config.debug){benchmark("Evaling expression:"+dynamicExp,new Date());}eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function makeSortFunction(type,direction,index){var a="a["+index+"]",b="b["+index+"]";if(type=='text'&&direction=='asc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+a+" < "+b+") ? -1 : 1 )));";}else if(type=='text'&&direction=='desc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+b+" < "+a+") ? -1 : 1 )));";}else if(type=='numeric'&&direction=='asc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+a+" - "+b+"));";}else if(type=='numeric'&&direction=='desc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+b+" - "+a+"));";}};function makeSortText(i){return"((a["+i+"] < b["+i+"]) ? -1 : ((a["+i+"] > b["+i+"]) ? 1 : 0));";};function makeSortTextDesc(i){return"((b["+i+"] < a["+i+"]) ? -1 : ((b["+i+"] > a["+i+"]) ? 1 : 0));";};function makeSortNumeric(i){return"a["+i+"]-b["+i+"];";};function makeSortNumericDesc(i){return"b["+i+"]-a["+i+"];";};function sortText(a,b){if(table.config.sortLocaleCompare)return a.localeCompare(b);return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){if(table.config.sortLocaleCompare)return b.localeCompare(a);return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$.data(this,"tablesorter",config);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){$this.trigger("sortStart");var $cell=$(this);var i=this.column;this.order=this.count++%2;if(this.lockedOrder)this.order=this.lockedOrder;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){var me=this;setTimeout(function(){me.config.parsers=buildParserCache(me,$headers);cache=buildCache(me);},1);}).bind("updateCell",function(e,cell){var config=this.config;var pos=[(cell.parentNode.rowIndex-1),cell.cellIndex];cache.normalized[pos[0]][pos[1]]=config.parsers[pos[1]].format(getElementText(config,cell),cell);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){return/^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g,'')));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLocaleLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[£$€?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[£$€]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}var $tr,row=-1,odd;$("tr:visible",table.tBodies[0]).each(function(i){$tr=$(this);if(!$tr.hasClass(table.config.cssChildRow))row++;odd=(row%2==0);$tr.removeClass(table.config.widgetZebra.css[odd?0:1]).addClass(table.config.widgetZebra.css[odd?1:0])});if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery); \ No newline at end of file
diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile
index 037a686963..f7dce195d7 100644
--- a/lib/common_test/src/Makefile
+++ b/lib/common_test/src/Makefile
@@ -70,14 +70,18 @@ MODULES= \
ct_hooks\
ct_hooks_lock\
cth_log_redirect\
- cth_surefire
+ cth_surefire \
+ ct_netconfc \
+ ct_conn_log_h \
+ cth_conn_log
TARGET_MODULES= $(MODULES:%=$(EBIN)/%)
BEAM_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
ERL_FILES= $(MODULES:=.erl)
HRL_FILES = \
- ct_util.hrl
+ ct_util.hrl \
+ ct_netconfc.hrl
EXTERNAL_HRL_FILES = \
../include/ct.hrl \
../include/ct_event.hrl
diff --git a/lib/common_test/src/common_test.app.src b/lib/common_test/src/common_test.app.src
index ae9a51faeb..18c1dec784 100644
--- a/lib/common_test/src/common_test.app.src
+++ b/lib/common_test/src/common_test.app.src
@@ -33,6 +33,8 @@
ct_master_event,
ct_master_logs,
ct_master_status,
+ ct_netconfc,
+ ct_conn_log_h,
ct_repeat,
ct_rpc,
ct_run,
@@ -49,6 +51,7 @@
ct_config_xml,
ct_slave,
cth_log_redirect,
+ cth_conn_log,
cth_surefire
]},
{registered, [ct_logs,
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index 571d99029f..5014309c0f 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -25,24 +25,24 @@
%%%
%%% <p><strong>Test Suite Support Macros</strong></p>
%%%
-%%% <p>The <code>config</code> macro is defined in <code>ct.hrl</code>. This
+%%% <p>The <c>config</c> macro is defined in <c>ct.hrl</c>. This
%%% macro should be used to retrieve information from the
-%%% <code>Config</code> variable sent to all test cases. It is used with two
+%%% <c>Config</c> variable sent to all test cases. It is used with two
%%% arguments, where the first is the name of the configuration
-%%% variable you wish to retrieve, and the second is the <code>Config</code>
+%%% variable you wish to retrieve, and the second is the <c>Config</c>
%%% variable supplied to the test case.</p>
%%%
%%% <p>Possible configuration variables include:</p>
%%% <ul>
-%%% <li><code>data_dir</code> - Data file directory.</li>
-%%% <li><code>priv_dir</code> - Scratch file directory.</li>
-%%% <li>Whatever added by <code>init_per_suite/1</code> or
-%%% <code>init_per_testcase/2</code> in the test suite.</li>
+%%% <li><c>data_dir</c> - Data file directory.</li>
+%%% <li><c>priv_dir</c> - Scratch file directory.</li>
+%%% <li>Whatever added by <c>init_per_suite/1</c> or
+%%% <c>init_per_testcase/2</c> in the test suite.</li>
%%% </ul>
%%% @type var_name() = atom(). A variable name which is specified when
-%%% <code>ct:require/2</code> is called,
-%%% e.g. <code>ct:require(mynodename,{node,[telnet]})</code>
+%%% <c>ct:require/2</c> is called,
+%%% e.g. <c>ct:require(mynodename,{node,[telnet]})</c>
%%%
%%% @type target_name() = var_name(). The name of a target.
%%%
@@ -51,6 +51,8 @@
-module(ct).
+-include("ct.hrl").
+
%% Command line user interface for running tests
-export([install/1, run/1, run/2, run/3,
run_test/1, run_testspec/1, step/3, step/4,
@@ -60,13 +62,15 @@
-export([require/1, require/2,
get_config/1, get_config/2, get_config/3,
reload_config/1,
- log/1, log/2, log/3,
- print/1, print/2, print/3,
- pal/1, pal/2, pal/3,
+ log/1, log/2, log/3, log/4,
+ print/1, print/2, print/3, print/4,
+ pal/1, pal/2, pal/3, pal/4,
capture_start/0, capture_stop/0, capture_get/0, capture_get/1,
fail/1, fail/2, comment/1, comment/2, make_priv_dir/0,
testcases/2, userdata/2, userdata/3,
- timetrap/1, get_timetrap_info/0, sleep/1]).
+ timetrap/1, get_timetrap_info/0, sleep/1,
+ notify/2, sync_notify/2,
+ break/1, break/2, continue/0, continue/1]).
%% New API for manipulating with config handlers
-export([add_config/2, remove_config/2]).
@@ -95,10 +99,10 @@
%%% <p>Run this function once before first test.</p>
%%%
%%% <p>Example:<br/>
-%%% <code>install([{config,["config_node.ctc","config_user.ctc"]}])</code>.</p>
+%%% <c>install([{config,["config_node.ctc","config_user.ctc"]}])</c>.</p>
%%%
%%% <p>Note that this function is automatically run by the
-%%% <code>ct_run</code> program.</p>
+%%% <c>ct_run</c> program.</p>
install(Opts) ->
ct_run:install(Opts).
@@ -111,10 +115,10 @@ install(Opts) ->
%%%
%%% @doc Run the given test case(s).
%%%
-%%% <p>Requires that <code>ct:install/1</code> has been run first.</p>
+%%% <p>Requires that <c>ct:install/1</c> has been run first.</p>
%%%
%%% <p>Suites (*_SUITE.erl) files must be stored in
-%%% <code>TestDir</code> or <code>TestDir/test</code>. All suites
+%%% <c>TestDir</c> or <c>TestDir/test</c>. All suites
%%% will be compiled when test is run.</p>
run(TestDir,Suite,Cases) ->
ct_run:run(TestDir,Suite,Cases).
@@ -150,8 +154,10 @@ run(TestDirs) ->
%%% {multiply_timetraps,M} | {scale_timetraps,Bool} |
%%% {repeat,N} | {duration,DurTime} | {until,StopTime} |
%%% {force_stop,Bool} | {decrypt,DecryptKeyOrFile} |
-%%% {refresh_logs,LogDir} | {logopts,LogOpts} | {basic_html,Bool} |
-%%% {ct_hooks, CTHs} | {enable_builtin_hooks,Bool}
+%%% {refresh_logs,LogDir} | {logopts,LogOpts} |
+%%% {verbosity,VLevels} | {basic_html,Bool} |
+%%% {ct_hooks, CTHs} | {enable_builtin_hooks,Bool} |
+%%% {release_shell,Bool}
%%% TestDirs = [string()] | string()
%%% Suites = [string()] | [atom()] | string() | atom()
%%% Cases = [atom()] | atom()
@@ -182,26 +188,45 @@ run(TestDirs) ->
%%% DecryptFile = string()
%%% LogOpts = [LogOpt]
%%% LogOpt = no_nl | no_src
+%%% VLevels = VLevel | [{Category,VLevel}]
+%%% VLevel = integer()
+%%% Category = atom()
%%% CTHs = [CTHModule | {CTHModule, CTHInitArgs}]
%%% CTHModule = atom()
%%% CTHInitArgs = term()
-%%% Result = [TestResult] | {error,Reason}
-%%% @doc Run tests as specified by the combination of options in <code>Opts</code>.
+%%% Result = {Ok,Failed,{UserSkipped,AutoSkipped}} | TestRunnerPid | {error,Reason}
+%%% Ok = integer()
+%%% Failed = integer()
+%%% UserSkipped = integer()
+%%% AutoSkipped = integer()
+%%% TestRunnerPid = pid()
+%%% Reason = term()
+%%% @doc <p>Run tests as specified by the combination of options in <c>Opts</c>.
%%% The options are the same as those used with the
-%%% <seealso marker="ct_run#ct_run"><code>ct_run</code></seealso> program.
-%%% Note that here a <code>TestDir</code> can be used to point out the path to
-%%% a <code>Suite</code>. Note also that the option <code>testcase</code>
-%%% corresponds to the <code>-case</code> option in the <code>ct_run</code>
-%%% program. Configuration files specified in <code>Opts</code> will be
-%%% installed automatically at startup.
+%%% <seealso marker="ct_run#ct_run"><c>ct_run</c></seealso> program.
+%%% Note that here a <c>TestDir</c> can be used to point out the path to
+%%% a <c>Suite</c>. Note also that the option <c>testcase</c>
+%%% corresponds to the <c>-case</c> option in the <c>ct_run</c>
+%%% program. Configuration files specified in <c>Opts</c> will be
+%%% installed automatically at startup.</p>
+%%% <p><c>TestRunnerPid</c> is returned if <c>release_shell == true</c>
+%%% (see <c>break/1</c> for details).</p>
+%%% <p><c>Reason</c> indicates what type of error has been encountered.</p>
run_test(Opts) ->
ct_run:run_test(Opts).
%%%-----------------------------------------------------------------
%%% @spec run_testspec(TestSpec) -> Result
%%% TestSpec = [term()]
-%%% @doc Run test specified by <code>TestSpec</code>. The terms are
+%%% Result = {Ok,Failed,{UserSkipped,AutoSkipped}} | {error,Reason}
+%%% Ok = integer()
+%%% Failed = integer()
+%%% UserSkipped = integer()
+%%% AutoSkipped = integer()
+%%% Reason = term()
+%%% @doc Run test specified by <c>TestSpec</c>. The terms are
%%% the same as those used in test specification files.
+%%% <p><c>Reason</c> indicates what type of error has been encountered.</p>
run_testspec(TestSpec) ->
ct_run:run_testspec(TestSpec).
@@ -221,8 +246,8 @@ step(TestDir,Suite,Case) ->
%%% Opt = config | keep_inactive
%%%
%%% @doc Step through a test case with the debugger. If the
-%%% <code>config</code> option has been given, breakpoints will
-%%% be set also on the configuration functions in <code>Suite</code>.
+%%% <c>config</c> option has been given, breakpoints will
+%%% be set also on the configuration functions in <c>Suite</c>.
%%% @see run/3
step(TestDir,Suite,Case,Opts) ->
ct_run:step(TestDir,Suite,Case,Opts).
@@ -234,22 +259,23 @@ step(TestDir,Suite,Case,Opts) ->
%%%
%%% <p>From this mode all test case support functions can be executed
%%% directly from the erlang shell. The interactive mode can also be
-%%% started from the OS command line with <code>ct_run -shell
-%%% [-config File...]</code>.</p>
+%%% started from the OS command line with <c>ct_run -shell
+%%% [-config File...]</c>.</p>
%%%
%%% <p>If any functions using "required config data" (e.g. telnet or
%%% ftp functions) are to be called from the erlang shell, config data
-%%% must first be required with <code>ct:require/2</code>.</p>
+%%% must first be required with <c>ct:require/2</c>.</p>
%%%
%%% <p>Example:<br/>
-%%% <code>&gt; ct:require(unix_telnet, unix).</code><br/>
-%%% <code>ok</code><br/>
-%%% <code>&gt; ct_telnet:open(unix_telnet).</code><br/>
-%%% <code>{ok,&lt;0.105.0&gt;}</code><br/>
-%%% <code>&gt; ct_telnet:cmd(unix_telnet, "ls .").</code><br/>
-%%% <code>{ok,["ls","file1 ...",...]}</code></p>
+%%% <c>&gt; ct:require(unix_telnet, unix).</c><br/>
+%%% <c>ok</c><br/>
+%%% <c>&gt; ct_telnet:open(unix_telnet).</c><br/>
+%%% <c>{ok,&lt;0.105.0&gt;}</c><br/>
+%%% <c>&gt; ct_telnet:cmd(unix_telnet, "ls .").</c><br/>
+%%% <c>{ok,["ls","file1 ...",...]}</c></p>
start_interactive() ->
- ct_util:start(interactive).
+ ct_util:start(interactive),
+ ok.
%%%-----------------------------------------------------------------
%%% @spec stop_interactive() -> ok
@@ -257,7 +283,8 @@ start_interactive() ->
%%% @doc Exit the interactive mode.
%%% @see start_interactive/0
stop_interactive() ->
- ct_util:stop(normal).
+ ct_util:stop(normal),
+ ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% MISC INTERFACE
@@ -265,27 +292,34 @@ stop_interactive() ->
%%%-----------------------------------------------------------------
%%% @spec require(Required) -> ok | {error,Reason}
-%%% Required = Key | {Key,SubKeys}
+%%% Required = Key | {Key,SubKeys} | {Key,SubKey,SubKeys}
%%% Key = atom()
%%% SubKeys = SubKey | [SubKey]
%%% SubKey = atom()
%%%
-%%% @doc Check if the required configuration is available.
+%%% @doc Check if the required configuration is available. It is possible
+%%% to specify arbitrarily deep tuples as <c>Required</c>. Note that it is
+%%% only the last element of the tuple which can be a list of <c>SubKey</c>s.
%%%
-%%% <p>Example: require the variable <code>myvar</code>:<br/>
-%%% <code>ok = ct:require(myvar)</code></p>
+%%% <p>Example 1: require the variable <c>myvar</c>:</p>
+%%% <pre>ok = ct:require(myvar).</pre>
%%%
%%% <p>In this case the config file must at least contain:</p>
-%%% <pre>
-%%% {myvar,Value}.</pre>
+%%% <pre>{myvar,Value}.</pre>
%%%
-%%% <p>Example: require the variable <code>myvar</code> with
-%%% subvariable <code>sub1</code>:<br/>
-%%% <code>ok = ct:require({myvar,sub1})</code></p>
+%%% <p>Example 2: require the key <c>myvar</c> with
+%%% subkeys <c>sub1</c> and <c>sub2</c>:</p>
+%%% <pre>ok = ct:require({myvar,[sub1,sub2]}).</pre>
%%%
%%% <p>In this case the config file must at least contain:</p>
-%%% <pre>
-%%% {myvar,[{sub1,Value}]}.</pre>
+%%% <pre>{myvar,[{sub1,Value},{sub2,Value}]}.</pre>
+%%%
+%%% <p>Example 3: require the key <c>myvar</c> with
+%%% subkey <c>sub1</c> with <c>subsub1</c>:</p>
+%%% <pre>ok = ct:require({myvar,sub1,sub2}).</pre>
+%%%
+%%% <p>In this case the config file must at least contain:</p>
+%%% <pre>{myvar,[{sub1,[{sub2,Value}]}]}.</pre>
%%%
%%% @see require/2
%%% @see get_config/1
@@ -297,30 +331,36 @@ require(Required) ->
%%%-----------------------------------------------------------------
%%% @spec require(Name,Required) -> ok | {error,Reason}
%%% Name = atom()
-%%% Required = Key | {Key,SubKeys}
+%%% Required = Key | {Key,SubKey} | {Key,SubKey,SubKey}
+%%% SubKey = Key
%%% Key = atom()
-%%% SubKeys = SubKey | [SubKey]
-%%% SubKey = atom()
%%%
%%% @doc Check if the required configuration is available, and give it
-%%% a name.
+%%% a name. The semantics for <c>Required</c> is the same as in
+%%% <c>required/1</c> except that it is not possible to specify a list
+%%% of <c>SubKey</c>s.
%%%
-%%% <p>If the requested data is available, the main entry will be
-%%% associated with <code>Name</code> so that the value of the element
-%%% can be read with <code>get_config/1,2</code> provided
-%%% <code>Name</code> instead of the <code>Key</code>.</p>
+%%% <p>If the requested data is available, the sub entry will be
+%%% associated with <c>Name</c> so that the value of the element
+%%% can be read with <c>get_config/1,2</c> provided
+%%% <c>Name</c> instead of the whole <c>Required</c> term.</p>
%%%
%%% <p>Example: Require one node with a telnet connection and an
-%%% ftp connection. Name the node <code>a</code>:<br/> <code>ok =
-%%% ct:require(a,{node,[telnet,ftp]}).</code><br/> All references
-%%% to this node may then use the node name. E.g. you can fetch a
-%%% file over ftp like this:<br/>
-%%% <code>ok = ct:ftp_get(a,RemoteFile,LocalFile).</code></p>
+%%% ftp connection. Name the node <c>a</c>:
+%%% <pre>ok = ct:require(a,{machine,node}).</pre>
+%%% All references to this node may then use the node name.
+%%% E.g. you can fetch a file over ftp like this:</p>
+%%% <pre>ok = ct:ftp_get(a,RemoteFile,LocalFile).</pre>
%%%
%%% <p>For this to work, the config file must at least contain:</p>
-%%% <pre>
-%%% {node,[{telnet,IpAddr},
-%%% {ftp,IpAddr}]}.</pre>
+%%% <pre>{machine,[{node,[{telnet,IpAddr},{ftp,IpAddr}]}]}.</pre>
+%%%
+%%% <note><p>The behaviour of this function changed radically in common_test
+%%% 1.6.2. In order too keep some backwards compatability it is still possible
+%%% to do: <br/><c>ct:require(a,{node,[telnet,ftp]}).</c><br/>
+%%% This will associate the name <c>a</c> with the top level <c>node</c> entry.
+%%% For this to work, the config file must at least contain:<br/>
+%%% <c>{node,[{telnet,IpAddr},{ftp,IpAddr}]}.</c></p></note>
%%%
%%% @see require/1
%%% @see get_config/1
@@ -343,7 +383,7 @@ get_config(Required,Default) ->
%%%-----------------------------------------------------------------
%%% @spec get_config(Required,Default,Opts) -> ValueOrElement
-%%% Required = KeyOrName | {KeyOrName,SubKey}
+%%% Required = KeyOrName | {KeyOrName,SubKey} | {KeyOrName,SubKey,SubKey}
%%% KeyOrName = atom()
%%% SubKey = atom()
%%% Default = term()
@@ -355,43 +395,41 @@ get_config(Required,Default) ->
%%%
%%% <p>This function returns the matching value(s) or config element(s),
%%% given a config variable key or its associated name
-%%% (if one has been specified with <code>require/2</code> or a
+%%% (if one has been specified with <c>require/2</c> or a
%%% require statement).</p>
%%%
%%% <p>Example, given the following config file:</p>
%%% <pre>
%%% {unix,[{telnet,IpAddr},
-%%% {username,Username},
-%%% {password,Password}]}.</pre>
-%%% <p><code>get_config(unix,Default) ->
+%%% {user,[{username,Username},
+%%% {password,Password}]}]}.</pre>
+%%% <p><c>ct:get_config(unix,Default) ->
%%% [{telnet,IpAddr},
-%%% {username,Username},
-%%% {password,Password}]</code><br/>
-%%% <code>get_config({unix,telnet},Default) -> IpAddr</code><br/>
-%%% <code>get_config({unix,ftp},Default) -> Default</code><br/>
-%%% <code>get_config(unknownkey,Default) -> Default</code></p>
+%%% {user, [{username,Username},
+%%% {password,Password}]}]</c><br/>
+%%% <c>ct:get_config({unix,telnet},Default) -> IpAddr</c><br/>
+%%% <c>ct:get_config({unix,user,username},Default) -> Username</c><br/>
+%%% <c>ct:get_config({unix,ftp},Default) -> Default</c><br/>
+%%% <c>ct:get_config(unknownkey,Default) -> Default</c></p>
%%%
%%% <p>If a config variable key has been associated with a name (by
-%%% means of <code>require/2</code> or a require statement), the name
+%%% means of <c>require/2</c> or a require statement), the name
%%% may be used instead of the key to read the value:</p>
%%%
-%%% <p><code>require(myhost,unix) -> ok</code><br/>
-%%% <code>get_config(myhost,Default) ->
-%%% [{telnet,IpAddr},
-%%% {username,Username},
-%%% {password,Password}]</code></p>
+%%% <p><c>ct:require(myuser,{unix,user}) -> ok.</c><br/>
+%%% <c>ct:get_config(myuser,Default) ->
+%%% [{username,Username},
+%%% {password,Password}]</c></p>
%%%
%%% <p>If a config variable is defined in multiple files and you want to
-%%% access all possible values, use the <code>all</code> option. The
+%%% access all possible values, use the <c>all</c> option. The
%%% values will be returned in a list and the order of the elements
%%% corresponds to the order that the config files were specified at
%%% startup.</p>
%%%
%%% <p>If you want config elements (key-value tuples) returned as result
-%%% instead of values, use the <code>element</code> option.
-%%% The returned elements will then be on the form <code>{KeyOrName,Value}</code>,
-%%% or (in case a subkey has been specified)
-%%% <code>{{KeyOrName,SubKey},Value}</code></p>
+%%% instead of values, use the <c>element</c> option.
+%%% The returned elements will then be on the form <c>{Required,Value}</c></p>
%%%
%%% @see get_config/1
%%% @see get_config/2
@@ -402,7 +440,7 @@ get_config(Required,Default,Opts) ->
%%%-----------------------------------------------------------------
%%% @spec reload_config(Required) -> ValueOrElement
-%%% Required = KeyOrName | {KeyOrName,SubKey}
+%%% Required = KeyOrName | {KeyOrName,SubKey} | {KeyOrName,SubKey,SubKey}
%%% KeyOrName = atom()
%%% SubKey = atom()
%%% ValueOrElement = term()
@@ -421,25 +459,41 @@ reload_config(Required)->
%%%-----------------------------------------------------------------
%%% @spec log(Format) -> ok
-%%% @equiv log(default,Format,[])
+%%% @equiv log(default,50,Format,[])
log(Format) ->
- log(default,Format,[]).
+ log(default,?STD_IMPORTANCE,Format,[]).
%%%-----------------------------------------------------------------
%%% @spec log(X1,X2) -> ok
-%%% X1 = Category | Format
+%%% X1 = Category | Importance | Format
%%% X2 = Format | Args
-%%% @equiv log(Category,Format,Args)
+%%% @equiv log(Category,Importance,Format,Args)
log(X1,X2) ->
- {Category,Format,Args} =
- if is_atom(X1) -> {X1,X2,[]};
- is_list(X1) -> {default,X1,X2}
+ {Category,Importance,Format,Args} =
+ if is_atom(X1) -> {X1,?STD_IMPORTANCE,X2,[]};
+ is_integer(X1) -> {default,X1,X2,[]};
+ is_list(X1) -> {default,?STD_IMPORTANCE,X1,X2}
end,
- log(Category,Format,Args).
+ log(Category,Importance,Format,Args).
%%%-----------------------------------------------------------------
-%%% @spec log(Category,Format,Args) -> ok
+%%% @spec log(X1,X2,X3) -> ok
+%%% X1 = Category | Importance
+%%% X2 = Importance | Format
+%%% X3 = Format | Args
+%%% @equiv log(Category,Importance,Format,Args)
+log(X1,X2,X3) ->
+ {Category,Importance,Format,Args} =
+ if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[]};
+ is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3};
+ is_integer(X1) -> {default,X1,X2,X3}
+ end,
+ log(Category,Importance,Format,Args).
+
+%%%-----------------------------------------------------------------
+%%% @spec log(Category,Importance,Format,Args) -> ok
%%% Category = atom()
+%%% Importance = integer()
%%% Format = string()
%%% Args = list()
%%%
@@ -448,30 +502,52 @@ log(X1,X2) ->
%%% <p>This function is meant for printing a string directly from a
%%% test case to the test case log file.</p>
%%%
-%%% <p>Default <code>Category</code> is <code>default</code> and
-%%% default <code>Args</code> is <code>[]</code>.</p>
-log(Category,Format,Args) ->
- ct_logs:tc_log(Category,Format,Args).
+%%% <p>Default <c>Category</c> is <c>default</c>,
+%%% default <c>Importance</c> is <c>?STD_IMPORTANCE</c>,
+%%% and default value for <c>Args</c> is <c>[]</c>.</p>
+%%% <p>Please see the User's Guide for details on <c>Category</c>
+%%% and <c>Importance</c>.</p>
+log(Category,Importance,Format,Args) ->
+ ct_logs:tc_log(Category,Importance,Format,Args).
%%%-----------------------------------------------------------------
%%% @spec print(Format) -> ok
-%%% @equiv print(default,Format,[])
+%%% @equiv print(default,50,Format,[])
print(Format) ->
- print(default,Format,[]).
+ print(default,?STD_IMPORTANCE,Format,[]).
%%%-----------------------------------------------------------------
-%%% @equiv print(Category,Format,Args)
+%%% @spec print(X1,X2) -> ok
+%%% X1 = Category | Importance | Format
+%%% X2 = Format | Args
+%%% @equiv print(Category,Importance,Format,Args)
print(X1,X2) ->
- {Category,Format,Args} =
- if is_atom(X1) -> {X1,X2,[]};
- is_list(X1) -> {default,X1,X2}
+ {Category,Importance,Format,Args} =
+ if is_atom(X1) -> {X1,?STD_IMPORTANCE,X2,[]};
+ is_integer(X1) -> {default,X1,X2,[]};
+ is_list(X1) -> {default,?STD_IMPORTANCE,X1,X2}
end,
- print(Category,Format,Args).
+ print(Category,Importance,Format,Args).
%%%-----------------------------------------------------------------
-%%% @spec print(Category,Format,Args) -> ok
+%%% @spec print(X1,X2,X3) -> ok
+%%% X1 = Category | Importance
+%%% X2 = Importance | Format
+%%% X3 = Format | Args
+%%% @equiv print(Category,Importance,Format,Args)
+print(X1,X2,X3) ->
+ {Category,Importance,Format,Args} =
+ if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[]};
+ is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3};
+ is_integer(X1) -> {default,X1,X2,X3}
+ end,
+ print(Category,Importance,Format,Args).
+
+%%%-----------------------------------------------------------------
+%%% @spec print(Category,Importance,Format,Args) -> ok
%%% Category = atom()
+%%% Importance = integer()
%%% Format = string()
%%% Args = list()
%%%
@@ -480,33 +556,52 @@ print(X1,X2) ->
%%% <p>This function is meant for printing a string from a test case
%%% to the console.</p>
%%%
-%%% <p>Default <code>Category</code> is <code>default</code> and
-%%% default <code>Args</code> is <code>[]</code>.</p>
-print(Category,Format,Args) ->
- ct_logs:tc_print(Category,Format,Args).
+%%% <p>Default <c>Category</c> is <c>default</c>,
+%%% default <c>Importance</c> is <c>?STD_IMPORTANCE</c>,
+%%% and default value for <c>Args</c> is <c>[]</c>.</p>
+%%% <p>Please see the User's Guide for details on <c>Category</c>
+%%% and <c>Importance</c>.</p>
+print(Category,Importance,Format,Args) ->
+ ct_logs:tc_print(Category,Importance,Format,Args).
%%%-----------------------------------------------------------------
%%% @spec pal(Format) -> ok
-%%% @equiv pal(default,Format,[])
+%%% @equiv pal(default,50,Format,[])
pal(Format) ->
- pal(default,Format,[]).
+ pal(default,?STD_IMPORTANCE,Format,[]).
%%%-----------------------------------------------------------------
%%% @spec pal(X1,X2) -> ok
-%%% X1 = Category | Format
+%%% X1 = Category | Importance | Format
%%% X2 = Format | Args
-%%% @equiv pal(Category,Format,Args)
+%%% @equiv pal(Category,Importance,Format,Args)
pal(X1,X2) ->
- {Category,Format,Args} =
- if is_atom(X1) -> {X1,X2,[]};
- is_list(X1) -> {default,X1,X2}
+ {Category,Importance,Format,Args} =
+ if is_atom(X1) -> {X1,?STD_IMPORTANCE,X2,[]};
+ is_integer(X1) -> {default,X1,X2,[]};
+ is_list(X1) -> {default,?STD_IMPORTANCE,X1,X2}
end,
- pal(Category,Format,Args).
+ pal(Category,Importance,Format,Args).
%%%-----------------------------------------------------------------
-%%% @spec pal(Category,Format,Args) -> ok
+%%% @spec pal(X1,X2,X3) -> ok
+%%% X1 = Category | Importance
+%%% X2 = Importance | Format
+%%% X3 = Format | Args
+%%% @equiv pal(Category,Importance,Format,Args)
+pal(X1,X2,X3) ->
+ {Category,Importance,Format,Args} =
+ if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[]};
+ is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3};
+ is_integer(X1) -> {default,X1,X2,X3}
+ end,
+ pal(Category,Importance,Format,Args).
+
+%%%-----------------------------------------------------------------
+%%% @spec pal(Category,Importance,Format,Args) -> ok
%%% Category = atom()
+%%% Importance = integer()
%%% Format = string()
%%% Args = list()
%%%
@@ -515,10 +610,13 @@ pal(X1,X2) ->
%%% <p>This function is meant for printing a string from a test case,
%%% both to the test case log file and to the console.</p>
%%%
-%%% <p>Default <code>Category</code> is <code>default</code> and
-%%% default <code>Args</code> is <code>[]</code>.</p>
-pal(Category,Format,Args) ->
- ct_logs:tc_pal(Category,Format,Args).
+%%% <p>Default <c>Category</c> is <c>default</c>,
+%%% default <c>Importance</c> is <c>?STD_IMPORTANCE</c>,
+%%% and default value for <c>Args</c> is <c>[]</c>.</p>
+%%% <p>Please see the User's Guide for details on <c>Category</c>
+%%% and <c>Importance</c>.</p>
+pal(Category,Importance,Format,Args) ->
+ ct_logs:tc_pal(Category,Importance,Format,Args).
%%%-----------------------------------------------------------------
%%% @spec capture_start() -> ok
@@ -535,7 +633,7 @@ capture_start() ->
%%% @spec capture_stop() -> ok
%%%
%%% @doc Stop capturing text strings (a session started with
-%%% <code>capture_start/0</code>).
+%%% <c>capture_start/0</c>).
%%%
%%% @see capture_start/0
%%% @see capture_get/1
@@ -558,9 +656,9 @@ capture_get() ->
%%%
%%% @doc Return and purge the list of text strings buffered
%%% during the latest session of capturing printouts to stdout.
-%%% With <code>ExclCategories</code> it's possible to specify
-%%% log categories that should be ignored in <code>ListOfStrings</code>.
-%%% If <code>ExclCategories = []</code>, no filtering takes place.
+%%% With <c>ExclCategories</c> it's possible to specify
+%%% log categories that should be ignored in <c>ListOfStrings</c>.
+%%% If <c>ExclCategories = []</c>, no filtering takes place.
%%%
%%% @see capture_start/0
%%% @see capture_stop/0
@@ -585,7 +683,7 @@ capture_get([]) ->
%%% Reason = term()
%%%
%%% @doc Terminate a test case with the given error
-%%% <code>Reason</code>.
+%%% <c>Reason</c>.
fail(Reason) ->
try
exit({test_case_failed,Reason})
@@ -605,7 +703,7 @@ fail(Reason) ->
%%%
%%% @doc Terminate a test case with an error message specified
%%% by a format string and a list of values (used as arguments to
-%%% <code>io_lib:format/2</code>).
+%%% <c>io_lib:format/2</c>).
fail(Format, Args) ->
try io_lib:format(Format, Args) of
Str ->
@@ -628,11 +726,11 @@ fail(Format, Args) ->
%%% @spec comment(Comment) -> void()
%%% Comment = term()
%%%
-%%% @doc Print the given <code>Comment</code> in the comment field in
+%%% @doc Print the given <c>Comment</c> in the comment field in
%%% the table on the test suite result page.
%%%
%%% <p>If called several times, only the last comment is printed.
-%%% The test case return value <code>{comment,Comment}</code>
+%%% The test case return value <c>{comment,Comment}</c>
%%% overwrites the string set by this function.</p>
comment(Comment) when is_list(Comment) ->
Formatted =
@@ -655,10 +753,10 @@ comment(Comment) ->
%%% @doc Print the formatted string in the comment field in
%%% the table on the test suite result page.
%%%
-%%% <p>The <code>Format</code> and <code>Args</code> arguments are
-%%% used in call to <code>io_lib:format/2</code> in order to create
-%%% the comment string. The behaviour of <code>comment/2</code> is
-%%% otherwise the same as the <code>comment/1</code> function (see
+%%% <p>The <c>Format</c> and <c>Args</c> arguments are
+%%% used in call to <c>io_lib:format/2</c> in order to create
+%%% the comment string. The behaviour of <c>comment/2</c> is
+%%% otherwise the same as the <c>comment/1</c> function (see
%%% above for details).</p>
comment(Format, Args) when is_list(Format), is_list(Args) ->
Formatted =
@@ -703,11 +801,11 @@ get_target_name(Handle) ->
%%% @doc Parse the printout from an SQL table and return a list of tuples.
%%%
%%% <p>The printout to parse would typically be the result of a
-%%% <code>select</code> command in SQL. The returned
-%%% <code>Table</code> is a list of tuples, where each tuple is a row
+%%% <c>select</c> command in SQL. The returned
+%%% <c>Table</c> is a list of tuples, where each tuple is a row
%%% in the table.</p>
%%%
-%%% <p><code>Heading</code> is a tuple of strings representing the
+%%% <p><c>Heading</c> is a tuple of strings representing the
%%% headings of each column in the table.</p>
parse_table(Data) ->
ct_util:parse_table(Data).
@@ -779,8 +877,8 @@ make_and_load(Dir, Suite) ->
%%% SuiteUserData = [term()]
%%% Reason = term()
%%%
-%%% @doc Returns any data specified with the tag <code>userdata</code>
-%%% in the list of tuples returned from <code>Suite:suite/0</code>.
+%%% @doc Returns any data specified with the tag <c>userdata</c>
+%%% in the list of tuples returned from <c>Suite:suite/0</c>.
userdata(TestDir, Suite) ->
case make_and_load(TestDir, Suite) of
E = {error,_} ->
@@ -815,9 +913,9 @@ get_userdata(_BadTerm, Spec) ->
%%% TCUserData = [term()]
%%% Reason = term()
%%%
-%%% @doc Returns any data specified with the tag <code>userdata</code>
-%%% in the list of tuples returned from <code>Suite:group(GroupName)</code>
-%%% or <code>Suite:Case()</code>.
+%%% @doc Returns any data specified with the tag <c>userdata</c>
+%%% in the list of tuples returned from <c>Suite:group(GroupName)</c>
+%%% or <c>Suite:Case()</c>.
userdata(TestDir, Suite, {group,GroupName}) ->
case make_and_load(TestDir, Suite) of
E = {error,_} ->
@@ -840,8 +938,9 @@ userdata(TestDir, Suite, Case) when is_atom(Case) ->
%%%-----------------------------------------------------------------
%%% @spec get_status() -> TestStatus | {error,Reason} | no_tests_running
%%% TestStatus = [StatusElem]
-%%% StatusElem = {current,{Suite,TestCase}} | {successful,Successful} |
+%%% StatusElem = {current,TestCaseInfo} | {successful,Successful} |
%%% {failed,Failed} | {skipped,Skipped} | {total,Total}
+%%% TestCaseInfo = {Suite,TestCase} | [{Suite,TestCase}]
%%% Suite = atom()
%%% TestCase = atom()
%%% Successful = integer()
@@ -853,7 +952,8 @@ userdata(TestDir, Suite, Case) when is_atom(Case) ->
%%% Reason = term()
%%%
%%% @doc Returns status of ongoing test. The returned list contains info about
-%%% which test case is currently executing, as well as counters for
+%%% which test case is currently executing (a list of cases when a
+%%% parallel test case group is executing), as well as counters for
%%% successful, failed, skipped, and total test cases so far.
get_status() ->
case get_testdata(curr_tc) of
@@ -878,6 +978,10 @@ get_testdata(Key) ->
Error;
{'EXIT',_Reason} ->
no_tests_running;
+ undefined ->
+ {error,no_testdata};
+ [CurrTC] when Key == curr_tc ->
+ {ok,CurrTC};
Data ->
{ok,Data}
end.
@@ -891,7 +995,7 @@ get_testdata(Key) ->
%%% executing. The function is therefore only safe to call from a function which
%%% has been called (or synchronously invoked) by the test case.</p>
%%%
-%%% <p><code>Reason</code>, the reason for aborting the test case, is printed
+%%% <p><c>Reason</c>, the reason for aborting the test case, is printed
%%% in the test case log.</p>
abort_current_testcase(Reason) ->
test_server_ctrl:abort_current_testcase(Reason).
@@ -904,13 +1008,13 @@ abort_current_testcase(Reason) ->
%%% Reason = term()
%%%
%%% @doc <p>This function encrypts the source config file with DES3 and
-%%% saves the result in file <code>EncryptFileName</code>. The key,
+%%% saves the result in file <c>EncryptFileName</c>. The key,
%%% a string, must be available in a text file named
-%%% <code>.ct_config.crypt</code> in the current directory, or the
+%%% <c>.ct_config.crypt</c> in the current directory, or the
%%% home directory of the user (it is searched for in that order).</p>
%%% <p>See the Common Test User's Guide for information about using
%%% encrypted config files when running tests.</p>
-%%% <p>See the <code>crypto</code> application for details on DES3
+%%% <p>See the <c>crypto</c> application for details on DES3
%%% encryption/decryption.</p>
encrypt_config_file(SrcFileName, EncryptFileName) ->
ct_config:encrypt_config_file(SrcFileName, EncryptFileName).
@@ -924,13 +1028,13 @@ encrypt_config_file(SrcFileName, EncryptFileName) ->
%%% Reason = term()
%%%
%%% @doc <p>This function encrypts the source config file with DES3 and
-%%% saves the result in the target file <code>EncryptFileName</code>.
+%%% saves the result in the target file <c>EncryptFileName</c>.
%%% The encryption key to use is either the value in
-%%% <code>{key,Key}</code> or the value stored in the file specified
-%%% by <code>{file,File}</code>.</p>
+%%% <c>{key,Key}</c> or the value stored in the file specified
+%%% by <c>{file,File}</c>.</p>
%%% <p>See the Common Test User's Guide for information about using
%%% encrypted config files when running tests.</p>
-%%% <p>See the <code>crypto</code> application for details on DES3
+%%% <p>See the <c>crypto</c> application for details on DES3
%%% encryption/decryption.</p>
encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile) ->
ct_config:encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile).
@@ -942,11 +1046,11 @@ encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile) ->
%%% TargetFileName = string()
%%% Reason = term()
%%%
-%%% @doc <p>This function decrypts <code>EncryptFileName</code>, previously
-%%% generated with <code>encrypt_config_file/2/3</code>. The original
+%%% @doc <p>This function decrypts <c>EncryptFileName</c>, previously
+%%% generated with <c>encrypt_config_file/2/3</c>. The original
%%% file contents is saved in the target file. The encryption key, a
%%% string, must be available in a text file named
-%%% <code>.ct_config.crypt</code> in the current directory, or the
+%%% <c>.ct_config.crypt</c> in the current directory, or the
%%% home directory of the user (it is searched for in that order).</p>
decrypt_config_file(EncryptFileName, TargetFileName) ->
ct_config:decrypt_config_file(EncryptFileName, TargetFileName).
@@ -959,8 +1063,8 @@ decrypt_config_file(EncryptFileName, TargetFileName) ->
%%% KeyOrFile = {key,string()} | {file,string()}
%%% Reason = term()
%%%
-%%% @doc <p>This function decrypts <code>EncryptFileName</code>, previously
-%%% generated with <code>encrypt_config_file/2/3</code>. The original
+%%% @doc <p>This function decrypts <c>EncryptFileName</c>, previously
+%%% generated with <c>encrypt_config_file/2/3</c>. The original
%%% file contents is saved in the target file. The key must have the
%%% the same value as that used for encryption.</p>
decrypt_config_file(EncryptFileName, TargetFileName, KeyOrFile) ->
@@ -977,7 +1081,7 @@ decrypt_config_file(EncryptFileName, TargetFileName, KeyOrFile) ->
%%% given callback module and configuration string. Callback module
%%% should be either loaded or present in the code part. Loaded
%%% configuration variables can later be removed using
-%%% <code>remove_config/2</code> function.</p>
+%%% <c>remove_config/2</c> function.</p>
add_config(Callback, Config)->
ct_config:add_config(Callback, Config).
@@ -1006,9 +1110,9 @@ remove_config(Callback, Config) ->
%%% A = list()
%%%
%%% @doc <p>Use this function to set a new timetrap for the running test case.
-%%% If the argument is <code>Func</code>, the timetrap will be triggered
-%%% when this function returns. <code>Func</code> may also return a new
-%%% <code>Time</code> value, which in that case will be the value for the
+%%% If the argument is <c>Func</c>, the timetrap will be triggered
+%%% when this function returns. <c>Func</c> may also return a new
+%%% <c>Time</c> value, which in that case will be the value for the
%%% new timetrap.</p>
timetrap(Time) ->
test_server:timetrap_cancel(),
@@ -1047,3 +1151,131 @@ sleep({seconds,Ss}) ->
sleep(trunc(Ss * 1000));
sleep(Time) ->
test_server:adjusted_sleep(Time).
+
+%%%-----------------------------------------------------------------
+%%% @spec notify(Name,Data) -> ok
+%%% Name = atom()
+%%% Data = term()
+%%%
+%%% @doc <p>Sends a asynchronous notification of type <c>Name</c> with
+%%% <c>Data</c>to the common_test event manager. This can later be
+%%% caught by any installed event manager. </p>
+%%% @see //stdlib/gen_event
+notify(Name,Data) ->
+ ct_event:notify(Name, Data).
+
+%%%-----------------------------------------------------------------
+%%% @spec sync_notify(Name,Data) -> ok
+%%% Name = atom()
+%%% Data = term()
+%%%
+%%% @doc <p>Sends a synchronous notification of type <c>Name</c> with
+%%% <c>Data</c>to the common_test event manager. This can later be
+%%% caught by any installed event manager. </p>
+%%% @see //stdlib/gen_event
+sync_notify(Name,Data) ->
+ ct_event:sync_notify(Name, Data).
+
+%%%-----------------------------------------------------------------
+%%% @spec break(Comment) -> ok | {error,Reason}
+%%% Comment = string()
+%%% Reason = {multiple_cases_running,TestCases} |
+%%% 'enable break with release_shell option'
+%%% TestCases = [atom()]
+%%%
+%%% @doc <p>This function will cancel any active timetrap and pause the
+%%% execution of the current test case until the user calls the
+%%% <c>continue/0</c> function. It gives the user the opportunity
+%%% to interact with the erlang node running the tests, e.g. for
+%%% debugging purposes or for manually executing a part of the
+%%% test case. If a parallel group is executing, <c>break/2</c>
+%%% should be called instead.</p>
+%%% <p>A cancelled timetrap will not be automatically
+%%% reactivated after the break, but must be started exlicitly with
+%%% <c>ct:timetrap/1</c></p>
+%%% <p>In order for the break/continue functionality to work,
+%%% Common Test must release the shell process controlling stdin.
+%%% This is done by setting the <c>release_shell</c> start option
+%%% to <c>true</c>. See the User's Guide for more information.</p>
+
+break(Comment) ->
+ case {ct_util:get_testdata(starter),
+ ct_util:get_testdata(release_shell)} of
+ {ct,ReleaseSh} when ReleaseSh /= true ->
+ Warning = "ct:break/1 can only be used if release_shell == true.\n",
+ ct_logs:log("Warning!", Warning, []),
+ io:format(user, "Warning! " ++ Warning, []),
+ {error,'enable break with release_shell option'};
+ _ ->
+ case get_testdata(curr_tc) of
+ {ok,{_,_TestCase}} ->
+ test_server:break(?MODULE, Comment);
+ {ok,Cases} when is_list(Cases) ->
+ {error,{'multiple cases running',
+ [TC || {_,TC} <- Cases]}};
+ Error = {error,_} ->
+ Error;
+ Error ->
+ {error,Error}
+ end
+ end.
+
+%%%-----------------------------------------------------------------
+%%% @spec break(TestCase, Comment) -> ok | {error,Reason}
+%%% TestCase = atom()
+%%% Comment = string()
+%%% Reason = 'test case not running' |
+%%% 'enable break with release_shell option'
+%%%
+%%% @doc <p>This function works the same way as <c>break/1</c>,
+%%% only the <c>TestCase</c> argument makes it possible to
+%%% pause a test case executing in a parallel group. The
+%%% <c>continue/1</c> function should be used to resume
+%%% execution of <c>TestCase</c>.</p>
+%%% <p>See <c>break/1</c> for more details.</p>
+break(TestCase, Comment) ->
+ case {ct_util:get_testdata(starter),
+ ct_util:get_testdata(release_shell)} of
+ {ct,ReleaseSh} when ReleaseSh /= true ->
+ Warning = "ct:break/2 can only be used if release_shell == true.\n",
+ ct_logs:log("Warning!", Warning, []),
+ io:format(user, "Warning! " ++ Warning, []),
+ {error,'enable break with release_shell option'};
+ _ ->
+ case get_testdata(curr_tc) of
+ {ok,Cases} when is_list(Cases) ->
+ case lists:keymember(TestCase, 2, Cases) of
+ true ->
+ test_server:break(?MODULE, TestCase, Comment);
+ false ->
+ {error,'test case not running'}
+ end;
+ {ok,{_,TestCase}} ->
+ test_server:break(?MODULE, TestCase, Comment);
+ Error = {error,_} ->
+ Error;
+ Error ->
+ {error,Error}
+ end
+ end.
+
+%%%-----------------------------------------------------------------
+%%% @spec continue() -> ok
+%%%
+%%% @doc <p>This function must be called in order to continue after a
+%%% test case (not executing in a parallel group) has called
+%%% <c>break/1</c>.</p>
+continue() ->
+ test_server:continue().
+
+%%%-----------------------------------------------------------------
+%%% @spec continue(TestCase) -> ok
+%%% TestCase = atom()
+%%%
+%%% @doc <p>This function must be called in order to continue after a
+%%% test case has called <c>break/2</c>. If the paused test case,
+%%% <c>TestCase</c>, executes in a parallel group, this
+%%% function - rather than <c>continue/0</c> - must be used
+%%% in order to let the test case proceed.</p>
+continue(TestCase) ->
+ test_server:continue(TestCase).
diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl
index 9277af5bc1..30bf5925c0 100644
--- a/lib/common_test/src/ct_config.erl
+++ b/lib/common_test/src/ct_config.erl
@@ -1,7 +1,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -122,8 +122,8 @@ return({To,Ref},Result) ->
loop(StartDir) ->
receive
- {{require,Name,Tag,SubTags},From} ->
- Result = do_require(Name,Tag,SubTags),
+ {{require,Name,Key},From} ->
+ Result = do_require(Name,Key),
return(From,Result),
loop(StartDir);
{{set_default_config,{Config,Scope}},From} ->
@@ -168,16 +168,19 @@ reload_config(KeyOrName) ->
call({reload_config, KeyOrName}).
process_default_configs(Opts) ->
- case lists:keysearch(config, 1, Opts) of
- {value,{_,Files=[File|_]}} when is_list(File) ->
- Files;
- {value,{_,File=[C|_]}} when is_integer(C) ->
- [File];
- {value,{_,[]}} ->
- [];
- false ->
- []
- end.
+ lists:flatmap(fun({config,[_|_] = FileOrFiles}) ->
+ case {io_lib:printable_list(FileOrFiles),
+ io_lib:printable_list(hd(FileOrFiles))} of
+ {true,true} ->
+ FileOrFiles;
+ {true,false} ->
+ [FileOrFiles];
+ _ ->
+ []
+ end;
+ (_) ->
+ []
+ end,Opts).
process_user_configs(Opts, Acc) ->
case lists:keytake(userconfig, 1, Opts) of
@@ -319,75 +322,58 @@ get_config(KeyOrName,Default) ->
get_config(KeyOrName,Default,[]).
get_config(KeyOrName,Default,Opts) when is_atom(KeyOrName) ->
- case lookup_config(KeyOrName) of
- [] ->
- Default;
- [{_Ref,Val}|_] = Vals ->
- case {lists:member(all,Opts),lists:member(element,Opts)} of
- {true,true} ->
- [{KeyOrName,V} || {_R,V} <- lists:sort(Vals)];
- {true,false} ->
- [V || {_R,V} <- lists:sort(Vals)];
- {false,true} ->
- {KeyOrName,Val};
- {false,false} ->
- Val
- end
+ case get_config({KeyOrName}, Default, Opts) of
+ %% If only an atom is given, then we need to unwrap the
+ %% key if it is returned
+ {{KeyOrName}, Val} ->
+ {KeyOrName, Val};
+ [{{KeyOrName}, _Val}|_] = Res ->
+ [{K, Val} || {{K},Val} <- Res, K == KeyOrName];
+ Else ->
+ Else
end;
-get_config({KeyOrName,SubKey},Default,Opts) ->
- case lookup_config(KeyOrName) of
+%% This useage of get_config is only used by internal ct functions
+%% and may change at any time
+get_config({DeepKey,SubKey}, Default, Opts) when is_tuple(DeepKey) ->
+ get_config(erlang:append_element(DeepKey, SubKey), Default, Opts);
+get_config(KeyOrName,Default,Opts) when is_tuple(KeyOrName) ->
+ case lookup_config(element(1,KeyOrName)) of
[] ->
- Default;
+ format_value([Default],KeyOrName,Opts);
Vals ->
- Vals1 = case [Val || {_Ref,Val} <- lists:sort(Vals)] of
- Result=[L|_] when is_list(L) ->
- case L of
- [{_,_}|_] ->
- Result;
- _ ->
- []
- end;
- _ ->
- []
- end,
- case get_subconfig([SubKey],Vals1,[],Opts) of
- {ok,[{_,SubVal}|_]=SubVals} ->
- case {lists:member(all,Opts),lists:member(element,Opts)} of
- {true,true} ->
- [{{KeyOrName,SubKey},Val} || {_,Val} <- SubVals];
- {true,false} ->
- [Val || {_SubKey,Val} <- SubVals];
- {false,true} ->
- {{KeyOrName,SubKey},SubVal};
- {false,false} ->
- SubVal
- end;
- _ ->
- Default
- end
+ NewVals =
+ lists:map(
+ fun({Val}) ->
+ get_config(tl(tuple_to_list(KeyOrName)),
+ Val,Default,Opts)
+ end,Vals),
+ format_value(NewVals,KeyOrName,Opts)
end.
-get_subconfig(SubKeys,Values) ->
- get_subconfig(SubKeys,Values,[],[]).
-
-get_subconfig(SubKeys,[Value|Rest],Mapped,Opts) ->
- case do_get_config(SubKeys,Value,[]) of
- {ok,SubMapped} ->
- case lists:member(all,Opts) of
- true ->
- get_subconfig(SubKeys,Rest,Mapped++SubMapped,Opts);
- false ->
- {ok,SubMapped}
- end;
- _Error ->
- get_subconfig(SubKeys,Rest,Mapped,Opts)
+get_config([],Vals,_Default,_Opts) ->
+ Vals;
+get_config([[]],Vals,Default,Opts) ->
+ get_config([],Vals,Default,Opts);
+%% This case is used by {require,{unix,[port,host]}} functionality
+get_config([SubKeys], Vals, Default, _Opts) when is_list(SubKeys) ->
+ case do_get_config(SubKeys, Vals, []) of
+ {ok, SubVals} ->
+ [SubVal || {_,SubVal} <- SubVals];
+
+ _ ->
+ Default
end;
-get_subconfig(SubKeys,[],[],_) ->
- {error,{not_available,SubKeys}};
-get_subconfig(_SubKeys,[],Mapped,_) ->
- {ok,Mapped}.
+get_config([Key|Rest], Vals, Default, Opts) ->
+ case do_get_config([Key], Vals, []) of
+ {ok, [{Key,NewVals}]} ->
+ get_config(Rest, NewVals, Default, Opts);
+ _ ->
+ Default
+ end.
+do_get_config([Key|_], Available, _Mapped) when not is_list(Available) ->
+ {error,{not_available,Key}};
do_get_config([Key|Required],Available,Mapped) ->
case lists:keysearch(Key,1,Available) of
{value,{Key,Value}} ->
@@ -403,8 +389,7 @@ do_get_config([],_Available,Mapped) ->
get_all_config() ->
ets:select(?attr_table,[{#ct_conf{name='$1',key='$2',value='$3',
default='$4',_='_'},
- [],
- [{{'$1','$2','$3','$4'}}]}]).
+ [],[{{'$1','$2','$3','$4'}}]}]).
lookup_config(KeyOrName) ->
case lookup_name(KeyOrName) of
@@ -415,13 +400,23 @@ lookup_config(KeyOrName) ->
end.
lookup_name(Name) ->
- ets:select(?attr_table,[{#ct_conf{ref='$1',value='$2',name=Name,_='_'},
- [],
- [{{'$1','$2'}}]}]).
+ ets:select(?attr_table,[{#ct_conf{value='$1',name=Name,_='_'},
+ [],[{{'$1'}}]}]).
lookup_key(Key) ->
- ets:select(?attr_table,[{#ct_conf{key=Key,ref='$1',value='$2',name='_UNDEF',_='_'},
- [],
- [{{'$1','$2'}}]}]).
+ ets:select(?attr_table,[{#ct_conf{key=Key,value='$1',name='_UNDEF',_='_'},
+ [],[{{'$1'}}]}]).
+
+format_value([SubVal|_] = SubVals, KeyOrName, Opts) ->
+ case {lists:member(all,Opts),lists:member(element,Opts)} of
+ {true,true} ->
+ [{KeyOrName,Val} || Val <- SubVals];
+ {true,false} ->
+ [Val || Val <- SubVals];
+ {false,true} ->
+ {KeyOrName,SubVal};
+ {false,false} ->
+ SubVal
+ end.
lookup_handler_for_config({Key, _Subkey}) ->
lookup_handler_for_config(Key);
@@ -475,65 +470,78 @@ release_allocated([H|T]) ->
release_allocated([]) ->
ok.
-allocate(Name,Key,SubKeys) ->
- case ets:match_object(?attr_table,#ct_conf{key=Key,name='_UNDEF',_='_'}) of
- [] ->
+allocate(Name,Key) ->
+ Ref = make_ref(),
+ case get_config(Key,Ref,[all,element]) of
+ [{_,Ref}] ->
{error,{not_available,Key}};
- Available ->
- case allocate_subconfig(Name,SubKeys,Available,false) of
- ok ->
- ok;
- Error ->
- Error
- end
+ Configs ->
+ associate(Name,Key,Configs),
+ ok
end.
-allocate_subconfig(Name,SubKeys,[C=#ct_conf{value=Value}|Rest],Found) ->
- case do_get_config(SubKeys,Value,[]) of
- {ok,_SubMapped} ->
- ets:insert(?attr_table,C#ct_conf{name=Name}),
- allocate_subconfig(Name,SubKeys,Rest,true);
- _Error ->
- allocate_subconfig(Name,SubKeys,Rest,Found)
- end;
-allocate_subconfig(_Name,_SubKeys,[],true) ->
+
+associate('_UNDEF',_Key,_Configs) ->
ok;
-allocate_subconfig(_Name,SubKeys,[],false) ->
- {error,{not_available,SubKeys}}.
+associate(Name,{Key,SubKeys},Configs) when is_atom(Key), is_list(SubKeys) ->
+ associate_int(Name,Configs,"true");
+associate(Name,_Key,Configs) ->
+ associate_int(Name,Configs,os:getenv("COMMON_TEST_ALIAS_TOP")).
+
+associate_int(Name,Configs,"true") ->
+ lists:map(fun({K,_Config}) ->
+ Cs = ets:match_object(
+ ?attr_table,
+ #ct_conf{key=element(1,K),
+ name='_UNDEF',_='_'}),
+ [ets:insert(?attr_table,C#ct_conf{name=Name})
+ || C <- Cs]
+ end,Configs);
+associate_int(Name,Configs,_) ->
+ lists:map(fun({K,Config}) ->
+ Key = if is_tuple(K) -> element(1,K);
+ is_atom(K) -> K
+ end,
+
+ Cs = ets:match_object(
+ ?attr_table,
+ #ct_conf{key=Key,
+ name='_UNDEF',_='_'}),
+ [ets:insert(?attr_table,C#ct_conf{name=Name,
+ value=Config})
+ || C <- Cs]
+ end,Configs).
+
+
delete_config(Default) ->
ets:match_delete(?attr_table,#ct_conf{default=Default,_='_'}),
ok.
-require(Key) when is_atom(Key) ->
- require({Key,[]});
-require({Key,SubKeys}) when is_atom(Key) ->
- allocate('_UNDEF',Key,to_list(SubKeys));
+require(Key) when is_atom(Key); is_tuple(Key) ->
+ allocate('_UNDEF',Key);
require(Key) ->
{error,{invalid,Key}}.
-require(Name,Key) when is_atom(Key) ->
- require(Name,{Key,[]});
-require(Name,{Key,SubKeys}) when is_atom(Name), is_atom(Key) ->
- call({require,Name,Key,to_list(SubKeys)});
+require(Name,Key) when is_atom(Name),is_atom(Key) orelse is_tuple(Key) ->
+ call({require,Name,Key});
require(Name,Keys) ->
{error,{invalid,{Name,Keys}}}.
-to_list(X) when is_list(X) -> X;
-to_list(X) -> [X].
-
-do_require(Name,Key,SubKeys) when is_list(SubKeys) ->
+do_require(Name,Key) ->
case get_key_from_name(Name) of
{error,_} ->
- allocate(Name,Key,SubKeys);
+ allocate(Name,Key);
{ok,Key} ->
%% already allocated - check that it has all required subkeys
- Vals = [Val || {_Ref,Val} <- lookup_name(Name)],
- case get_subconfig(SubKeys,Vals) of
- {ok,_SubMapped} ->
- ok;
- Error ->
- Error
+ R = make_ref(),
+ case get_config(Key,R,[]) of
+ R ->
+ {error,{not_available,Key}};
+ {error,_} = Error ->
+ Error;
+ _Error ->
+ ok
end;
{ok,OtherKey} ->
{error,{name_in_use,Name,OtherKey}}
@@ -760,13 +768,13 @@ check_config_files(Configs) ->
end,
lists:keysearch(error, 1, lists:flatten(lists:map(ConfigChecker, Configs))).
-prepare_user_configs([ConfigString|UserConfigs], Acc, new) ->
+prepare_user_configs([CallbackMod|UserConfigs], Acc, new) ->
prepare_user_configs(UserConfigs,
- [{list_to_atom(ConfigString), []}|Acc],
+ [{list_to_atom(CallbackMod),[]}|Acc],
cur);
prepare_user_configs(["and"|UserConfigs], Acc, _) ->
prepare_user_configs(UserConfigs, Acc, new);
-prepare_user_configs([ConfigString|UserConfigs], [{LastMod, LastList}|Acc], cur) ->
+prepare_user_configs([ConfigString|UserConfigs], [{LastMod,LastList}|Acc], cur) ->
prepare_user_configs(UserConfigs,
[{LastMod, [ConfigString|LastList]}|Acc],
cur);
diff --git a/lib/common_test/src/ct_conn_log_h.erl b/lib/common_test/src/ct_conn_log_h.erl
new file mode 100644
index 0000000000..bf27238121
--- /dev/null
+++ b/lib/common_test/src/ct_conn_log_h.erl
@@ -0,0 +1,230 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(ct_conn_log_h).
+
+%%%
+%%% A handler that can be connected to the error_logger event
+%%% handler. Writes all ct connection events. See comments in
+%%% cth_conn_log for more information.
+%%%
+
+-include("ct_util.hrl").
+
+-export([init/1,
+ handle_event/2, handle_call/2, handle_info/2,
+ terminate/2]).
+
+-record(state, {group_leader,logs=[]}).
+
+-define(WIDTH,80).
+
+%%%-----------------------------------------------------------------
+%%% Callbacks
+init({GL,Logs}) ->
+ open_files(Logs,#state{group_leader=GL}).
+
+open_files([{ConnMod,{LogType,Logs}}|T],State) ->
+ case do_open_files(Logs,[]) of
+ {ok,Fds} ->
+ open_files(T,State#state{logs=[{ConnMod,{LogType,Fds}} |
+ State#state.logs]});
+ Error ->
+ Error
+ end;
+open_files([],State) ->
+ {ok,State}.
+
+
+do_open_files([{Tag,File}|Logs],Acc) ->
+ case file:open(File, [write]) of
+ {ok,Fd} ->
+ do_open_files(Logs,[{Tag,Fd}|Acc]);
+ {error,Reason} ->
+ {error,{could_not_open_log,File,Reason}}
+ end;
+do_open_files([],Acc) ->
+ {ok,lists:reverse(Acc)}.
+
+handle_event({_Type, GL, _Msg}, State) when node(GL) /= node() ->
+ {ok, State};
+handle_event({_Type,_GL,{Pid,{ct_connection,Action,ConnName},Report}},State) ->
+ Info = conn_info(Pid,#conn_log{name=ConnName,action=Action}),
+ write_report(now(),Info,Report,State),
+ {ok, State};
+handle_event({_Type,_GL,{Pid,Info=#conn_log{},Report}},State) ->
+ write_report(now(),conn_info(Pid,Info),Report,State),
+ {ok, State};
+handle_event({error_report,_,{Pid,_,[{ct_connection,ConnName}|R]}},State) ->
+ %% Error reports from connection
+ write_error(now(),conn_info(Pid,#conn_log{name=ConnName}),R,State),
+ {ok, State};
+handle_event(_, State) ->
+ {ok, State}.
+
+handle_info(_, State) ->
+ {ok, State}.
+
+handle_call(_Query, State) ->
+ {ok, {error, bad_query}, State}.
+
+terminate(_,#state{logs=Logs}) ->
+ [file:close(Fd) || {_,{_,Fds}} <- Logs, {_,Fd} <- Fds],
+ ok.
+
+
+%%%-----------------------------------------------------------------
+%%% Writing reports
+write_report(Time,#conn_log{module=ConnMod}=Info,Data,State) ->
+ {LogType,Fd} = get_log(Info,State),
+ io:format(Fd,"~n~s~s~s",[format_head(ConnMod,LogType,Time),
+ format_title(LogType,Info),
+ format_data(ConnMod,LogType,Data)]).
+
+write_error(Time,#conn_log{module=ConnMod}=Info,Report,State) ->
+ case get_log(Info,State) of
+ {html,_} ->
+ %% The error will anyway be written in the html log by the
+ %% sasl error handler, so don't write it again.
+ ok;
+ {LogType,Fd} ->
+ io:format(Fd,"~n~s~s~s",[format_head(ConnMod,LogType,Time," ERROR"),
+ format_title(LogType,Info),
+ format_error(LogType,Report)])
+ end.
+
+get_log(Info,State) ->
+ case proplists:get_value(Info#conn_log.module,State#state.logs) of
+ {html,_} ->
+ {html,State#state.group_leader};
+ {LogType,Fds} ->
+ {LogType,get_fd(Info,Fds)};
+ undefined ->
+ {html,State#state.group_leader}
+ end.
+
+get_fd(#conn_log{name=undefined},Fds) ->
+ proplists:get_value(default,Fds);
+get_fd(#conn_log{name=ConnName},Fds) ->
+ case proplists:get_value(ConnName,Fds) of
+ undefined ->
+ proplists:get_value(default,Fds);
+ Fd ->
+ Fd
+ end.
+
+%%%-----------------------------------------------------------------
+%%% Formatting
+format_head(ConnMod,LogType,Time) ->
+ format_head(ConnMod,LogType,Time,"").
+
+format_head(ConnMod,raw,Time,Text) ->
+ io_lib:format("~n~p, ~p~s, ",[now_to_time(Time),ConnMod,Text]);
+format_head(ConnMod,_,Time,Text) ->
+ Head = pad_char_end(?WIDTH,pretty_head(now_to_time(Time),ConnMod,Text),$=),
+ io_lib:format("~n~s",[Head]).
+
+format_title(raw,#conn_log{client=Client}=Info) ->
+ io_lib:format("Client ~p ~s ~s",[Client,actionstr(Info),serverstr(Info)]);
+format_title(_,Info) ->
+ Title = pad_char_end(?WIDTH,pretty_title(Info),$=),
+ io_lib:format("~n~s", [Title]).
+
+format_data(_,_,NoData) when NoData == ""; NoData == <<>> ->
+ "";
+format_data(ConnMod,LogType,Data) ->
+ ConnMod:format_data(LogType,Data).
+
+format_error(raw,Report) ->
+ io_lib:format("~n~p~n",[Report]);
+format_error(pretty,Report) ->
+ [io_lib:format("~n ~p: ~p",[K,V]) || {K,V} <- Report].
+
+
+
+
+%%%-----------------------------------------------------------------
+%%% Helpers
+conn_info(LoggingProc, #conn_log{client=undefined} = ConnInfo) ->
+ conn_info(ConnInfo#conn_log{client=LoggingProc});
+conn_info(_, ConnInfo) ->
+ conn_info(ConnInfo).
+
+conn_info(#conn_log{client=Client, module=undefined} = ConnInfo) ->
+ case ets:lookup(ct_connections,Client) of
+ [#conn{address=Address,callback=Callback}] ->
+ ConnInfo#conn_log{address=Address,module=Callback};
+ [] ->
+ ConnInfo
+ end;
+conn_info(ConnInfo) ->
+ ConnInfo.
+
+
+now_to_time({_,_,MicroS}=Now) ->
+ {calendar:now_to_local_time(Now),MicroS}.
+
+pretty_head({{{Y,Mo,D},{H,Mi,S}},MicroS},ConnMod,Text0) ->
+ Text = string:to_upper(atom_to_list(ConnMod) ++ Text0),
+ io_lib:format("= ~s ==== ~s-~s-~p::~s:~s:~s,~s ",
+ [Text,t(D),month(Mo),Y,t(H),t(Mi),t(S),
+ micro2milli(MicroS)]).
+
+pretty_title(#conn_log{client=Client}=Info) ->
+ io_lib:format("= Client ~p ~s Server ~s ",
+ [Client,actionstr(Info),serverstr(Info)]).
+
+actionstr(#conn_log{action=send}) -> "----->";
+actionstr(#conn_log{action=recv}) -> "<-----";
+actionstr(#conn_log{action=open}) -> "opened session to";
+actionstr(#conn_log{action=close}) -> "closed session to";
+actionstr(_) -> "<---->".
+
+serverstr(#conn_log{name=undefined,address=Address}) ->
+ io_lib:format("~p",[Address]);
+serverstr(#conn_log{name=Alias,address=Address}) ->
+ io_lib:format("~p(~p)",[Alias,Address]).
+
+month(1) -> "Jan";
+month(2) -> "Feb";
+month(3) -> "Mar";
+month(4) -> "Apr";
+month(5) -> "May";
+month(6) -> "Jun";
+month(7) -> "Jul";
+month(8) -> "Aug";
+month(9) -> "Sep";
+month(10) -> "Oct";
+month(11) -> "Nov";
+month(12) -> "Dec".
+
+micro2milli(X) ->
+ pad0(3,integer_to_list(X div 1000)).
+
+t(X) ->
+ pad0(2,integer_to_list(X)).
+
+pad0(N,Str) ->
+ M = length(Str),
+ lists:duplicate(N-M,$0) ++ Str.
+
+pad_char_end(N,Str,Char) ->
+ case length(lists:flatten(Str)) of
+ M when M<N -> Str ++ lists:duplicate(N-M,Char);
+ _ -> Str
+ end.
diff --git a/lib/common_test/src/ct_event.erl b/lib/common_test/src/ct_event.erl
index 3e79898ad1..49e0635d79 100644
--- a/lib/common_test/src/ct_event.erl
+++ b/lib/common_test/src/ct_event.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. 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
@@ -31,7 +31,7 @@
%% API
-export([start_link/0, add_handler/0, add_handler/1, stop/0]).
--export([notify/1, sync_notify/1]).
+-export([notify/1, notify/2, sync_notify/1,sync_notify/2]).
-export([is_alive/0]).
%% gen_event callbacks
@@ -90,6 +90,13 @@ notify(Event) ->
end.
%%--------------------------------------------------------------------
+%% Function: notify(Name,Data) -> ok
+%% Description: Asynchronous notification to event manager.
+%%--------------------------------------------------------------------
+notify(Name, Data) ->
+ notify(#event{ name = Name, data = Data}).
+
+%%--------------------------------------------------------------------
%% Function: sync_notify(Event) -> ok
%% Description: Synchronous notification to event manager.
%%--------------------------------------------------------------------
@@ -102,6 +109,13 @@ sync_notify(Event) ->
end.
%%--------------------------------------------------------------------
+%% Function: sync_notify(Name,Data) -> ok
+%% Description: Synchronous notification to event manager.
+%%--------------------------------------------------------------------
+sync_notify(Name,Data) ->
+ sync_notify(#event{ name = Name, data = Data}).
+
+%%--------------------------------------------------------------------
%% Function: is_alive() -> true | false
%% Description: Check if Event Manager is alive.
%%--------------------------------------------------------------------
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index 11575cd0fb..bec3368869 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -27,7 +27,7 @@
-export([init_tc/3, end_tc/3, end_tc/4, get_suite/2, get_all_cases/1]).
-export([report/2, warn/1, error_notification/4]).
--export([get_logopts/0, format_comment/1, get_html_wrapper/3]).
+-export([get_logopts/0, format_comment/1, get_html_wrapper/4]).
-export([error_in_suite/1, init_per_suite/1, end_per_suite/1,
init_per_group/2, end_per_group/2]).
@@ -72,18 +72,25 @@ init_tc(Mod,Func,Config) ->
{Suite,{suite0_failed,_}=Failure} ->
{skip,Failure};
_ ->
- ct_util:set_testdata({curr_tc,{Suite,Func}}),
+ ct_util:update_testdata(curr_tc,
+ fun(undefined) ->
+ [{Suite,Func}];
+ (Running) ->
+ [{Suite,Func}|Running]
+ end, [create]),
case ct_util:read_suite_data({seq,Suite,Func}) of
undefined ->
init_tc1(Mod,Suite,Func,Config);
Seq when is_atom(Seq) ->
case ct_util:read_suite_data({seq,Suite,Seq}) of
[Func|TCs] -> % this is the 1st case in Seq
- %% make sure no cases in this seq are marked as failed
- %% from an earlier execution in the same suite
+ %% make sure no cases in this seq are
+ %% marked as failed from an earlier execution
+ %% in the same suite
lists:foreach(
fun(TC) ->
- ct_util:save_suite_data({seq,Suite,TC},Seq)
+ ct_util:save_suite_data({seq,Suite,TC},
+ Seq)
end, TCs);
_ ->
ok
@@ -204,20 +211,23 @@ init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config) ->
data={Mod,FuncSpec}}),
case catch configure(MergedInfo,MergedInfo,SuiteInfo,
- FuncSpec,Config) of
+ FuncSpec,[],Config) of
{suite0_failed,Reason} ->
- ct_util:set_testdata({curr_tc,{Mod,{suite0_failed,{require,Reason}}}}),
+ ct_util:set_testdata({curr_tc,{Mod,{suite0_failed,
+ {require,Reason}}}}),
{skip,{require_failed_in_suite0,Reason}};
{error,Reason} ->
{auto_skip,{require_failed,Reason}};
{'EXIT',Reason} ->
{auto_skip,Reason};
- {ok,Config1} ->
+ {ok,PostInitHook,Config1} ->
case get('$test_server_framework_test') of
undefined ->
- ct_suite_init(Suite, FuncSpec, Config1);
+ ct_suite_init(Suite, FuncSpec, PostInitHook, Config1);
Fun ->
- case Fun(init_tc, Config1) of
+ PostInitHookResult = do_post_init_hook(PostInitHook,
+ Config1),
+ case Fun(init_tc, [PostInitHookResult ++ Config1]) of
NewConfig when is_list(NewConfig) ->
{ok,NewConfig};
Else ->
@@ -226,14 +236,28 @@ init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config) ->
end
end.
-ct_suite_init(Suite, Func, [Config]) when is_list(Config) ->
+ct_suite_init(Suite, Func, PostInitHook, Config) when is_list(Config) ->
case ct_hooks:init_tc(Suite, Func, Config) of
NewConfig when is_list(NewConfig) ->
- {ok, [NewConfig]};
+ PostInitHookResult = do_post_init_hook(PostInitHook, NewConfig),
+ {ok, [PostInitHookResult ++ NewConfig]};
Else ->
Else
end.
+do_post_init_hook(PostInitHook, Config) ->
+ lists:flatmap(fun({Tag,Fun}) ->
+ case lists:keysearch(Tag,1,Config) of
+ {value,_} ->
+ [];
+ false ->
+ case Fun() of
+ {error,_} -> [];
+ Result -> [{Tag,Result}]
+ end
+ end
+ end, PostInitHook).
+
add_defaults(Mod,Func, GroupPath) ->
Suite = get_suite_name(Mod, GroupPath),
case (catch Suite:suite()) of
@@ -453,15 +477,16 @@ timetrap_first([],Info,[]) ->
timetrap_first([],Info,Found) ->
?rev(Found) ++ ?rev(Info).
-configure([{require,Required}|Rest],Info,SuiteInfo,Scope,Config) ->
+configure([{require,Required}|Rest],
+ Info,SuiteInfo,Scope,PostInitHook,Config) ->
case ct:require(Required) of
ok ->
- configure(Rest,Info,SuiteInfo,Scope,Config);
+ configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config);
Error = {error,Reason} ->
case required_default('_UNDEF',Required,Info,
SuiteInfo,Scope) of
ok ->
- configure(Rest,Info,SuiteInfo,Scope,Config);
+ configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config);
_ ->
case lists:keymember(Required,2,SuiteInfo) of
true ->
@@ -471,14 +496,15 @@ configure([{require,Required}|Rest],Info,SuiteInfo,Scope,Config) ->
end
end
end;
-configure([{require,Name,Required}|Rest],Info,SuiteInfo,Scope,Config) ->
+configure([{require,Name,Required}|Rest],
+ Info,SuiteInfo,Scope,PostInitHook,Config) ->
case ct:require(Name,Required) of
ok ->
- configure(Rest,Info,SuiteInfo,Scope,Config);
+ configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config);
Error = {error,Reason} ->
case required_default(Name,Required,Info,SuiteInfo,Scope) of
ok ->
- configure(Rest,Info,SuiteInfo,Scope,Config);
+ configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config);
_ ->
case lists:keymember(Name,2,SuiteInfo) of
true ->
@@ -488,17 +514,24 @@ configure([{require,Name,Required}|Rest],Info,SuiteInfo,Scope,Config) ->
end
end
end;
-configure([{timetrap,off}|Rest],Info,SuiteInfo,Scope,Config) ->
- configure(Rest,Info,SuiteInfo,Scope,Config);
-configure([{timetrap,Time}|Rest],Info,SuiteInfo,Scope,Config) ->
- Dog = test_server:timetrap(Time),
- configure(Rest,Info,SuiteInfo,Scope,[{watchdog,Dog}|Config]);
-configure([{ct_hooks, Hook} | Rest], Info, SuiteInfo, Scope, Config) ->
- configure(Rest, Info, SuiteInfo, Scope, [{ct_hooks, Hook} | Config]);
-configure([_|Rest],Info,SuiteInfo,Scope,Config) ->
- configure(Rest,Info,SuiteInfo,Scope,Config);
-configure([],_,_,_,Config) ->
- {ok,[Config]}.
+configure([{timetrap,off}|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) ->
+ configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config);
+configure([{timetrap,Time}|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) ->
+ PostInitHook1 =
+ [{watchdog,fun() -> case test_server:get_timetrap_info() of
+ undefined ->
+ test_server:timetrap(Time);
+ _ ->
+ {error,already_set}
+ end
+ end} | PostInitHook],
+ configure(Rest,Info,SuiteInfo,Scope,PostInitHook1,Config);
+configure([{ct_hooks,Hook}|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) ->
+ configure(Rest,Info,SuiteInfo,Scope,PostInitHook,[{ct_hooks,Hook}|Config]);
+configure([_|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) ->
+ configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config);
+configure([],_,_,_,PostInitHook,Config) ->
+ {ok,PostInitHook,Config}.
%% the require element in Info may come from suite/0 and
%% should be scoped 'suite', or come from the group info
@@ -562,10 +595,8 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) ->
%% in case Mod == ct_framework, lookup the suite name
Suite = get_suite_name(Mod, Args),
- case lists:keysearch(watchdog,1,Args) of
- {value,{watchdog,Dog}} -> test_server:timetrap_cancel(Dog);
- false -> ok
- end,
+ test_server:timetrap_cancel(),
+
%% save the testcase process pid so that it can be used
%% to look up the attached trace window later
case ct_util:get_testdata(interpret) of
@@ -633,7 +664,22 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) ->
end,
ct_util:reset_silent_connections(),
-
+
+ %% reset the curr_tc state, or delete this TC from the list of
+ %% executing cases (if in a parallel group)
+ ClearCurrTC = fun(Running = [_,_|_]) ->
+ lists:keydelete(Func,2,Running);
+ ({_,{suite0_failed,_}}) ->
+ undefined;
+ ([{_,CurrTC}]) when CurrTC == Func ->
+ undefined;
+ (undefined) ->
+ undefined;
+ (Unexpected) ->
+ exit({error,{reset_curr_tc,{Mod,Func},Unexpected}})
+ end,
+ ct_util:update_testdata(curr_tc,ClearCurrTC),
+
case FinalResult of
{skip,{sequence_failed,_,_}} ->
%% ct_logs:init_tc is never called for a skipped test case
@@ -1483,6 +1529,12 @@ report(What,Data) ->
end;
tests_done ->
ok;
+ severe_error ->
+ ct_event:sync_notify(#event{name=What,
+ node=node(),
+ data=Data}),
+ ct_util:set_testdata({What,Data}),
+ ok;
tc_start ->
%% Data = {{Suite,Func},LogFileName}
ct_event:sync_notify(#event{name=tc_logfile,
@@ -1634,5 +1686,5 @@ format_comment(Comment) ->
%%%-----------------------------------------------------------------
%%% @spec get_html_wrapper(TestName, PrintLabel, Cwd) -> Header
-get_html_wrapper(TestName, PrintLabel, Cwd) ->
- ct_logs:get_ts_html_wrapper(TestName, PrintLabel, Cwd).
+get_html_wrapper(TestName, PrintLabel, Cwd, TableCols) ->
+ ct_logs:get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols).
diff --git a/lib/common_test/src/ct_ftp.erl b/lib/common_test/src/ct_ftp.erl
index 5db73066a3..8790393b36 100644
--- a/lib/common_test/src/ct_ftp.erl
+++ b/lib/common_test/src/ct_ftp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. 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
@@ -66,6 +66,7 @@
%%% {unix,[{ftp,IpAddr},
%%% {username,Username},
%%% {password,Password}]}.</pre>
+%%% @see ct:require/2
put(KeyOrName,LocalFile,RemoteFile) ->
Fun = fun(Ftp) -> send(Ftp,LocalFile,RemoteFile) end,
open_and_do(KeyOrName,Fun).
@@ -85,6 +86,7 @@ put(KeyOrName,LocalFile,RemoteFile) ->
%%%
%%% <p>The config file must be as for put/3.</p>
%%% @see put/3
+%%% @see ct:require/2
get(KeyOrName,RemoteFile,LocalFile) ->
Fun = fun(Ftp) -> recv(Ftp,RemoteFile,LocalFile) end,
open_and_do(KeyOrName,Fun).
@@ -105,6 +107,10 @@ get(KeyOrName,RemoteFile,LocalFile) ->
%%% simply use <code>Key</code>, the configuration variable name, to
%%% specify the target. Note that a connection that has no associated target
%%% name can only be closed with the handle value.</p>
+%%%
+%%% <p>See <c>ct:require/2</c> for how to create a new <c>Name</c></p>
+%%%
+%%% @see ct:require/2
open(KeyOrName) ->
case ct_util:get_key_from_name(KeyOrName) of
{ok,node} ->
diff --git a/lib/common_test/src/ct_gen_conn.erl b/lib/common_test/src/ct_gen_conn.erl
index 5aab4dd2dd..1f01d84601 100644
--- a/lib/common_test/src/ct_gen_conn.erl
+++ b/lib/common_test/src/ct_gen_conn.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. 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
@@ -27,7 +27,7 @@
-compile(export_all).
-export([start/4, stop/1]).
--export([call/2, do_within_time/2]).
+-export([call/2, call/3, return/2, do_within_time/2]).
-ifdef(debug).
-define(dbg,true).
@@ -39,17 +39,24 @@
name,
address,
init_data,
+ reconnect = true,
+ forward = false,
+ use_existing = true,
+ old = false,
conn_pid,
cb_state,
ct_util_server}).
%%%-----------------------------------------------------------------
-%%% @spec start(Name,Address,InitData,CallbackMod) ->
+%%% @spec start(Address,InitData,CallbackMod,Opts) ->
%%% {ok,Handle} | {error,Reason}
%%% Name = term()
%%% CallbackMod = atom()
%%% InitData = term()
%%% Address = term()
+%%% Opts = [Opt]
+%%% Opt = {name,Name} | {use_existing_connection,boolean()} |
+%%% {reconnect,boolean()} | {forward_messages,boolean()}
%%%
%%% @doc Open a connection and start the generic connection owner process.
%%%
@@ -60,50 +67,67 @@
%%% <code>InitData</code> and returna
%%% <code>{ok,ConnectionPid,State}</code> or
%%% <code>{error,Reason}</code>.</p>
+%%%
+%%% If no name is given, the <code>Name</code> argument in init/3 will
+%%% have the value <code>undefined</code>.
+%%%
+%%% The callback modules must also export
+%%% ```
+%%% handle_msg(Msg,From,State) -> {reply,Reply,State} |
+%%% {noreply,State} |
+%%% {stop,Reply,State}
+%%% terminate(ConnectionPid,State) -> term()
+%%% close(Handle) -> term()
+%%% '''
+%%%
+%%% The <code>close/1</code> callback function is actually a callback
+%%% for ct_util, for closing registered connections when
+%%% ct_util_server is terminated. <code>Handle</code> is the Pid of
+%%% the ct_gen_conn process.
+%%%
+%%% If option <code>reconnect</code> is <code>true</code>, then the
+%%% callback must also export
+%%% ```
+%%% reconnect(Address,State) -> {ok,ConnectionPid,State}
+%%% '''
+%%%
+%%% If option <code>forward_messages</code> is <ocde>true</code>, then
+%%% the callback must also export
+%%% ```
+%%% handle_msg(Msg,State) -> {noreply,State} | {stop,State}
+%%% '''
+%%%
+%%% An old interface still exists. This is used by ct_telnet, ct_ftp
+%%% and ct_ssh. The start function then has an explicit
+%%% <code>Name</code> argument, and no <code>Opts</code> argument. The
+%%% callback must export:
+%%%
+%%% ```
+%%% init(Name,Address,InitData) -> {ok,ConnectionPid,State}
+%%% handle_msg(Msg,State) -> {Reply,State}
+%%% reconnect(Address,State) -> {ok,ConnectionPid,State}
+%%% terminate(ConnectionPid,State) -> term()
+%%% close(Handle) -> term()
+%%% '''
+%%%
+start(Address,InitData,CallbackMod,Opts) when is_list(Opts) ->
+ do_start(Address,InitData,CallbackMod,Opts);
start(Name,Address,InitData,CallbackMod) ->
- case ct_util:does_connection_exist(Name,Address,CallbackMod) of
- {ok,Pid} ->
- log("ct_gen_conn:start","Using existing connection!\n",[]),
- {ok,Pid};
- false ->
- Self = self(),
- Pid = spawn(fun() ->
- init_gen(Self, #gen_opts{callback=CallbackMod,
- name=Name,
- address=Address,
- init_data=InitData})
- end),
- MRef = erlang:monitor(process,Pid),
- receive
- {connected,Pid} ->
- erlang:demonitor(MRef, [flush]),
- ct_util:register_connection(Name,Address,CallbackMod,Pid),
- {ok,Pid};
- {Error,Pid} ->
- receive {'DOWN',MRef,process,_,_} -> ok end,
- Error;
- {'DOWN',MRef,process,_,Reason} ->
- log("ct_gen_conn:start",
- "Connection process died: ~p\n",
- [Reason]),
- {error,{connection_process_died,Reason}}
- end
- end.
-
+ do_start(Address,InitData,CallbackMod,[{name,Name},{old,true}]).
%%%-----------------------------------------------------------------
%%% @spec stop(Handle) -> ok
%%% Handle = handle()
%%%
-%%% @doc Close the telnet connection and stop the process managing it.
+%%% @doc Close the connection and stop the process managing it.
stop(Pid) ->
- call(Pid,stop).
+ call(Pid,stop,5000).
%%%-----------------------------------------------------------------
%%% @spec log(Heading,Format,Args) -> ok
%%%
%%% @doc Log activities on the current connection (tool-internal use only).
-%%% @see ct_logs:log/3
+%%% @see ct_logs:log/3
log(Heading,Format,Args) ->
log(log,[Heading,Format,Args]).
@@ -111,7 +135,7 @@ log(Heading,Format,Args) ->
%%% @spec start_log(Heading) -> ok
%%%
%%% @doc Log activities on the current connection (tool-internal use only).
-%%% @see ct_logs:start_log/1
+%%% @see ct_logs:start_log/1
start_log(Heading) ->
log(start_log,[Heading]).
@@ -119,7 +143,7 @@ start_log(Heading) ->
%%% @spec cont_log(Format,Args) -> ok
%%%
%%% @doc Log activities on the current connection (tool-internal use only).
-%%% @see ct_logs:cont_log/2
+%%% @see ct_logs:cont_log/2
cont_log(Format,Args) ->
log(cont_log,[Format,Args]).
@@ -127,7 +151,7 @@ cont_log(Format,Args) ->
%%% @spec end_log() -> ok
%%%
%%% @doc Log activities on the current connection (tool-internal use only).
-%%% @see ct_logs:end_log/0
+%%% @see ct_logs:end_log/0
end_log() ->
log(end_log,[]).
@@ -148,10 +172,10 @@ do_within_time(Fun,Timeout) ->
Silent = get(silent),
TmpPid = spawn_link(fun() -> put(silent,Silent),
R = Fun(),
- Self ! {self(),R}
+ Self ! {self(),R}
end),
ConnPid = get(conn_pid),
- receive
+ receive
{TmpPid,Result} ->
Result;
{'EXIT',ConnPid,_Reason}=M ->
@@ -159,7 +183,7 @@ do_within_time(Fun,Timeout) ->
exit(TmpPid,kill),
self() ! M,
{error,connection_closed}
- after
+ after
Timeout ->
exit(TmpPid,kill),
receive
@@ -176,12 +200,66 @@ do_within_time(Fun,Timeout) ->
%%%=================================================================
%%% Internal functions
-call(Pid,Msg) ->
+do_start(Address,InitData,CallbackMod,Opts0) ->
+ Opts = check_opts(Opts0,#gen_opts{callback=CallbackMod,
+ address=Address,
+ init_data=InitData}),
+ case ct_util:does_connection_exist(Opts#gen_opts.name,
+ Address,CallbackMod) of
+ {ok,Pid} when Opts#gen_opts.use_existing ->
+ log("ct_gen_conn:start","Using existing connection!\n",[]),
+ {ok,Pid};
+ {ok,Pid} when not Opts#gen_opts.use_existing ->
+ {error,{connection_exists,Pid}};
+ false ->
+ do_start(Opts)
+ end.
+
+do_start(Opts) ->
+ Self = self(),
+ Pid = spawn(fun() -> init_gen(Self, Opts) end),
+ MRef = erlang:monitor(process,Pid),
+ receive
+ {connected,Pid} ->
+ erlang:demonitor(MRef, [flush]),
+ ct_util:register_connection(Opts#gen_opts.name, Opts#gen_opts.address,
+ Opts#gen_opts.callback, Pid),
+ {ok,Pid};
+ {Error,Pid} ->
+ receive {'DOWN',MRef,process,_,_} -> ok end,
+ Error;
+ {'DOWN',MRef,process,_,Reason} ->
+ log("ct_gen_conn:start",
+ "Connection process died: ~p\n",
+ [Reason]),
+ {error,{connection_process_died,Reason}}
+ end.
+
+check_opts(Opts0) ->
+ check_opts(Opts0,#gen_opts{}).
+
+check_opts([{name,Name}|T],Opts) ->
+ check_opts(T,Opts#gen_opts{name=Name});
+check_opts([{reconnect,Bool}|T],Opts) ->
+ check_opts(T,Opts#gen_opts{reconnect=Bool});
+check_opts([{forward_messages,Bool}|T],Opts) ->
+ check_opts(T,Opts#gen_opts{forward=Bool});
+check_opts([{use_existing_connection,Bool}|T],Opts) ->
+ check_opts(T,Opts#gen_opts{use_existing=Bool});
+check_opts([{old,Bool}|T],Opts) ->
+ check_opts(T,Opts#gen_opts{old=Bool});
+check_opts([],Opts) ->
+ Opts.
+
+call(Pid, Msg) ->
+ call(Pid, Msg, infinity).
+
+call(Pid, Msg, Timeout) ->
MRef = erlang:monitor(process,Pid),
Ref = make_ref(),
Pid ! {Msg,{self(),Ref}},
receive
- {Ref, Result} ->
+ {Ref, Result} ->
erlang:demonitor(MRef, [flush]),
case Result of
{retry,_Data} ->
@@ -189,8 +267,15 @@ call(Pid,Msg) ->
Other ->
Other
end;
- {'DOWN',MRef,process,_,Reason} ->
+ {'DOWN',MRef,process,_,Reason} ->
{error,{process_down,Pid,Reason}}
+ after Timeout ->
+ erlang:demonitor(MRef, [flush]),
+ log("ct_gen_conn",
+ "Connection process ~p not responding. Killing now!",
+ [Pid]),
+ exit(Pid, kill),
+ {error,{process_down,Pid,forced_termination}}
end.
return({To,Ref},Result) ->
@@ -198,36 +283,47 @@ return({To,Ref},Result) ->
init_gen(Parent,Opts) ->
process_flag(trap_exit,true),
- CtUtilServer = whereis(ct_util_server),
- link(CtUtilServer),
put(silent,false),
- case catch (Opts#gen_opts.callback):init(Opts#gen_opts.name,
- Opts#gen_opts.address,
- Opts#gen_opts.init_data) of
+ try (Opts#gen_opts.callback):init(Opts#gen_opts.name,
+ Opts#gen_opts.address,
+ Opts#gen_opts.init_data) of
{ok,ConnPid,State} when is_pid(ConnPid) ->
link(ConnPid),
put(conn_pid,ConnPid),
+ CtUtilServer = whereis(ct_util_server),
+ link(CtUtilServer),
Parent ! {connected,self()},
loop(Opts#gen_opts{conn_pid=ConnPid,
cb_state=State,
ct_util_server=CtUtilServer});
{error,Reason} ->
Parent ! {{error,Reason},self()}
+ catch
+ throw:{error,Reason} ->
+ Parent ! {{error,Reason},self()}
end.
loop(Opts) ->
receive
{'EXIT',Pid,Reason} when Pid==Opts#gen_opts.conn_pid ->
- log("Connection down!\nOpening new!","Reason: ~p\nAddress: ~p\n",
- [Reason,Opts#gen_opts.address]),
- case reconnect(Opts) of
- {ok, NewPid, NewState} ->
- link(NewPid),
- put(conn_pid,NewPid),
- loop(Opts#gen_opts{conn_pid=NewPid,cb_state=NewState});
- Error ->
+ case Opts#gen_opts.reconnect of
+ true ->
+ log("Connection down!\nOpening new!",
+ "Reason: ~p\nAddress: ~p\n",
+ [Reason,Opts#gen_opts.address]),
+ case reconnect(Opts) of
+ {ok, NewPid, NewState} ->
+ link(NewPid),
+ put(conn_pid,NewPid),
+ loop(Opts#gen_opts{conn_pid=NewPid,cb_state=NewState});
+ Error ->
+ ct_util:unregister_connection(self()),
+ log("Reconnect failed. Giving up!","Reason: ~p\n",
+ [Error])
+ end;
+ false ->
ct_util:unregister_connection(self()),
- log("Reconnect failed. Giving up!","Reason: ~p\n",[Error])
+ log("Connection closed!","Reason: ~p\n",[Reason])
end;
{'EXIT',Pid,Reason} ->
case Opts#gen_opts.ct_util_server of
@@ -252,24 +348,40 @@ loop(Opts) ->
loop(Opts);
{{retry,{_Error,_Name,_CPid,Msg}}, From} ->
log("Rerunning command","Connection reestablished. Rerunning command...",[]),
- {Return,NewState} =
+ {Return,NewState} =
(Opts#gen_opts.callback):handle_msg(Msg,Opts#gen_opts.cb_state),
return(From, Return),
- loop(Opts#gen_opts{cb_state=NewState});
- {Msg,From={Pid,_Ref}} when is_pid(Pid) ->
- {Return,NewState} =
+ loop(Opts#gen_opts{cb_state=NewState});
+ {Msg,From={Pid,_Ref}} when is_pid(Pid), Opts#gen_opts.old==true ->
+ {Return,NewState} =
(Opts#gen_opts.callback):handle_msg(Msg,Opts#gen_opts.cb_state),
return(From, Return),
- loop(Opts#gen_opts{cb_state=NewState})
+ loop(Opts#gen_opts{cb_state=NewState});
+ {Msg,From={Pid,_Ref}} when is_pid(Pid) ->
+ case (Opts#gen_opts.callback):handle_msg(Msg,From,
+ Opts#gen_opts.cb_state) of
+ {reply,Reply,NewState} ->
+ return(From,Reply),
+ loop(Opts#gen_opts{cb_state=NewState});
+ {noreply,NewState} ->
+ loop(Opts#gen_opts{cb_state=NewState});
+ {stop,Reply,NewState} ->
+ ct_util:unregister_connection(self()),
+ (Opts#gen_opts.callback):terminate(Opts#gen_opts.conn_pid,
+ NewState),
+ return(From,Reply)
+ end;
+ Msg when Opts#gen_opts.forward==true ->
+ case (Opts#gen_opts.callback):handle_msg(Msg,Opts#gen_opts.cb_state) of
+ {noreply,NewState} ->
+ loop(Opts#gen_opts{cb_state=NewState});
+ {stop,NewState} ->
+ ct_util:unregister_connection(self()),
+ (Opts#gen_opts.callback):terminate(Opts#gen_opts.conn_pid,
+ NewState)
+ end
end.
-nozero({ok,S}) when is_list(S) ->
- {ok,[C || C <- S,
- C=/=0,
- C=/=13]};
-nozero(M) ->
- M.
-
reconnect(Opts) ->
(Opts#gen_opts.callback):reconnect(Opts#gen_opts.address,
Opts#gen_opts.cb_state).
@@ -277,10 +389,8 @@ reconnect(Opts) ->
log(Func,Args) ->
case get(silent) of
- true when not ?dbg->
+ true when not ?dbg->
ok;
_ ->
apply(ct_logs,Func,Args)
end.
-
-
diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl
index 0fe6e03079..1bcc63738e 100644
--- a/lib/common_test/src/ct_hooks.erl
+++ b/lib/common_test/src/ct_hooks.erl
@@ -48,7 +48,7 @@
%% @doc Called before any suites are started
-spec init(State :: term()) -> ok |
- {error, Reason :: term()}.
+ {fail, Reason :: term()}.
init(Opts) ->
call(get_new_hooks(Opts, undefined) ++ get_builtin_hooks(Opts),
ok, init, []).
@@ -192,12 +192,12 @@ call([{Hook, call_id, NextFun} | Rest], Config, Meta, Hooks) ->
case lists:keyfind(NewId, #ct_hook_config.id, Hooks) of
false when NextFun =:= undefined ->
{Hooks ++ [NewHook],
- [{NewId, call_init} | Rest]};
+ Rest ++ [{NewId, call_init}]};
ExistingHook when is_tuple(ExistingHook) ->
{Hooks, Rest};
_ ->
{Hooks ++ [NewHook],
- [{NewId, call_init}, {NewId,NextFun} | Rest]}
+ Rest ++ [{NewId, call_init}, {NewId,NextFun}]}
end,
call(resort(NewRest,NewHooks,Meta), Config, Meta, NewHooks)
catch Error:Reason ->
@@ -353,11 +353,10 @@ pos(Id,[_|Rest],Num) ->
pos(Id,Rest,Num+1).
-
catch_apply(M,F,A, Default) ->
try
apply(M,F,A)
- catch error:Reason ->
+ catch _:Reason ->
case erlang:get_stacktrace() of
%% Return the default if it was the CTH module which did not have the function.
[{M,F,A,_}|_] when Reason == undef ->
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 1ccbdc3718..0b7a8bb075 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -28,23 +28,25 @@
-module(ct_logs).
--export([init/1,close/2,init_tc/1,end_tc/1]).
--export([get_log_dir/0,get_log_dir/1]).
--export([log/3,start_log/1,cont_log/2,end_log/0]).
--export([set_stylesheet/2,clear_stylesheet/1]).
--export([add_external_logs/1,add_link/3]).
+-export([init/2, close/2, init_tc/1, end_tc/1]).
+-export([get_log_dir/0, get_log_dir/1]).
+-export([log/3, start_log/1, cont_log/2, end_log/0]).
+-export([set_stylesheet/2, clear_stylesheet/1]).
+-export([add_external_logs/1, add_link/3]).
-export([make_last_run_index/0]).
-export([make_all_suites_index/1,make_all_runs_index/1]).
--export([get_ts_html_wrapper/3]).
--export([xhtml/2, locate_default_css_file/0, make_relative/1]).
+-export([get_ts_html_wrapper/4]).
+-export([xhtml/2, locate_priv_file/1, make_relative/1]).
+-export([insert_javascript/1]).
%% Logging stuff directly from testcase
--export([tc_log/3,tc_log/4,tc_log_async/3,tc_print/3,tc_pal/3,ct_log/3,
- basic_html/0]).
+-export([tc_log/3, tc_log/4, tc_log_async/3, tc_print/3, tc_print/4,
+ tc_pal/3, tc_pal/4, ct_log/3, basic_html/0]).
%% Simulate logger process for use without ct environment running
-export([simulate/0]).
+-include("ct.hrl").
-include("ct_event.hrl").
-include("ct_util.hrl").
-include_lib("kernel/include/file.hrl").
@@ -56,7 +58,6 @@
-define(all_runs_name, "all_runs.html").
-define(index_name, "index.html").
-define(totals_name, "totals.info").
--define(css_default, "ct_default.css").
-define(table_color1,"#ADD8E6").
-define(table_color2,"#E4F0FE").
@@ -79,9 +80,9 @@
%%% started. A new directory named ct_run.&lt;timestamp&gt; is created
%%% and all logs are stored under this directory.</p>
%%%
-init(Mode) ->
+init(Mode, Verbosity) ->
Self = self(),
- Pid = spawn_link(fun() -> logger(Self,Mode) end),
+ Pid = spawn_link(fun() -> logger(Self, Mode, Verbosity) end),
MRef = erlang:monitor(process,Pid),
receive
{started,Pid,Result} ->
@@ -240,7 +241,7 @@ end_tc(TCPid) ->
%%% activity it is. <code>Format</code> and <code>Args</code> is the
%%% data to log (as in <code>io:format(Format,Args)</code>).</p>
log(Heading,Format,Args) ->
- cast({log,sync,self(),group_leader(),
+ cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE,
[{int_header(),[log_timestamp(now()),Heading]},
{Format,Args},
{int_footer(),[]}]}),
@@ -262,7 +263,7 @@ log(Heading,Format,Args) ->
%%% @see cont_log/2
%%% @see end_log/0
start_log(Heading) ->
- cast({log,sync,self(),group_leader(),
+ cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE,
[{int_header(),[log_timestamp(now()),Heading]}]}),
ok.
@@ -277,7 +278,8 @@ cont_log([],[]) ->
ok;
cont_log(Format,Args) ->
maybe_log_timestamp(),
- cast({log,sync,self(),group_leader(),[{Format,Args}]}),
+ cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE,
+ [{Format,Args}]}),
ok.
%%%-----------------------------------------------------------------
@@ -288,7 +290,8 @@ cont_log(Format,Args) ->
%%% @see start_log/1
%%% @see cont_log/2
end_log() ->
- cast({log,sync,self(),group_leader(),[{int_footer(), []}]}),
+ cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE,
+ [{int_footer(), []}]}),
ok.
@@ -321,10 +324,16 @@ add_link(Heading,File,Type) ->
[filename:join("log_private",File),Type,File]).
-
%%%-----------------------------------------------------------------
%%% @spec tc_log(Category,Format,Args) -> ok
+%%% @equiv tc_log(Category,?STD_IMPORTANCE,Format,Args)
+tc_log(Category,Format,Args) ->
+ tc_log(Category,?STD_IMPORTANCE,Format,Args).
+
+%%%-----------------------------------------------------------------
+%%% @spec tc_log(Category,Importance,Format,Args) -> ok
%%% Category = atom()
+%%% Importance = integer()
%%% Format = string()
%%% Args = list()
%%%
@@ -333,19 +342,26 @@ add_link(Heading,File,Type) ->
%%% <p>This function is called by <code>ct</code> when logging
%%% stuff directly from a testcase (i.e. not from within the CT
%%% framework).</p>
-tc_log(Category,Format,Args) ->
- tc_log(Category,"User",Format,Args).
+tc_log(Category,Importance,Format,Args) ->
+ tc_log(Category,Importance,"User",Format,Args).
-tc_log(Category,Printer,Format,Args) ->
- cast({log,sync,self(),group_leader(),[{div_header(Category,Printer),[]},
- {Format,Args},
- {div_footer(),[]}]}),
+tc_log(Category,Importance,Printer,Format,Args) ->
+ cast({log,sync,self(),group_leader(),Category,Importance,
+ [{div_header(Category,Printer),[]},
+ {Format,Args},
+ {div_footer(),[]}]}),
ok.
-
%%%-----------------------------------------------------------------
%%% @spec tc_log_async(Category,Format,Args) -> ok
+%%% @equiv tc_log_async(Category,?STD_IMPORTANCE,Format,Args)
+tc_log_async(Category,Format,Args) ->
+ tc_log_async(Category,?STD_IMPORTANCE,Format,Args).
+
+%%%-----------------------------------------------------------------
+%%% @spec tc_log_async(Category,Importance,Format,Args) -> ok
%%% Category = atom()
+%%% Importance = integer()
%%% Format = string()
%%% Args = list()
%%%
@@ -356,40 +372,66 @@ tc_log(Category,Printer,Format,Args) ->
%%% to avoid deadlocks when e.g. the hook that handles SASL printouts
%%% prints to the test case log file at the same time test server
%%% asks ct_logs for an html wrapper.</p>
-tc_log_async(Category,Format,Args) ->
- cast({log,async,self(),group_leader(),[{div_header(Category),[]},
- {Format,Args},
- {div_footer(),[]}]}),
+tc_log_async(Category,Importance,Format,Args) ->
+ cast({log,async,self(),group_leader(),Category,Importance,
+ [{div_header(Category),[]},
+ {Format,Args},
+ {div_footer(),[]}]}),
ok.
+%%%-----------------------------------------------------------------
+%%% @spec tc_print(Category,Format,Args)
+%%% @equiv tc_print(Category,?STD_IMPORTANCE,Format,Args)
+tc_print(Category,Format,Args) ->
+ tc_print(Category,?STD_IMPORTANCE,Format,Args).
%%%-----------------------------------------------------------------
-%%% @spec tc_print(Category,Format,Args) -> ok
+%%% @spec tc_print(Category,Importance,Format,Args) -> ok
%%% Category = atom()
+%%% Importance = integer()
%%% Format = string()
%%% Args = list()
%%%
%%% @doc Console printout from a testcase.
%%%
%%% <p>This function is called by <code>ct</code> when printing
-%%% stuff a testcase on the user console.</p>
-tc_print(Category,Format,Args) ->
- Head = get_heading(Category),
- io:format(user, lists:concat([Head,Format,"\n\n"]), Args),
- ok.
+%%% stuff from a testcase on the user console.</p>
+tc_print(Category,Importance,Format,Args) ->
+ VLvl = case ct_util:get_testdata({verbosity,Category}) of
+ undefined ->
+ ct_util:get_testdata({verbosity,'$unspecified'});
+ {error,bad_invocation} ->
+ ?MAX_VERBOSITY;
+ Val ->
+ Val
+ end,
+ if Importance >= (100-VLvl) ->
+ Head = get_heading(Category),
+ io:format(user, lists:concat([Head,Format,"\n\n"]), Args),
+ ok;
+ true ->
+ ok
+ end.
get_heading(default) ->
- io_lib:format("-----------------------------"
+ io_lib:format("\n-----------------------------"
"-----------------------\n~s\n",
[log_timestamp(now())]);
get_heading(Category) ->
- io_lib:format("-----------------------------"
+ io_lib:format("\n-----------------------------"
"-----------------------\n~s ~w\n",
[log_timestamp(now()),Category]).
%%%-----------------------------------------------------------------
%%% @spec tc_pal(Category,Format,Args) -> ok
+%%% @equiv tc_pal(Category,?STD_IMPORTANCE,Format,Args) -> ok
+tc_pal(Category,Format,Args) ->
+ tc_pal(Category,?STD_IMPORTANCE,Format,Args).
+
+%%%-----------------------------------------------------------------
+%%% @spec tc_pal(Category,Importance,Format,Args) -> ok
%%% Category = atom()
+%%% Importance = integer()
%%% Format = string()
%%% Args = list()
%%%
@@ -398,16 +440,17 @@ get_heading(Category) ->
%%% <p>This function is called by <code>ct</code> when logging
%%% stuff directly from a testcase. The info is written both in the
%%% log and on the console.</p>
-tc_pal(Category,Format,Args) ->
- tc_print(Category,Format,Args),
- cast({log,sync,self(),group_leader(),[{div_header(Category),[]},
- {Format,Args},
- {div_footer(),[]}]}),
+tc_pal(Category,Importance,Format,Args) ->
+ tc_print(Category,Importance,Format,Args),
+ cast({log,sync,self(),group_leader(),Category,Importance,
+ [{div_header(Category),[]},
+ {Format,Args},
+ {div_footer(),[]}]}),
ok.
%%%-----------------------------------------------------------------
-%%% @spec tc_pal(Category,Format,Args) -> ok
+%%% @spec ct_pal(Category,Format,Args) -> ok
%%% Category = atom()
%%% Format = string()
%%% Args = list()
@@ -445,7 +488,7 @@ maybe_log_timestamp() ->
{MS,S,_} ->
ok;
_ ->
- cast({log,sync,self(),group_leader(),
+ cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE,
[{"<i>~s</i>",[log_timestamp({MS,S,US})]}]})
end.
@@ -469,7 +512,7 @@ log_timestamp({MS,S,US}) ->
stylesheet,
async_print_jobs}).
-logger(Parent,Mode) ->
+logger(Parent, Mode, Verbosity) ->
register(?MODULE,self()),
%%! Below is a temporary workaround for the limitation of
@@ -502,26 +545,27 @@ logger(Parent,Mode) ->
%% dir) so logs are independent of Common Test installation
{ok,Cwd} = file:get_cwd(),
CTPath = code:lib_dir(common_test),
- CSSFileSrc = filename:join(filename:join(CTPath, "priv"),
- ?css_default),
- CSSFileDestTop = filename:join(Cwd, ?css_default),
- CSSFileDestRun = filename:join(AbsDir, ?css_default),
- case file:copy(CSSFileSrc, CSSFileDestTop) of
- {error,Reason0} ->
+ PrivFiles = [?css_default,?jquery_script,?tablesorter_script],
+ PrivFilesSrc = [filename:join(filename:join(CTPath, "priv"), F) ||
+ F <- PrivFiles],
+ PrivFilesDestTop = [filename:join(Cwd, F) || F <- PrivFiles],
+ PrivFilesDestRun = [filename:join(AbsDir, F) || F <- PrivFiles],
+ case copy_priv_files(PrivFilesSrc, PrivFilesDestTop) of
+ {error,Src1,Dest1,Reason1} ->
io:format(user, "ERROR! "++
- "CSS file ~p could not be copied to ~p. "++
- "Reason: ~p~n",
- [CSSFileSrc,CSSFileDestTop,Reason0]),
- exit({css_file_error,CSSFileDestTop});
- _ ->
- case file:copy(CSSFileSrc, CSSFileDestRun) of
- {error,Reason1} ->
+ "Priv file ~p could not be copied to ~p. "++
+ "Reason: ~p~n",
+ [Src1,Dest1,Reason1]),
+ exit({priv_file_error,Dest1});
+ ok ->
+ case copy_priv_files(PrivFilesSrc, PrivFilesDestRun) of
+ {error,Src2,Dest2,Reason2} ->
io:format(user, "ERROR! "++
- "CSS file ~p could not be copied to ~p. "++
- "Reason: ~p~n",
- [CSSFileSrc,CSSFileDestRun,Reason1]),
- exit({css_file_error,CSSFileDestRun});
- _ ->
+ "Priv file ~p could not be copied to ~p. "++
+ "Reason: ~p~n",
+ [Src2,Dest2,Reason2]),
+ exit({priv_file_error,Dest2});
+ ok ->
ok
end
end
@@ -541,6 +585,23 @@ logger(Parent,Mode) ->
[log_timestamp(now()),"Common Test Logger started"]),
Parent ! {started,self(),{Time,filename:absname("")}},
set_evmgr_gl(CtLogFd),
+
+ %% save verbosity levels in dictionary for fast lookups
+ io:format(CtLogFd, "\nVERBOSITY LEVELS:\n", []),
+ case proplists:get_value('$unspecified', Verbosity) of
+ undefined -> ok;
+ GenLvl -> io:format(CtLogFd, "~-25s~3w~n",
+ ["general level",GenLvl])
+ end,
+ [begin put({verbosity,Cat},VLvl),
+ if Cat == '$unspecified' ->
+ ok;
+ true ->
+ io:format(CtLogFd, "~-25w~3w~n", [Cat,VLvl])
+ end
+ end || {Cat,VLvl} <- Verbosity],
+ io:nl(CtLogFd),
+
logger_loop(#logger_state{parent=Parent,
log_dir=AbsDir,
start_time=Time,
@@ -549,31 +610,58 @@ logger(Parent,Mode) ->
tc_groupleaders=[],
async_print_jobs=[]}).
+copy_priv_files([SrcF | SrcFs], [DestF | DestFs]) ->
+ case file:copy(SrcF, DestF) of
+ {error,Reason} ->
+ {error,SrcF,DestF,Reason};
+ _ ->
+ copy_priv_files(SrcFs, DestFs)
+ end;
+copy_priv_files([], []) ->
+ ok.
+
logger_loop(State) ->
receive
- {log,SyncOrAsync,Pid,GL,List} ->
- case get_groupleader(Pid, GL, State) of
- {tc_log,TCGL,TCGLs} ->
- case erlang:is_process_alive(TCGL) of
- true ->
- State1 = print_to_log(SyncOrAsync, Pid, TCGL,
- List, State),
- logger_loop(State1#logger_state{tc_groupleaders =
- TCGLs});
- false ->
- %% Group leader is dead, so write to the
- %% CtLog instead
- Fd = State#logger_state.ct_log_fd,
- [begin io:format(Fd,Str,Args),io:nl(Fd) end ||
+ {log,SyncOrAsync,Pid,GL,Category,Importance,List} ->
+ VLvl = case Category of
+ ct_internal ->
+ ?MAX_VERBOSITY;
+ _ ->
+ case get({verbosity,Category}) of
+ undefined -> get({verbosity,'$unspecified'});
+ Val -> Val
+ end
+ end,
+ if Importance >= (100-VLvl) ->
+ case get_groupleader(Pid, GL, State) of
+ {tc_log,TCGL,TCGLs} ->
+ case erlang:is_process_alive(TCGL) of
+ true ->
+ State1 = print_to_log(SyncOrAsync, Pid,
+ TCGL, List, State),
+ logger_loop(State1#logger_state{
+ tc_groupleaders = TCGLs});
+ false ->
+ %% Group leader is dead, so write to the
+ %% CtLog instead
+ Fd = State#logger_state.ct_log_fd,
+ [begin io:format(Fd,Str,Args),
+ io:nl(Fd) end || {Str,Args} <- List],
+ logger_loop(State)
+ end;
+ {ct_log,Fd,TCGLs} ->
+ [begin io:format(Fd,Str,Args),io:nl(Fd) end ||
{Str,Args} <- List],
- logger_loop(State)
+ logger_loop(State#logger_state{
+ tc_groupleaders = TCGLs})
end;
- {ct_log,Fd,TCGLs} ->
- [begin io:format(Fd,Str,Args),io:nl(Fd) end ||
- {Str,Args} <- List],
- logger_loop(State#logger_state{tc_groupleaders = TCGLs})
- end;
+ true ->
+ logger_loop(State)
+ end;
{{init_tc,TCPid,GL,RefreshLog},From} ->
+ %% make sure no IO for this test case from the
+ %% CT logger gets rejected
+ test_server:permit_io(GL, self()),
print_style(GL, State#logger_state.stylesheet),
set_evmgr_gl(GL),
TCGLs = add_tc_gl(TCPid,GL,State),
@@ -659,13 +747,24 @@ create_io_fun(FromPid, State) ->
print_to_log(sync, FromPid, TCGL, List, State) ->
IoFun = create_io_fun(FromPid, State),
- io:format(TCGL, "~s", [lists:foldl(IoFun, [], List)]),
+ %% in some situations (exceptions), the printout is made from the
+ %% test server IO process and there's no valid group leader to send to
+ IoProc = if FromPid /= TCGL -> TCGL;
+ true -> State#logger_state.ct_log_fd
+ end,
+ io:format(IoProc, "~s", [lists:foldl(IoFun, [], List)]),
State;
print_to_log(async, FromPid, TCGL, List, State) ->
IoFun = create_io_fun(FromPid, State),
+ %% in some situations (exceptions), the printout is made from the
+ %% test server IO process and there's no valid group leader to send to
+ IoProc = if FromPid /= TCGL -> TCGL;
+ true -> State#logger_state.ct_log_fd
+ end,
Printer = fun() ->
- io:format(TCGL, "~s", [lists:foldl(IoFun, [], List)])
+ test_server:permit_io(IoProc, self()),
+ io:format(IoProc, "~s", [lists:foldl(IoFun, [], List)])
end,
case State#logger_state.async_print_jobs of
[] ->
@@ -770,7 +869,7 @@ set_evmgr_gl(GL) ->
open_ctlog() ->
{ok,Fd} = file:open(?ct_log_name,[write]),
- io:format(Fd, header("Common Test Framework Log"), []),
+ io:format(Fd, header("Common Test Framework Log", {[],[1,2],[]}), []),
case file:consult(ct_run:variables_file_name("../")) of
{ok,Vars} ->
io:format(Fd, config_table(Vars), []);
@@ -1080,14 +1179,14 @@ total_row(Success, Fail, UserSkip, AutoSkip, NotBuilt, All) ->
integer_to_list(UserSkip),integer_to_list(AutoSkip)}
end,
[xhtml("<tr valign=top>\n",
- ["<tr class=\"",odd_or_even(),"\">\n"]),
+ ["</tbody>\n<tfoot>\n<tr class=\"",odd_or_even(),"\">\n"]),
"<td><b>Total</b></td>\n", Label, TimestampCell,
"<td align=right><b>",integer_to_list(Success),"<b></td>\n",
"<td align=right><b>",integer_to_list(Fail),"<b></td>\n",
"<td align=right>",integer_to_list(AllSkip),
" (",UserSkipStr,"/",AutoSkipStr,")</td>\n",
"<td align=right><b>",integer_to_list(NotBuilt),"<b></td>\n",
- AllInfo, "</tr>\n"].
+ AllInfo, "</tr>\n</tfoot>\n"].
not_built(_BaseName,_LogDir,_All,[]) ->
0;
@@ -1144,10 +1243,12 @@ index_header(Label, StartTime) ->
Head =
case Label of
undefined ->
- header("Test Results", format_time(StartTime));
+ header("Test Results", format_time(StartTime),
+ {[],[1],[2,3,4,5]});
_ ->
header("Test Results for '" ++ Label ++ "'",
- format_time(StartTime))
+ format_time(StartTime),
+ {[],[1],[2,3,4,5]})
end,
[Head |
["<center>\n",
@@ -1159,15 +1260,17 @@ index_header(Label, StartTime) ->
"\">COMMON TEST FRAMEWORK LOG</a>\n</div>"]),
xhtml("<br>\n", "<br /><br /><br />\n"),
xhtml(["<table border=\"3\" cellpadding=\"5\" "
- "bgcolor=\"",?table_color3,"\">\n"], "<table>\n"),
+ "bgcolor=\"",?table_color3,"\">\n"],
+ ["<table id=\"",?sortable_table_name,"\">\n",
+ "<thead>\n<tr>\n"]),
"<th><b>Test Name</b></th>\n",
xhtml(["<th><font color=\"",?table_color3,"\">_</font>Ok"
"<font color=\"",?table_color3,"\">_</font></th>\n"],
"<th>Ok</th>\n"),
"<th>Failed</th>\n",
"<th>Skipped", xhtml("<br>", "<br />"), "(User/Auto)</th>\n"
- "<th>Missing", xhtml("<br>", "<br />"), "Suites</th>\n"
- "\n"]].
+ "<th>Missing", xhtml("<br>", "<br />"), "Suites</th>\n",
+ xhtml("", "</tr>\n</thead>\n<tbody>\n")]].
all_suites_index_header() ->
{ok,Cwd} = file:get_cwd(),
@@ -1180,12 +1283,14 @@ all_suites_index_header(IndexDir) ->
AllRunsLink = xhtml(["<a href=\"",?all_runs_name,"\">",AllRuns,"</a>\n"],
["<div id=\"button_holder\" class=\"btn\">\n"
"<a href=\"",?all_runs_name,"\">",AllRuns,"</a>\n</div>"]),
- [header("Test Results") |
+ [header("Test Results", {[3],[1,2,8,9,10],[4,5,6,7]}) |
["<center>\n",
AllRunsLink,
xhtml("<br><br>\n", "<br /><br />\n"),
xhtml(["<table border=\"3\" cellpadding=\"5\" "
- "bgcolor=\"",?table_color2,"\">\n"], "<table>\n"),
+ "bgcolor=\"",?table_color2,"\">\n"],
+ ["<table id=\"",?sortable_table_name,"\">\n",
+ "<thead>\n<tr>\n"]),
"<th>Test Name</th>\n",
"<th>Label</th>\n",
"<th>Test Run Started</th>\n",
@@ -1198,7 +1303,7 @@ all_suites_index_header(IndexDir) ->
"<th>Node</th>\n",
"<th>CT Log</th>\n",
"<th>Old Runs</th>\n",
- "\n"]].
+ xhtml("", "</tr>\n</thead>\n<tbody>\n")]].
all_runs_header() ->
{ok,Cwd} = file:get_cwd(),
@@ -1210,10 +1315,12 @@ all_runs_header() ->
"<a href=\"",?index_name,
"\">TEST INDEX PAGE</a>\n</div>"]),
xhtml("<br>\n", "<br /><br />\n")],
- [header(Title) |
+ [header(Title, {[1],[2,3,5],[4,6,7,8,9,10]}) |
["<center>\n", IxLink,
xhtml(["<table border=\"3\" cellpadding=\"5\" "
- "bgcolor=\"",?table_color1,"\">\n"], "<table>\n"),
+ "bgcolor=\"",?table_color1,"\">\n"],
+ ["<table id=\"",?sortable_table_name,"\">\n",
+ "<thead>\n<tr>\n"]),
"<th><b>History</b></th>\n"
"<th><b>Node</b></th>\n"
"<th><b>Label</b></th>\n"
@@ -1225,23 +1332,29 @@ all_runs_header() ->
"<th>Ok</th>\n"),
"<th>Failed</th>\n"
"<th>Skipped<br>(User/Auto)</th>\n"
- "<th>Missing<br>Suites</th>\n"
- "\n"]].
+ "<th>Missing<br>Suites</th>\n",
+ xhtml("", "</tr>\n</thead>\n<tbody>\n")]].
-header(Title) ->
- header1(Title, "").
-header(Title, SubTitle) ->
- header1(Title, SubTitle).
+header(Title, TableCols) ->
+ header1(Title, "", TableCols).
+header(Title, SubTitle, TableCols) ->
+ header1(Title, SubTitle, TableCols).
-header1(Title, SubTitle) ->
+header1(Title, SubTitle, TableCols) ->
SubTitleHTML = if SubTitle =/= "" ->
["<center>\n",
"<h3>" ++ SubTitle ++ "</h3>\n",
xhtml("</center>\n<br>\n", "</center>\n<br />\n")];
- true -> xhtml("<br>\n", "<br />\n")
+ true -> xhtml("<br>", "<br />")
end,
CSSFile = xhtml(fun() -> "" end,
- fun() -> make_relative(locate_default_css_file()) end),
+ fun() -> make_relative(locate_priv_file(?css_default)) end),
+ JQueryFile =
+ xhtml(fun() -> "" end,
+ fun() -> make_relative(locate_priv_file(?jquery_script)) end),
+ TableSorterFile =
+ xhtml(fun() -> "" end,
+ fun() -> make_relative(locate_priv_file(?tablesorter_script)) end),
[xhtml(["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
"<html>\n"],
["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n",
@@ -1252,7 +1365,17 @@ header1(Title, SubTitle) ->
"<title>" ++ Title ++ " " ++ SubTitle ++ "</title>\n",
"<meta http-equiv=\"cache-control\" content=\"no-cache\">\n",
xhtml("",
- ["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">"]),
+ ["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">\n"]),
+ xhtml("",
+ ["<script type=\"text/javascript\" src=\"",JQueryFile,
+ "\"></script>\n"]),
+ xhtml("",
+ ["<script type=\"text/javascript\" src=\"",TableSorterFile,
+ "\"></script>\n"]),
+ xhtml(fun() -> "" end,
+ fun() -> insert_javascript({tablesorter,?sortable_table_name,
+ TableCols})
+ end),
"</head>\n",
body_tag(),
"<center>\n",
@@ -1264,6 +1387,10 @@ index_footer() ->
["</table>\n"
"</center>\n" | footer()].
+all_runs_index_footer() ->
+ ["</tbody>\n</table>\n"
+ "</center>\n" | footer()].
+
footer() ->
["<center>\n",
xhtml("<br><br>\n<hr>\n", "<br /><br />\n"),
@@ -1275,7 +1402,8 @@ footer() ->
xhtml("<br>\n", "<br />\n"),
xhtml("</font></p>\n", "</div>\n"),
"</center>\n"
- "</body>\n"].
+ "</body>\n"
+ "</html>\n"].
body_tag() ->
@@ -1291,7 +1419,7 @@ current_time() ->
format_time({{Y, Mon, D}, {H, Min, S}}) ->
Weekday = weekday(calendar:day_of_the_week(Y, Mon, D)),
- lists:flatten(io_lib:format("~s ~s ~p ~w ~2.2.0w:~2.2.0w:~2.2.0w",
+ lists:flatten(io_lib:format("~s ~s ~2.2.0w ~w ~2.2.0w:~2.2.0w:~2.2.0w",
[Weekday, month(Mon), D, Y, H, Min, S])).
weekday(1) -> "Mon";
@@ -1417,8 +1545,12 @@ config_table_header() ->
[
xhtml(["<h2>Configuration</h2>\n"
"<table border=\"3\" cellpadding=\"5\" bgcolor=\"",?table_color1,"\"\n"],
- "<h4>CONFIGURATION</h4>\n<table>\n"),
- "<tr><th>Key</th><th>Value</th></tr>\n"].
+ ["<h4>CONFIGURATION</h4>\n",
+ "<table id=\"",?sortable_table_name,"\">\n",
+ "<thead>\n"]),
+ "<tr><th>Key</th><th>Value</th></tr>\n",
+ xhtml("", "</thead>\n<tbody>\n")
+ ].
config_table1([{Key,Value}|Vars]) ->
[xhtml(["<tr><td>", atom_to_list(Key), "</td>\n",
@@ -1428,7 +1560,7 @@ config_table1([{Key,Value}|Vars]) ->
"<td>", io_lib:format("~p",[Value]), "</td>\n</tr>\n"]) |
config_table1(Vars)];
config_table1([]) ->
- ["</table>\n"].
+ ["</tbody>\n</table>\n"].
make_all_runs_index(When) ->
@@ -1442,7 +1574,8 @@ make_all_runs_index(When) ->
DirsSorted = (catch sort_all_runs(Dirs)),
Header = all_runs_header(),
Index = [runentry(Dir) || Dir <- DirsSorted],
- Result = file:write_file(AbsName,Header++Index++index_footer()),
+ Result = file:write_file(AbsName,Header++Index++
+ all_runs_index_footer()),
if When == start -> ok;
true -> io:put_chars("done\n")
end,
@@ -1981,7 +2114,7 @@ simulate() ->
simulate_logger_loop() ->
receive
- {log,_,_,_,List} ->
+ {log,_,_,_,_,_,List} ->
S = [[io_lib:format(Str,Args),io_lib:nl()] || {Str,Args} <- List],
io:format("~s",[S]),
simulate_logger_loop();
@@ -2078,34 +2211,34 @@ basic_html() ->
end.
%%%-----------------------------------------------------------------
-%%% @spec locate_default_css_file() -> CSSFile
+%%% @spec locate_priv_file(FileName) -> PrivFile
%%%
%%% @doc
%%%
-locate_default_css_file() ->
+locate_priv_file(FileName) ->
{ok,CWD} = file:get_cwd(),
- CSSFileInCwd = filename:join(CWD, ?css_default),
- case filelib:is_file(CSSFileInCwd) of
+ PrivFileInCwd = filename:join(CWD, FileName),
+ case filelib:is_file(PrivFileInCwd) of
true ->
- CSSFileInCwd;
+ PrivFileInCwd;
false ->
- CSSResultFile =
+ PrivResultFile =
case {whereis(?MODULE),self()} of
{Self,Self} ->
%% executed on the ct_logs process
- filename:join(get(ct_run_dir), ?css_default);
+ filename:join(get(ct_run_dir), FileName);
_ ->
%% executed on other process than ct_logs
{ok,RunDir} = get_log_dir(true),
- filename:join(RunDir, ?css_default)
+ filename:join(RunDir, FileName)
end,
- case filelib:is_file(CSSResultFile) of
+ case filelib:is_file(PrivResultFile) of
true ->
- CSSResultFile;
+ PrivResultFile;
false ->
%% last resort, try use css file in CT installation
CTPath = code:lib_dir(common_test),
- filename:join(filename:join(CTPath, "priv"), ?css_default)
+ filename:join(filename:join(CTPath, "priv"), FileName)
end
end.
@@ -2144,7 +2277,7 @@ make_relative1(DirTs, CwdTs) ->
%%%
%%% @doc
%%%
-get_ts_html_wrapper(TestName, PrintLabel, Cwd) ->
+get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols) ->
TestName1 = if is_list(TestName) ->
lists:flatten(TestName);
true ->
@@ -2204,17 +2337,36 @@ get_ts_html_wrapper(TestName, PrintLabel, Cwd) ->
"Open Telecom Platform</a><br />\n",
"Updated: <!date>", current_time(), "<!/date>",
"<br />\n</div>\n"],
- CSSFile = xhtml(fun() -> "" end,
- fun() -> make_relative(locate_default_css_file(), Cwd) end),
+ CSSFile =
+ xhtml(fun() -> "" end,
+ fun() -> make_relative(locate_priv_file(?css_default),
+ Cwd)
+ end),
+ JQueryFile =
+ xhtml(fun() -> "" end,
+ fun() -> make_relative(locate_priv_file(?jquery_script),
+ Cwd)
+ end),
+ TableSorterFile =
+ xhtml(fun() -> "" end,
+ fun() -> make_relative(locate_priv_file(?tablesorter_script),
+ Cwd)
+ end),
+ TableSorterScript =
+ xhtml(fun() -> "" end,
+ fun() -> insert_javascript({tablesorter,
+ ?sortable_table_name,
+ TableCols}) end),
{xhtml,
["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n",
"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n",
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n",
"<head>\n<title>", TestName1, "</title>\n",
"<meta http-equiv=\"cache-control\" content=\"no-cache\">\n",
- "<link rel=\"stylesheet\" href=\"", CSSFile, "\" type=\"text/css\">",
- "</head>\n","<body>\n",
- LabelStr, "\n"],
+ "<link rel=\"stylesheet\" href=\"", CSSFile, "\" type=\"text/css\">\n",
+ "<script type=\"text/javascript\" src=\"", JQueryFile, "\"></script>\n",
+ "<script type=\"text/javascript\" src=\"", TableSorterFile, "\"></script>\n"] ++
+ TableSorterScript ++ ["</head>\n","<body>\n", LabelStr, "\n"],
["<center>\n<br /><hr /><p>\n",
"<a href=\"", AllRuns,
"\">Test run history\n</a> | ",
@@ -2222,3 +2374,89 @@ get_ts_html_wrapper(TestName, PrintLabel, Cwd) ->
"\">Top level test index\n</a>\n</p>\n",
Copyright,"</center>\n</body>\n</html>\n"]}
end.
+
+insert_javascript({tablesorter,_TableName,undefined}) ->
+ [];
+
+insert_javascript({tablesorter,TableName,
+ {DateCols,TextCols,ValCols}}) ->
+ Headers =
+ lists:flatten(
+ lists:sort(
+ lists:flatmap(fun({Sorter,Cols}) ->
+ [lists:flatten(
+ io_lib:format(" ~w: "
+ "{ sorter: '~s' },\n",
+ [Col-1,Sorter])) || Col<-Cols]
+ end, [{"CTDateSorter",DateCols},
+ {"CTTextSorter",TextCols},
+ {"CTValSorter",ValCols}]))),
+ Headers1 = string:substr(Headers, 1, length(Headers)-2),
+
+ ["<script type=\"text/javascript\">\n",
+ "// Parser for date format, e.g: Wed Jul 4 2012 11:24:15\n",
+ "var monthNames = {};\n",
+ "monthNames[\"Jan\"] = \"01\"; monthNames[\"Feb\"] = \"02\";\n",
+ "monthNames[\"Mar\"] = \"03\"; monthNames[\"Apr\"] = \"04\";\n",
+ "monthNames[\"May\"] = \"05\"; monthNames[\"Jun\"] = \"06\";\n",
+ "monthNames[\"Jul\"] = \"07\"; monthNames[\"Aug\"] = \"08\";\n",
+ "monthNames[\"Sep\"] = \"09\"; monthNames[\"Oct\"] = \"10\";\n",
+ "monthNames[\"Nov\"] = \"11\"; monthNames[\"Dec\"] = \"12\";\n",
+ "$.tablesorter.addParser({\n",
+ " id: 'CTDateSorter',\n",
+ " is: function(s) {\n",
+ " return false; },\n",
+ " format: function(s) {\n",
+ %% place empty cells, "-" and "?" at the bottom
+ " if (s.length < 2) return 999999999;\n",
+ " else {\n",
+ %% match out each date element
+ " var date = s.match(/(\\w{3})\\s(\\w{3})\\s(\\d{2})\\s(\\d{4})\\s(\\d{2}):(\\d{2}):(\\d{2})/);\n",
+ " var y = date[4]; var mo = monthNames[date[2]]; var d = String(date[3]);\n",
+ " var h = String(date[5]); var mi = String(date[6]); var sec = String(date[7]);\n",
+ " return (parseInt('' + y + mo + d + h + mi + sec)); }},\n",
+ " type: 'numeric' });\n",
+
+ "// Parser for general text format\n",
+ "$.tablesorter.addParser({\n",
+ " id: 'CTTextSorter',\n",
+ " is: function(s) {\n",
+ " return false; },\n",
+ " format: function(s) {\n",
+ %% place empty cells, "?" and "-" at the bottom
+ " if (s.length < 1) return 'zzzzzzzz';\n",
+ " else if (s == \"?\") return 'zzzzzzz';\n",
+ " else if (s == \"-\") return 'zzzzzz';\n",
+ " else if (s == \"FAILED\") return 'A';\n",
+ " else if (s == \"SKIPPED\") return 'B';\n",
+ " else if (s == \"OK\") return 'C';\n",
+ " else return '' + s; },\n",
+ " type: 'text' });\n",
+
+ "// Parser for numerical values\n",
+ "$.tablesorter.addParser({\n",
+ " id: 'CTValSorter',\n",
+ " is: function(s) {\n",
+ " return false; },\n",
+ " format: function(s) {\n"
+ %% place empty cells and "?" at the bottom
+ " if (s.length < 1) return '-2';\n",
+ " else if (s == \"?\") return '-1';\n",
+ %% look for skip value, eg "3 (2/1)"
+ " else if ((s.search(/(\\d{1,})\\s/)) >= 0) {\n",
+ " var num = s.match(/(\\d{1,})\\s/);\n",
+ %% return only the total skip value for sorting
+ " return (parseInt('' + num[1])); }\n",
+ " else if ((s.search(/(\\d{1,})\\.(\\d{3})s/)) >= 0) {\n",
+ " var num = s.match(/(\\d{1,})\\.(\\d{3})/);\n",
+ " if (num[1] == \"0\") return (parseInt('' + num[2]));\n",
+ " else return (parseInt('' + num[1] + num[2])); }\n",
+ " else return '' + s; },\n",
+ " type: 'numeric' });\n",
+
+ "$(document).ready(function() {\n",
+ " $(\"#",TableName,"\").tablesorter({\n",
+ " headers: { \n", Headers1, "\n }\n });\n",
+ " $(\"#",TableName,"\").trigger(\"update\");\n",
+ " $(\"#",TableName,"\").trigger(\"appendCache\");\n",
+ "});\n</script>\n"].
diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl
index 042c5ba267..99bec3ea09 100644
--- a/lib/common_test/src/ct_master.erl
+++ b/lib/common_test/src/ct_master.erl
@@ -696,8 +696,9 @@ status(MasterPid,Event) ->
log(To,Heading,Str,Args) ->
if To == all ; To == tty ->
- Str1 = ["=== ",Heading," ===\n",io_lib:format(Str,Args),"\n"],
- io:format(Str1,[]);
+ Chars = ["=== ",Heading," ===\n",
+ io_lib:format(Str,Args),"\n"],
+ io:put_chars(Chars);
true ->
ok
end,
diff --git a/lib/common_test/src/ct_master_logs.erl b/lib/common_test/src/ct_master_logs.erl
index 2a951bc5cf..d76288feef 100644
--- a/lib/common_test/src/ct_master_logs.erl
+++ b/lib/common_test/src/ct_master_logs.erl
@@ -26,6 +26,8 @@
-export([start/2, make_all_runs_index/0, log/3, nodedir/2,
stop/0]).
+-include("ct_util.hrl").
+
-record(state, {log_fd, start_time, logdir, rundir,
nodedir_ix_fd, nodes, nodedirs=[]}).
@@ -33,7 +35,6 @@
-define(all_runs_name, "master_runs.html").
-define(nodedir_index_name, "index.html").
-define(details_file_name,"details.info").
--define(css_default, "ct_default.css").
-define(table_color,"lightblue").
%%%--------------------------------------------------------------------
@@ -95,29 +96,30 @@ init(Parent,LogDir,Nodes) ->
put(basic_html, true);
BasicHtml ->
put(basic_html, BasicHtml),
- %% copy stylesheet to log dir (both top dir and test run
+ %% copy priv files to log dir (both top dir and test run
%% dir) so logs are independent of Common Test installation
CTPath = code:lib_dir(common_test),
- CSSFileSrc = filename:join(filename:join(CTPath, "priv"),
- ?css_default),
- CSSFileDestTop = filename:join(LogDir, ?css_default),
- CSSFileDestRun = filename:join(RunDirAbs, ?css_default),
- case file:copy(CSSFileSrc, CSSFileDestTop) of
- {error,Reason0} ->
+ PrivFiles = [?css_default,?jquery_script,?tablesorter_script],
+ PrivFilesSrc = [filename:join(filename:join(CTPath, "priv"), F) ||
+ F <- PrivFiles],
+ PrivFilesDestTop = [filename:join(LogDir, F) || F <- PrivFiles],
+ PrivFilesDestRun = [filename:join(RunDirAbs, F) || F <- PrivFiles],
+ case copy_priv_files(PrivFilesSrc, PrivFilesDestTop) of
+ {error,Src1,Dest1,Reason1} ->
io:format(user, "ERROR! "++
- "CSS file ~p could not be copied to ~p. "++
+ "Priv file ~p could not be copied to ~p. "++
"Reason: ~p~n",
- [CSSFileSrc,CSSFileDestTop,Reason0]),
- exit({css_file_error,CSSFileDestTop});
- _ ->
- case file:copy(CSSFileSrc, CSSFileDestRun) of
- {error,Reason1} ->
+ [Src1,Dest1,Reason1]),
+ exit({priv_file_error,Dest1});
+ ok ->
+ case copy_priv_files(PrivFilesSrc, PrivFilesDestRun) of
+ {error,Src2,Dest2,Reason2} ->
io:format(user, "ERROR! "++
- "CSS file ~p could not be copied to ~p. "++
+ "Priv file ~p could not be copied to ~p. "++
"Reason: ~p~n",
- [CSSFileSrc,CSSFileDestRun,Reason1]),
- exit({css_file_error,CSSFileDestRun});
- _ ->
+ [Src2,Dest2,Reason2]),
+ exit({priv_file_error,Dest2});
+ ok ->
ok
end
end
@@ -132,7 +134,7 @@ init(Parent,LogDir,Nodes) ->
io:format(CtLogFd,int_header(),[log_timestamp(now()),"Test Nodes\n"]),
io:format(CtLogFd,"~s\n",[NodeStr]),
- io:format(CtLogFd,int_footer()++"\n",[]),
+ io:put_chars(CtLogFd,[int_footer(),"\n"]),
NodeDirIxFd = open_nodedir_index(RunDirAbs,Time),
Parent ! {started,self(),{Time,RunDirAbs}},
@@ -146,6 +148,16 @@ init(Parent,LogDir,Nodes) ->
{N,""}
end,Nodes)}).
+copy_priv_files([SrcF | SrcFs], [DestF | DestFs]) ->
+ case file:copy(SrcF, DestF) of
+ {error,Reason} ->
+ {error,SrcF,DestF,Reason};
+ _ ->
+ copy_priv_files(SrcFs, DestFs)
+ end;
+copy_priv_files([], []) ->
+ ok.
+
loop(State) ->
receive
{log,_From,List} ->
@@ -190,24 +202,21 @@ loop(State) ->
open_ct_master_log(Dir) ->
FullName = filename:join(Dir,?ct_master_log_name),
{ok,Fd} = file:open(FullName,[write]),
- io:format(Fd,header("Common Test Master Log"),[]),
+ io:put_chars(Fd,header("Common Test Master Log", {[],[1,2],[]})),
%% maybe add config info here later
- io:format(Fd, config_table([]), []),
- io:format(Fd,
- "<style>\n"
- "div.ct_internal { background:lightgrey; color:black }\n"
- "div.default { background:lightgreen; color:black }\n"
- "</style>\n",
- []),
- io:format(Fd,
- xhtml("<br><h2>Progress Log</h2>\n<pre>\n",
- "<br /><h2>Progress Log</h2>\n<pre>\n"),
- []),
+ io:put_chars(config_table([])),
+ io:put_chars(Fd,
+ "<style>\n"
+ "div.ct_internal { background:lightgrey; color:black }\n"
+ "div.default { background:lightgreen; color:black }\n"
+ "</style>\n"),
+ io:put_chars(Fd,
+ xhtml("<br><h2>Progress Log</h2>\n<pre>\n",
+ "<br /><h2>Progress Log</h2>\n<pre>\n")),
Fd.
close_ct_master_log(Fd) ->
- io:format(Fd,"</pre>",[]),
- io:format(Fd,footer(),[]),
+ io:put_chars(Fd,["</pre>",footer()]),
file:close(Fd).
config_table(Vars) ->
@@ -216,11 +225,14 @@ config_table(Vars) ->
config_table_header() ->
["<h2>Configuration</h2>\n",
xhtml(["<table border=\"3\" cellpadding=\"5\" "
- "bgcolor=\"",?table_color,"\"\n"], "<table>\n"),
- "<tr><th>Key</th><th>Value</th></tr>\n"].
+ "bgcolor=\"",?table_color,"\"\n"],
+ ["<table id=\"",?sortable_table_name,"\">\n",
+ "<thead>\n"]),
+ "<tr><th>Key</th><th>Value</th></tr>\n",
+ xhtml("", "</thead>\n<tbody>\n")].
config_table1([]) ->
- ["</table>\n"].
+ ["</tbody>\n</table>\n"].
int_header() ->
"<div class=\"ct_internal\"><b>*** CT MASTER ~s *** ~s</b>".
@@ -233,31 +245,33 @@ int_footer() ->
open_nodedir_index(Dir,StartTime) ->
FullName = filename:join(Dir,?nodedir_index_name),
{ok,Fd} = file:open(FullName,[write]),
- io:format(Fd,nodedir_index_header(StartTime),[]),
+ io:put_chars(Fd,nodedir_index_header(StartTime)),
Fd.
print_nodedir(Node,RunDir,Fd) ->
Index = filename:join(RunDir,"index.html"),
- io:format(Fd,
- ["<tr>\n"
- "<td align=center>",atom_to_list(Node),"</td>\n",
- "<td align=left><a href=\"",Index,"\">",Index,"</a></td>\n",
- "</tr>\n"],[]),
+ io:put_chars(Fd,
+ ["<tr>\n"
+ "<td align=center>",atom_to_list(Node),"</td>\n",
+ "<td align=left><a href=\"",Index,"\">",Index,"</a></td>\n",
+ "</tr>\n"]),
ok.
close_nodedir_index(Fd) ->
- io:format(Fd,index_footer(),[]),
+ io:put_chars(Fd,index_footer()),
file:close(Fd).
nodedir_index_header(StartTime) ->
- [header("Log Files " ++ format_time(StartTime)) |
+ [header("Log Files " ++ format_time(StartTime), {[],[1,2],[]}) |
["<center>\n",
"<p><a href=\"",?ct_master_log_name,"\">Common Test Master Log</a></p>",
xhtml(["<table border=\"3\" cellpadding=\"5\" "
- "bgcolor=\"",?table_color,"\">\n"], "<table>\n"),
+ "bgcolor=\"",?table_color,"\">\n"],
+ ["<table id=\"",?sortable_table_name,"\">\n",
+ "<thead>\n<tr>\n"]),
"<th><b>Node</b></th>\n",
"<th><b>Log</b></th>\n",
- "\n"]].
+ xhtml("", "</tr>\n</thead>\n<tbody>\n")]].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% All Run Index functions %%%
@@ -315,14 +329,16 @@ runentry(Dir) ->
"</tr>\n"].
all_runs_header() ->
- [header("Master Test Runs") |
+ [header("Master Test Runs", {[1],[2,3],[]}) |
["<center>\n",
xhtml(["<table border=\"3\" cellpadding=\"5\" "
- "bgcolor=\"",?table_color,"\">\n"], "<table>\n"),
+ "bgcolor=\"",?table_color,"\">\n"],
+ ["<table id=\"",?sortable_table_name,"\">\n",
+ "<thead>\n<tr>\n"]),
"<th><b>History</b></th>\n"
"<th><b>Master Host</b></th>\n"
- "<th><b>Test Nodes</b></th>\n"
- "\n"]].
+ "<th><b>Test Nodes</b></th>\n",
+ xhtml("", "</tr></thead>\n<tbody>\n")]].
timestamp(Dir) ->
[S,Min,H,D,M,Y|_] = lists:reverse(string:tokens(Dir,".-_")),
@@ -346,9 +362,16 @@ read_details_file(Dir) ->
%%% Internal functions
%%%--------------------------------------------------------------------
-header(Title) ->
+header(Title, TableCols) ->
CSSFile = xhtml(fun() -> "" end,
- fun() -> make_relative(locate_default_css_file()) end),
+ fun() -> make_relative(locate_priv_file(?css_default)) end),
+ JQueryFile =
+ xhtml(fun() -> "" end,
+ fun() -> make_relative(locate_priv_file(?jquery_script)) end),
+ TableSorterFile =
+ xhtml(fun() -> "" end,
+ fun() -> make_relative(locate_priv_file(?tablesorter_script)) end),
+
[xhtml(["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
"<html>\n"],
["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n",
@@ -360,6 +383,16 @@ header(Title) ->
"<meta http-equiv=\"cache-control\" content=\"no-cache\">\n",
xhtml("",
["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">"]),
+ xhtml("",
+ ["<script type=\"text/javascript\" src=\"",JQueryFile,
+ "\"></script>\n"]),
+ xhtml("",
+ ["<script type=\"text/javascript\" src=\"",TableSorterFile,
+ "\"></script>\n"]),
+ xhtml(fun() -> "" end,
+ fun() -> ct_logs:insert_javascript({tablesorter,
+ ?sortable_table_name,
+ TableCols}) end),
"</head>\n",
body_tag(),
"<center>\n",
@@ -367,7 +400,7 @@ header(Title) ->
"</center>\n"].
index_footer() ->
- ["</table>\n"
+ ["</tbody>\n</table>\n"
"</center>\n" | footer()].
footer() ->
@@ -393,7 +426,7 @@ current_time() ->
format_time({{Y, Mon, D}, {H, Min, S}}) ->
Weekday = weekday(calendar:day_of_the_week(Y, Mon, D)),
- lists:flatten(io_lib:format("~s ~s ~p ~w ~2.2.0w:~2.2.0w:~2.2.0w",
+ lists:flatten(io_lib:format("~s ~s ~2.2.0w ~w ~2.2.0w:~2.2.0w:~2.2.0w",
[Weekday, month(Mon), D, Y, H, Min, S])).
weekday(1) -> "Mon";
@@ -446,8 +479,8 @@ basic_html() ->
xhtml(HTML, XHTML) ->
ct_logs:xhtml(HTML, XHTML).
-locate_default_css_file() ->
- ct_logs:locate_default_css_file().
+locate_priv_file(File) ->
+ ct_logs:locate_priv_file(File).
make_relative(Dir) ->
ct_logs:make_relative(Dir).
diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl
new file mode 100644
index 0000000000..52fe9599ce
--- /dev/null
+++ b/lib/common_test/src/ct_netconfc.erl
@@ -0,0 +1,1835 @@
+%%----------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%----------------------------------------------------------------------
+%% File: ct_netconfc.erl
+%%
+%% Description:
+%% This file contains the Netconf client interface
+%%
+%% @author Support
+%%
+%% @doc Netconf client module.
+%%
+%% <p>The Netconf client is compliant with RFC4741 and RFC4742.</p>
+%%
+%% <p> For each server to test against, the following entry can be
+%% added to a configuration file:</p>
+%%
+%% <p>`{server_id(),options()}.'</p>
+%%
+%% <p> The `server_id()' or an associated `target_name()' (see
+%% {@link ct}) shall then be used in calls to {@link open/2}.</p>
+%%
+%% <p>If no configuration exists for a server, a session can still be
+%% opened by calling {@link open/2} with all necessary options given
+%% in the call. The first argument to {@link open/2} can then be any
+%% atom.</p>
+%%
+%% == Logging ==
+%%
+%% The netconf server uses the `error_logger' for logging of netconf
+%% traffic. A special purpose error handler is implemented in
+%% `ct_conn_log_h'. To use this error handler, add the `cth_conn_log'
+%% hook in your test suite, e.g.
+%%
+%% ```
+%% suite() ->
+%% [{ct_hooks, [{cth_conn_log, [{conn_mod(),hook_options()}]}]}].
+%%'''
+%%
+%% The `conn_mod()' is the name of the common_test module implementing
+%% the connection protocol, e.g. `ct_netconfc'.
+%%
+%% The hook option `log_type' specifies the type of logging:
+%%
+%% <dl>
+%% <dt>`raw'</dt>
+%% <dd>The sent and received netconf data is logged to a separate
+%% text file as is without any formatting. A link to the file is
+%% added to the test case HTML log.</dd>
+%%
+%% <dt>`pretty'</dt>
+%% <dd>The sent and received netconf data is logged to a separate
+%% text file with XML data nicely indented. A link to the file is
+%% added to the test case HTML log.</dd>
+%%
+%% <dt>`html (default)'</dt>
+%% <dd>The sent and received netconf traffic is pretty printed
+%% directly in the test case HTML log.</dd>
+%%
+%% <dt>`silent'</dt>
+%% <dd>Netconf traffic is not logged.</dd>
+%% </dl>
+%%
+%% By default, all netconf traffic is logged in one single log
+%% file. However, it is possible to have different connections logged
+%% in separate files. To do this, use the hook option `hosts' and
+%% list the names of the servers/connections that will be used in the
+%% suite. Note that the connections must be named for this to work,
+%% i.e. they must be opened with {@link open/2}.
+%%
+%% The `hosts' option has no effect if `log_type' is set to `html' or
+%% `silent'.
+%%
+%% The hook options can also be specified in a configuration file with
+%% the configuration variable `ct_conn_log':
+%%
+%% ```
+%% {ct_conn_log,[{conn_mod(),hook_options()}]}.
+%% '''
+%%
+%% For example:
+%%
+%% ```
+%% {ct_conn_log,[{ct_netconfc,[{log_type,pretty},
+%% {hosts,[key_or_name()]}]}]}
+%% '''
+%%
+%% <b>Note</b> that hook options specified in a configuration file
+%% will overwrite the hardcoded hook options in the test suite.
+%%
+%% === Logging example 1 ===
+%%
+%% The following `ct_hooks' statement will cause pretty printing of
+%% netconf traffic to separate logs for the connections named
+%% `nc_server1' and `nc_server2'. Any other connections will be logged
+%% to default netconf log.
+%%
+%% ```
+%% suite() ->
+%% [{ct_hooks, [{cth_conn_log, [{ct_netconfc,[{log_type,pretty}},
+%% {hosts,[nc_server1,nc_server2]}]}
+%% ]}]}].
+%%'''
+%%
+%% Connections must be opened like this:
+%%
+%% ```
+%% open(nc_server1,[...]),
+%% open(nc_server2,[...]).
+%% '''
+%%
+%% === Logging example 2 ===
+%%
+%% The following configuration file will cause raw logging of all
+%% netconf traffic into one single text file.
+%%
+%% ```
+%% {ct_conn_log,[{ct_netconfc,[{log_type,raw}]}]}.
+%% '''
+%%
+%% The `ct_hooks' statement must look like this:
+%%
+%% ```
+%% suite() ->
+%% [{ct_hooks, [{cth_conn_log, []}]}].
+%% '''
+%%
+%% The same `ct_hooks' statement without the configuration file would
+%% cause HTML logging of all netconf connections into the test case
+%% HTML log.
+%%
+%% == Notifications ==
+%%
+%% The netconf client is also compliant with RFC5277 NETCONF Event
+%% Notifications, which defines a mechanism for an asynchronous
+%% message notification delivery service for the netconf protocol.
+%%
+%% Specific functions to support this are {@link
+%% create_subscription/6} and {@link get_event_streams/3}. (The
+%% functions also exist with other arities.)
+%%
+%% @end
+%%----------------------------------------------------------------------
+-module(ct_netconfc).
+
+-include("ct_netconfc.hrl").
+-include("ct_util.hrl").
+-include_lib("xmerl/include/xmerl.hrl").
+
+%%----------------------------------------------------------------------
+%% External exports
+%%----------------------------------------------------------------------
+-export([open/1,
+ open/2,
+ only_open/1,
+ only_open/2,
+ hello/1,
+ hello/2,
+ close_session/1,
+ close_session/2,
+ kill_session/2,
+ kill_session/3,
+ send/2,
+ send/3,
+ send_rpc/2,
+ send_rpc/3,
+ lock/2,
+ lock/3,
+ unlock/2,
+ unlock/3,
+ get/2,
+ get/3,
+ get_config/3,
+ get_config/4,
+ edit_config/3,
+ edit_config/4,
+ delete_config/2,
+ delete_config/3,
+ copy_config/3,
+ copy_config/4,
+ action/2,
+ action/3,
+ create_subscription/1,
+ create_subscription/2,
+ create_subscription/3,
+ create_subscription/4,
+ create_subscription/5,
+ create_subscription/6,
+ get_event_streams/2,
+ get_event_streams/3,
+ get_capabilities/1,
+ get_capabilities/2,
+ get_session_id/1,
+ get_session_id/2]).
+
+%%----------------------------------------------------------------------
+%% Exported types
+%%----------------------------------------------------------------------
+-export_type([hook_options/0,
+ conn_mod/0,
+ log_type/0,
+ key_or_name/0,
+ notification/0]).
+
+%%----------------------------------------------------------------------
+%% Internal exports
+%%----------------------------------------------------------------------
+%% ct_gen_conn callbacks
+-export([init/3,
+ handle_msg/3,
+ handle_msg/2,
+ terminate/2,
+ close/1]).
+
+%% ct_conn_log callback
+-export([format_data/2]).
+
+%%----------------------------------------------------------------------
+%% Internal defines
+%%----------------------------------------------------------------------
+-define(APPLICATION,?MODULE).
+-define(VALID_SSH_OPTS,[user, password, user_dir]).
+-define(DEFAULT_STREAM,"NETCONF").
+
+-define(error(ConnName,Report),
+ error_logger:error_report([{ct_connection,ConnName},
+ {client,self()},
+ {module,?MODULE},
+ {line,?LINE} |
+ Report])).
+
+-define(is_timeout(T), (is_integer(T) orelse T==infinity)).
+-define(is_filter(F),
+ (is_atom(F) orelse (is_tuple(F) andalso is_atom(element(1,F))))).
+-define(is_string(S), (is_list(S) andalso is_integer(hd(S)))).
+
+%%----------------------------------------------------------------------
+%% Records
+%%----------------------------------------------------------------------
+%% Client state
+-record(state, {host,
+ port,
+ connection, % #connection
+ capabilities,
+ session_id,
+ msg_id = 1,
+ hello_status,
+ buff = <<>>,
+ pending = [], % [#pending]
+ event_receiver}).% pid
+
+%% Run-time client options.
+-record(options, {ssh = [], % Options for the ssh application
+ host,
+ port = ?DEFAULT_PORT,
+ timeout = ?DEFAULT_TIMEOUT,
+ name}).
+
+%% Connection reference
+-record(connection, {reference, % {CM,Ch}
+ host,
+ port,
+ name}).
+
+%% Pending replies from server
+-record(pending, {tref, % timer ref (returned from timer:xxx)
+ ref, % pending ref
+ msg_id,
+ op,
+ caller}).% pid which sent the request
+
+%%----------------------------------------------------------------------
+%% Type declarations
+%%----------------------------------------------------------------------
+-type client() :: handle() | server_id() | target_name().
+-type handle() :: term().
+%% An opaque reference for a connection (netconf session). See {@link
+%% ct} for more information.
+
+-type server_id() :: atom().
+%% A `ServerId' which exists in a configuration file.
+-type target_name() :: atom().
+%% A name which is associated to a `server_id()' via a
+%% `require' statement or a call to {@link ct:require/2} in the
+%% test suite.
+-type key_or_name() :: server_id() | target_name().
+
+-type options() :: [option()].
+%% Options used for setting up ssh connection to a netconf server.
+
+-type option() :: {ssh,host()} | {port,inet:port_number()} | {user,string()} |
+ {password,string()} | {user_dir,string()} |
+ {timeout,timeout()}.
+-type host() :: inet:host_name() | inet:ip_address().
+
+-type notification() :: {notification, xml_attributes(), notification_content()}.
+-type notification_content() :: [event_time()|simple_xml()].
+-type event_time() :: {eventTime,xml_attributes(),[xs_datetime()]}.
+
+-type stream_name() :: string().
+-type streams() :: [{stream_name(),[stream_data()]}].
+-type stream_data() :: {description,string()} |
+ {replaySupport,string()} |
+ {replayLogCreationTime,string()} |
+ {replayLogAgedTime,string()}.
+%% See XML Schema for Event Notifications found in RFC5277 for further
+%% detail about the data format for the string values.
+
+-type hook_options() :: [hook_option()].
+%% Options that can be given to `cth_conn_log' in the `ct_hook' statement.
+-type hook_option() :: {log_type,log_type()} |
+ {hosts,[key_or_name()]}.
+-type log_type() :: raw | pretty | html | silent.
+%-type error_handler() :: module().
+-type conn_mod() :: ct_netconfc.
+
+-type error_reason() :: term().
+
+-type simple_xml() :: {xml_tag(), xml_attributes(), xml_content()} |
+ {xml_tag(), xml_content()} |
+ xml_tag().
+%% <p>This type is further described in the documentation for the
+%% <tt>Xmerl</tt> application.</p>
+-type xml_tag() :: atom().
+-type xml_attributes() :: [{xml_attribute_tag(),xml_attribute_value()}].
+-type xml_attribute_tag() :: atom().
+-type xml_attribute_value() :: string().
+-type xml_content() :: [simple_xml() | iolist()].
+-type xpath() :: {xpath,string()}.
+
+-type netconf_db() :: running | startup | candidate.
+-type xs_datetime() :: string().
+%% This date and time identifyer has the same format as the XML type
+%% dateTime and compliant to RFC3339. The format is
+%% ```[-]CCYY-MM-DDThh:mm:ss[.s][Z|(+|-)hh:mm]'''
+
+%%----------------------------------------------------------------------
+%% External interface functions
+%%----------------------------------------------------------------------
+
+%%----------------------------------------------------------------------
+-spec open(Options) -> Result when
+ Options :: options(),
+ Result :: {ok,handle()} | {error,error_reason()}.
+%% @doc Open a netconf session and exchange `hello' messages.
+%%
+%% If the server options are specified in a configuration file, or if
+%% a named client is needed for logging purposes (see {@section
+%% Logging}) use {@link open/2} instead.
+%%
+%% The opaque `handler()' reference which is returned from this
+%% function is required as client identifier when calling any other
+%% function in this module.
+%%
+%% The `timeout' option (milli seconds) is used when setting up
+%% the ssh connection and when waiting for the hello message from the
+%% server. It is not used for any other purposes during the lifetime
+%% of the connection.
+%%
+%% @end
+%%----------------------------------------------------------------------
+open(Options) ->
+ open(Options,#options{},[],true).
+
+%%----------------------------------------------------------------------
+-spec open(KeyOrName, ExtraOptions) -> Result when
+ KeyOrName :: key_or_name(),
+ ExtraOptions :: options(),
+ Result :: {ok,handle()} | {error,error_reason()}.
+%% @doc Open a named netconf session and exchange `hello' messages.
+%%
+%% If `KeyOrName' is a configured `server_id()' or a
+%% `target_name()' associated with such an ID, then the options
+%% for this server will be fetched from the configuration file.
+%
+%% The `ExtraOptions' argument will be added to the options found in
+%% the configuration file. If the same options are given, the values
+%% from the configuration file will overwrite `ExtraOptions'.
+%%
+%% If the server is not specified in a configuration file, use {@link
+%% open/1} instead.
+%%
+%% The opaque `handle()' reference which is returned from this
+%% function can be used as client identifier when calling any other
+%% function in this module. However, if `KeyOrName' is a
+%% `target_name()', i.e. if the server is named via a call to
+%% `ct:require/2' or a `require' statement in the test
+%% suite, then this name may be used instead of the `handle()'.
+%%
+%% The `timeout' option (milli seconds) is used when setting up
+%% the ssh connection and when waiting for the hello message from the
+%% server. It is not used for any other purposes during the lifetime
+%% of the connection.
+%%
+%% @see ct:require/2
+%% @end
+%%----------------------------------------------------------------------
+open(KeyOrName, ExtraOpts) ->
+ open(KeyOrName, ExtraOpts, true).
+
+open(KeyOrName, ExtraOpts, Hello) ->
+ SortedExtra = lists:keysort(1,ExtraOpts),
+ SortedConfig = lists:keysort(1,ct:get_config(KeyOrName,[])),
+ AllOpts = lists:ukeymerge(1,SortedConfig,SortedExtra),
+ open(AllOpts,#options{name=KeyOrName},[{name,KeyOrName}],Hello).
+
+open(OptList,InitOptRec,NameOpt,Hello) ->
+ case check_options(OptList,undefined,undefined,InitOptRec) of
+ {Host,Port,Options} ->
+ case ct_gen_conn:start({Host,Port},Options,?MODULE,
+ NameOpt ++ [{reconnect,false},
+ {use_existing_connection,false},
+ {forward_messages,true}]) of
+ {ok,Client} when Hello==true ->
+ case hello(Client,Options#options.timeout) of
+ ok ->
+ {ok,Client};
+ Error ->
+ Error
+ end;
+ Other ->
+ Other
+ end;
+ Error ->
+ Error
+ end.
+
+
+%%----------------------------------------------------------------------
+-spec only_open(Options) -> Result when
+ Options :: options(),
+ Result :: {ok,handle()} | {error,error_reason()}.
+%% @doc Open a netconf session, but don't send `hello'.
+%%
+%% As {@link open/1} but does not send a `hello' message.
+%%
+%% @end
+%%----------------------------------------------------------------------
+only_open(Options) ->
+ open(Options,#options{},[],false).
+
+%%----------------------------------------------------------------------
+-spec only_open(KeyOrName,ExtraOptions) -> Result when
+ KeyOrName :: key_or_name(),
+ ExtraOptions :: options(),
+ Result :: {ok,handle()} | {error,error_reason()}.
+%% @doc Open a name netconf session, but don't send `hello'.
+%%
+%% As {@link open/2} but does not send a `hello' message.
+%%
+%% @end
+%%----------------------------------------------------------------------
+only_open(KeyOrName, ExtraOpts) ->
+ open(KeyOrName, ExtraOpts, false).
+
+%%----------------------------------------------------------------------
+%% @spec hello(Client) -> Result
+%% @equiv hello(Client, infinity)
+hello(Client) ->
+ hello(Client,?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec hello(Client,Timeout) -> Result when
+ Client :: handle(),
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Exchange `hello' messages with the server.
+%%
+%% Sends a `hello' message to the server and waits for the return.
+%%
+%% @end
+%%----------------------------------------------------------------------
+hello(Client,Timeout) ->
+ call(Client, {hello, Timeout}).
+
+%%----------------------------------------------------------------------
+%% @spec get_session_id(Client) -> Result
+%% @equiv get_session_id(Client, infinity)
+get_session_id(Client) ->
+ get_session_id(Client, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec get_session_id(Client, Timeout) -> Result when
+ Client :: client(),
+ Timeout :: timeout(),
+ Result :: pos_integer() | {error,error_reason()}.
+%% @doc Returns the session id associated with the given client.
+%%
+%% @end
+%%----------------------------------------------------------------------
+get_session_id(Client, Timeout) ->
+ call(Client, get_session_id, Timeout).
+
+%%----------------------------------------------------------------------
+%% @spec get_capabilities(Client) -> Result
+%% @equiv get_capabilities(Client, infinity)
+get_capabilities(Client) ->
+ get_capabilities(Client, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec get_capabilities(Client, Timeout) -> Result when
+ Client :: client(),
+ Timeout :: timeout(),
+ Result :: [string()] | {error,error_reason()}.
+%% @doc Returns the server side capabilities
+%%
+%% The following capability identifiers, defined in RFC 4741, can be returned:
+%%
+%% <ul>
+%% <li>`"urn:ietf:params:netconf:base:1.0"'</li>
+%% <li>`"urn:ietf:params:netconf:capability:writable-running:1.0"'</li>
+%% <li>`"urn:ietf:params:netconf:capability:candidate:1.0"'</li>
+%% <li>`"urn:ietf:params:netconf:capability:confirmed-commit:1.0"'</li>
+%% <li>`"urn:ietf:params:netconf:capability:rollback-on-error:1.0"'</li>
+%% <li>`"urn:ietf:params:netconf:capability:startup:1.0"'</li>
+%% <li>`"urn:ietf:params:netconf:capability:url:1.0"'</li>
+%% <li>`"urn:ietf:params:netconf:capability:xpath:1.0"'</li>
+%% </ul>
+%%
+%% Note, additional identifiers may exist, e.g. server side namespace.
+%%
+%% @end
+%%----------------------------------------------------------------------
+get_capabilities(Client, Timeout) ->
+ call(Client, get_capabilities, Timeout).
+
+%% @private
+send(Client, SimpleXml) ->
+ send(Client, SimpleXml, ?DEFAULT_TIMEOUT).
+%% @private
+send(Client, SimpleXml, Timeout) ->
+ call(Client,{send, Timeout, SimpleXml}).
+
+%% @private
+send_rpc(Client, SimpleXml) ->
+ send_rpc(Client, SimpleXml, ?DEFAULT_TIMEOUT).
+%% @private
+send_rpc(Client, SimpleXml, Timeout) ->
+ call(Client,{send_rpc, SimpleXml, Timeout}).
+
+
+
+%%----------------------------------------------------------------------
+%% @spec lock(Client, Target) -> Result
+%% @equiv lock(Client, Target, infinity)
+lock(Client, Target) ->
+ lock(Client, Target,?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec lock(Client, Target, Timeout) -> Result when
+ Client :: client(),
+ Target :: netconf_db(),
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Unlock configuration target.
+%%
+%% Which target parameters that can be used depends on if
+%% `:candidate' and/or `:startup' are supported by the
+%% server. If successfull, the configuration system of the device is
+%% not available to other clients (Netconf, CORBA, SNMP etc). Locks
+%% are intended to be short-lived.
+%%
+%% The operations {@link kill_session/2} or {@link kill_session/3} can
+%% be used to force the release of a lock owned by another Netconf
+%% session. How this is achieved by the server side is implementation
+%% specific.
+%%
+%% @end
+%%----------------------------------------------------------------------
+lock(Client, Target, Timeout) ->
+ call(Client,{send_rpc_op,lock,[Target],Timeout}).
+
+%%----------------------------------------------------------------------
+%% @spec unlock(Client, Target) -> Result
+%% @equiv unlock(Client, Target, infinity)
+unlock(Client, Target) ->
+ unlock(Client, Target,?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec unlock(Client, Target, Timeout) -> Result when
+ Client :: client(),
+ Target :: netconf_db(),
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Unlock configuration target.
+%%
+%% If the client earlier has aquired a lock, via {@link lock/2} or
+%% {@link lock/3}, this operation release the associated lock. To be
+%% able to access another target than `running', the server must
+%% support `:candidate' and/or `:startup'.
+%%
+%% @end
+%%----------------------------------------------------------------------
+unlock(Client, Target, Timeout) ->
+ call(Client, {send_rpc_op, unlock, [Target], Timeout}).
+
+%%----------------------------------------------------------------------
+%% @spec get(Client, Filter) -> Result
+%% @equiv get(Client, Filter, infinity)
+get(Client, Filter) ->
+ get(Client, Filter, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec get(Client, Filter, Timeout) -> Result when
+ Client :: client(),
+ Filter :: simple_xml() | xpath(),
+ Timeout :: timeout(),
+ Result :: {ok,simple_xml()} | {error,error_reason()}.
+%% @doc Get data.
+%%
+%% This operation returns both configuration and state data from the
+%% server.
+%%
+%% Filter type `xpath' can only be used if the server supports
+%% `:xpath'.
+%%
+%% @end
+%%----------------------------------------------------------------------
+get(Client, Filter, Timeout) ->
+ call(Client,{send_rpc_op, get, [Filter], Timeout}).
+
+%%----------------------------------------------------------------------
+%% @spec get_config(Client, Source, Filter) -> Result
+%% @equiv get_config(Client, Source, Filter, infinity)
+get_config(Client, Source, Filter) ->
+ get_config(Client, Source, Filter, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec get_config(Client, Source, Filter, Timeout) -> Result when
+ Client :: client(),
+ Source :: netconf_db(),
+ Filter :: simple_xml() | xpath(),
+ Timeout :: timeout(),
+ Result :: {ok,simple_xml()} | {error,error_reason()}.
+%% @doc Get configuration data.
+%%
+%% To be able to access another source than `running', the server
+%% must advertise `:candidate' and/or `:startup'.
+%%
+%% Filter type `xpath' can only be used if the server supports
+%% `:xpath'.
+%%
+%%
+%% @end
+%%----------------------------------------------------------------------
+get_config(Client, Source, Filter, Timeout) ->
+ call(Client, {send_rpc_op, get_config, [Source, Filter], Timeout}).
+
+%%----------------------------------------------------------------------
+%% @spec edit_config(Client, Target, Config) -> Result
+%% @equiv edit_config(Client, Target, Config, infinity)
+edit_config(Client, Target, Config) ->
+ edit_config(Client, Target, Config, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec edit_config(Client, Target, Config, Timeout) -> Result when
+ Client :: client(),
+ Target :: netconf_db(),
+ Config :: simple_xml(),
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Edit configuration data.
+%%
+%% Per default only the running target is available, unless the server
+%% include `:candidate' or `:startup' in its list of
+%% capabilities.
+%%
+%% @end
+%%----------------------------------------------------------------------
+edit_config(Client, Target, Config, Timeout) ->
+ call(Client, {send_rpc_op, edit_config, [Target,Config], Timeout}).
+
+
+%%----------------------------------------------------------------------
+%% @spec delete_config(Client, Target) -> Result
+%% @equiv delete_config(Client, Target, infinity)
+delete_config(Client, Target) ->
+ delete_config(Client, Target, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec delete_config(Client, Target, Timeout) -> Result when
+ Client :: client(),
+ Target :: startup | candidate,
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Delete configuration data.
+%%
+%% The running configuration cannot be deleted and `:candidate'
+%% or `:startup' must be advertised by the server.
+%%
+%% @end
+%%----------------------------------------------------------------------
+delete_config(Client, Target, Timeout) when Target == startup;
+ Target == candidate ->
+ call(Client,{send_rpc_op, delete_config, [Target], Timeout}).
+
+%%----------------------------------------------------------------------
+%% @spec copy_config(Client, Source, Target) -> Result
+%% @equiv copy_config(Client, Source, Target, infinity)
+copy_config(Client, Source, Target) ->
+ copy_config(Client, Source, Target, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec copy_config(Client, Target, Source, Timeout) -> Result when
+ Client :: client(),
+ Target :: netconf_db(),
+ Source :: netconf_db(),
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Copy configuration data.
+%%
+%% Which source and target options that can be issued depends on the
+%% capabilities supported by the server. I.e. `:candidate' and/or
+%% `:startup' are required.
+%%
+%% @end
+%%----------------------------------------------------------------------
+copy_config(Client, Target, Source, Timeout) ->
+ call(Client,{send_rpc_op, copy_config, [Target, Source], Timeout}).
+
+%%----------------------------------------------------------------------
+%% @spec action(Client, Action) -> Result
+%% @equiv action(Client, Action, infinity)
+action(Client,Action) ->
+ action(Client,Action,?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec action(Client, Action, Timeout) -> Result when
+ Client :: client(),
+ Action :: simple_xml(),
+ Timeout :: timeout(),
+ Result :: {ok,simple_xml()} | {error,error_reason()}.
+%% @doc Execute an action.
+%%
+%% @end
+%%----------------------------------------------------------------------
+action(Client,Action,Timeout) ->
+ call(Client,{send_rpc_op, action, [Action], Timeout}).
+
+%%----------------------------------------------------------------------
+create_subscription(Client) ->
+ create_subscription(Client,?DEFAULT_STREAM,?DEFAULT_TIMEOUT).
+
+create_subscription(Client,Timeout)
+ when ?is_timeout(Timeout) ->
+ create_subscription(Client,?DEFAULT_STREAM,Timeout);
+create_subscription(Client,Stream)
+ when is_list(Stream) ->
+ create_subscription(Client,Stream,?DEFAULT_TIMEOUT);
+create_subscription(Client,Filter)
+ when ?is_filter(Filter) ->
+ create_subscription(Client,?DEFAULT_STREAM,Filter,
+ ?DEFAULT_TIMEOUT).
+
+create_subscription(Client,Stream,Timeout)
+ when is_list(Stream) andalso
+ ?is_timeout(Timeout) ->
+ call(Client,{send_rpc_op,{create_subscription,self()},
+ [Stream,undefined,undefined,undefined],
+ Timeout});
+create_subscription(Client,StartTime,StopTime)
+ when is_list(StartTime) andalso
+ is_list(StopTime) ->
+ create_subscription(Client,?DEFAULT_STREAM,StartTime,StopTime,
+ ?DEFAULT_TIMEOUT);
+create_subscription(Client,Filter,Timeout)
+ when ?is_filter(Filter) andalso
+ ?is_timeout(Timeout) ->
+ create_subscription(Client,?DEFAULT_STREAM,Filter,Timeout);
+create_subscription(Client,Stream,Filter)
+ when is_list(Stream) andalso
+ ?is_filter(Filter) ->
+ create_subscription(Client,Stream,Filter,?DEFAULT_TIMEOUT).
+
+create_subscription(Client,StartTime,StopTime,Timeout)
+ when is_list(StartTime) andalso
+ is_list(StopTime) andalso
+ ?is_timeout(Timeout) ->
+ create_subscription(Client,?DEFAULT_STREAM,StartTime,StopTime,Timeout);
+create_subscription(Client,Stream,StartTime,StopTime)
+ when is_list(Stream) andalso
+ is_list(StartTime) andalso
+ is_list(StopTime) ->
+ create_subscription(Client,Stream,StartTime,StopTime,?DEFAULT_TIMEOUT);
+create_subscription(Client,Filter,StartTime,StopTime)
+ when ?is_filter(Filter) andalso
+ is_list(StartTime) andalso
+ is_list(StopTime) ->
+ create_subscription(Client,?DEFAULT_STREAM,Filter,
+ StartTime,StopTime,?DEFAULT_TIMEOUT);
+create_subscription(Client,Stream,Filter,Timeout)
+ when is_list(Stream) andalso
+ ?is_filter(Filter) andalso
+ ?is_timeout(Timeout) ->
+ call(Client,{send_rpc_op,{create_subscription,self()},
+ [Stream,Filter,undefined,undefined],
+ Timeout}).
+
+create_subscription(Client,Stream,StartTime,StopTime,Timeout)
+ when is_list(Stream) andalso
+ is_list(StartTime) andalso
+ is_list(StopTime) andalso
+ ?is_timeout(Timeout) ->
+ call(Client,{send_rpc_op,{create_subscription,self()},
+ [Stream,undefined,StartTime,StopTime],
+ Timeout});
+create_subscription(Client,Stream,Filter,StartTime,StopTime)
+ when is_list(Stream) andalso
+ ?is_filter(Filter) andalso
+ is_list(StartTime) andalso
+ is_list(StopTime) ->
+ create_subscription(Client,Stream,Filter,StartTime,StopTime,?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec create_subscription(Client, Stream, Filter,StartTime, StopTime, Timeout) ->
+ Result when
+ Client :: client(),
+ Stream :: stream_name(),
+ Filter :: simple_xml(),
+ StartTime :: xs_datetime(),
+ StopTime :: xs_datetime(),
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Create a subscription for event notifications.
+%%
+%% This function sets up a subscription for netconf event
+%% notifications of the given stream type, matching the given
+%% filter. The calling process will receive notifications as messages
+%% of type `notification()'.
+%%
+%% <dl>
+%% <dt>Stream:</dt>
+%% <dd> An optional parameter that indicates which stream of events
+%% is of interest. If not present, events in the default NETCONF
+%% stream will be sent.</dd>
+%%
+%% <dt>Filter:</dt>
+%% <dd>An optional parameter that indicates which subset of all
+%% possible events is of interest. The format of this parameter is
+%% the same as that of the filter parameter in the NETCONF protocol
+%% operations. If not present, all events not precluded by other
+%% parameters will be sent. See section 3.6 for more information on
+%% filters.</dd>
+%%
+%% <dt>StartTime:</dt>
+%% <dd>An optional parameter used to trigger the replay feature and
+%% indicate that the replay should start at the time specified. If
+%% `StartTime' is not present, this is not a replay subscription.
+%% It is not valid to specify start times that are later than the
+%% current time. If the `StartTime' specified is earlier than the
+%% log can support, the replay will begin with the earliest
+%% available notification. This parameter is of type dateTime and
+%% compliant to [RFC3339]. Implementations must support time
+%% zones.</dd>
+%%
+%% <dt>StopTime:</dt>
+%% <dd>An optional parameter used with the optional replay feature
+%% to indicate the newest notifications of interest. If `StopTime'
+%% is not present, the notifications will continue until the
+%% subscription is terminated. Must be used with and be later than
+%% `StartTime'. Values of `StopTime' in the future are valid. This
+%% parameter is of type dateTime and compliant to [RFC3339].
+%% Implementations must support time zones.</dd>
+%% </dl>
+%%
+%% See RFC5277 for further details about the event notification
+%% mechanism.
+%%
+%% @end
+%%----------------------------------------------------------------------
+create_subscription(Client,Stream,Filter,StartTime,StopTime,Timeout) ->
+ call(Client,{send_rpc_op,{create_subscription, self()},
+ [Stream,Filter,StartTime,StopTime],
+ Timeout}).
+
+%%----------------------------------------------------------------------
+%% @spec get_event_streams(Client, Timeout) -> Result
+%% @equiv get_event_streams(Client, [], Timeout)
+get_event_streams(Client,Timeout) when is_integer(Timeout); Timeout==infinity ->
+ get_event_streams(Client,[],Timeout);
+
+%%----------------------------------------------------------------------
+%% @spec get_event_streams(Client, Streams) -> Result
+%% @equiv get_event_streams(Client, Streams, infinity)
+get_event_streams(Client,Streams) when is_list(Streams) ->
+ get_event_streams(Client,Streams,?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec get_event_streams(Client, Streams, Timeout)
+ -> Result when
+ Client :: client(),
+ Streams :: [stream_name()],
+ Timeout :: timeout(),
+ Result :: {ok,streams()} | {error,error_reason()}.
+%% @doc Send a request to get the given event streams.
+%%
+%% `Streams' is a list of stream names. The following filter will
+%% be sent to the netconf server in a `get' request:
+%%
+%% ```
+%% <netconf xmlns="urn:ietf:params:xml:ns:netmod:notification">
+%% <streams>
+%% <stream>
+%% <name>StreamName1</name>
+%% </stream>
+%% <stream>
+%% <name>StreamName2</name>
+%% </stream>
+%% ...
+%% </streams>
+%% </netconf>
+%% '''
+%%
+%% If `Streams' is an empty list, ALL streams will be requested
+%% by sending the following filter:
+%%
+%% ```
+%% <netconf xmlns="urn:ietf:params:xml:ns:netmod:notification">
+%% <streams/>
+%% </netconf>
+%% '''
+%%
+%% If more complex filtering is needed, a use {@link get/2} or {@link
+%% get/3} and specify the exact filter according to XML Schema for
+%% Event Notifications found in RFC5277.
+%%
+%% @end
+%%----------------------------------------------------------------------
+get_event_streams(Client,Streams,Timeout) ->
+ call(Client,{get_event_streams,Streams,Timeout}).
+
+
+%%----------------------------------------------------------------------
+%% @spec close_session(Client) -> Result
+%% @equiv close_session(Client, infinity)
+close_session(Client) ->
+ close_session(Client, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec close_session(Client, Timeout) -> Result when
+ Client :: client(),
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Request graceful termination of the session associated with the client.
+%%
+%% When a netconf server receives a `close-session' request, it
+%% will gracefully close the session. The server will release any
+%% locks and resources associated with the session and gracefully
+%% close any associated connections. Any NETCONF requests received
+%% after a `close-session' request will be ignored.
+%%
+%% @end
+%%----------------------------------------------------------------------
+close_session(Client, Timeout) ->
+ call(Client,{send_rpc_op, close_session, [], Timeout}).
+
+
+%%----------------------------------------------------------------------
+%% @spec kill_session(Client, SessionId) -> Result
+%% @equiv kill_session(Client, SessionId, infinity)
+kill_session(Client, SessionId) ->
+ kill_session(Client, SessionId, ?DEFAULT_TIMEOUT).
+
+%%----------------------------------------------------------------------
+-spec kill_session(Client, SessionId, Timeout) -> Result when
+ Client :: client(),
+ SessionId :: pos_integer(),
+ Timeout :: timeout(),
+ Result :: ok | {error,error_reason()}.
+%% @doc Force termination of the session associated with the supplied
+%% session id.
+%%
+%% The server side shall abort any operations currently in process,
+%% release any locks and resources associated with the session, and
+%% close any associated connections.
+%%
+%% Only if the server is in the confirmed commit phase, the
+%% configuration will be restored to its state before entering the
+%% confirmed commit phase. Otherwise, no configuration roll back will
+%% be performed.
+%%
+%% If the given `SessionId' is equal to the current session id,
+%% an error will be returned.
+%%
+%% @end
+%% ----------------------------------------------------------------------
+kill_session(Client, SessionId, Timeout) ->
+ call(Client,{send_rpc_op, kill_session, [SessionId], Timeout}).
+
+
+%%----------------------------------------------------------------------
+%% Callback functions
+%%----------------------------------------------------------------------
+
+%% @private
+init(_KeyOrName,{_Host,_Port},Options) ->
+ case ssh_open(Options) of
+ {ok, Connection} ->
+ log(Connection,open),
+ {ConnPid,_} = Connection#connection.reference,
+ {ok, ConnPid, #state{connection = Connection}};
+ {error,Reason}->
+ {error,Reason}
+ end.
+
+%% @private
+terminate(_, #state{connection=Connection}) ->
+ ssh_close(Connection),
+ log(Connection,close),
+ ok.
+
+%% @private
+handle_msg({hello,Timeout}, From,
+ #state{connection=Connection,hello_status=HelloStatus} = State) ->
+ case do_send(Connection, client_hello()) of
+ ok ->
+ case HelloStatus of
+ undefined ->
+ {Ref,TRef} = set_request_timer(Timeout),
+ {noreply, State#state{hello_status=#pending{tref=TRef,
+ ref=Ref,
+ caller=From}}};
+ received ->
+ {reply, ok, State#state{hello_status=done}};
+ {error,Reason} ->
+ {stop, {error,Reason}, State}
+ end;
+ Error ->
+ {stop, Error, State}
+ end;
+handle_msg(_, _From, #state{session_id=undefined} = State) ->
+ %% Hello is not yet excanged - this shall never happen
+ {reply,{error,waiting_for_hello},State};
+handle_msg(get_capabilities, _From, #state{capabilities = Caps} = State) ->
+ {reply, Caps, State};
+handle_msg(get_session_id, _From, #state{session_id = Id} = State) ->
+ {reply, Id, State};
+handle_msg({send, Timeout, SimpleXml}, From,
+ #state{connection=Connection,pending=Pending} = State) ->
+ case do_send(Connection, SimpleXml) of
+ ok ->
+ {Ref,TRef} = set_request_timer(Timeout),
+ {noreply, State#state{pending=[#pending{tref=TRef,
+ ref=Ref,
+ caller=From} | Pending]}};
+ Error ->
+ {reply, Error, State}
+ end;
+handle_msg({send_rpc, SimpleXml, Timeout}, From, State) ->
+ do_send_rpc(undefined, SimpleXml, Timeout, From, State);
+handle_msg({send_rpc_op, Op, Data, Timeout}, From, State) ->
+ SimpleXml = encode_rpc_operation(Op,Data),
+ do_send_rpc(Op, SimpleXml, Timeout, From, State);
+handle_msg({get_event_streams=Op,Streams,Timeout}, From, State) ->
+ Filter = {netconf,?NETMOD_NOTIF_NAMESPACE_ATTR,
+ [{streams,[{stream,[{name,[Name]}]} || Name <- Streams]}]},
+ SimpleXml = encode_rpc_operation(get,[Filter]),
+ do_send_rpc(Op, SimpleXml, Timeout, From, State).
+
+handle_msg({ssh_cm, _CM, {data, _Ch, _Type, Data}}, State) ->
+ handle_data(Data, State);
+handle_msg({ssh_cm, _CM, _SshCloseMsg}, State) ->
+ %% _SshCloseMsg can probably be one of
+ %% {eof,Ch}
+ %% {exit_status,Ch,Status}
+ %% {exit_signal,Ch,ExitSignal,ErrorMsg,LanguageString}
+ %% {signal,Ch,Signal}
+
+ %% This might e.g. happen if the server terminates the connection,
+ %% as in kill-session (or if ssh:close is called from somewhere
+ %% unexpected).
+
+ %%! Log this??
+ %%! Currently the log will say that the client closed the
+ %%! connection - due to terminate/2
+
+ {stop, State};
+handle_msg({Ref,timeout},
+ #state{hello_status=#pending{ref=Ref,caller=Caller}} = State) ->
+ ct_gen_conn:return(Caller,{error,{hello_session_failed,timeout}}),
+ {stop,State#state{hello_status={error,timeout}}};
+handle_msg({Ref,timeout},#state{pending=Pending} = State) ->
+ {value,#pending{caller=Caller},Pending1} =
+ lists:keytake(Ref,#pending.ref,Pending),
+ ct_gen_conn:return(Caller,{error,timeout}),
+ {noreply,State#state{pending=Pending1}}.
+
+%% @private
+%% Called by ct_util_server to close registered connections before terminate.
+close(Client) ->
+ case get_handle(Client) of
+ {ok,Pid} ->
+ case ct_gen_conn:stop(Pid) of
+ {error,{process_down,Pid,noproc}} ->
+ {error,already_closed};
+ Result ->
+ Result
+ end;
+ Error ->
+ Error
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Internal functions
+%%----------------------------------------------------------------------
+call(Client, Msg) ->
+ call(Client, Msg, infinity).
+call(Client, Msg, Timeout) ->
+ case get_handle(Client) of
+ {ok,Pid} ->
+ case ct_gen_conn:call(Pid,Msg,Timeout) of
+ {error,{process_down,Client,noproc}} ->
+ {error,no_such_client};
+ {error,{process_down,Client,normal}} ->
+ {error,closed};
+ {error,{process_down,Client,Reason}} ->
+ {error,{closed,Reason}};
+ Other ->
+ Other
+ end;
+ Error ->
+ Error
+ end.
+
+get_handle(Client) when is_pid(Client) ->
+ {ok,Client};
+get_handle(Client) ->
+ case ct_util:get_connections(Client, ?MODULE) of
+ {ok,[{Pid,_}]} ->
+ {ok,Pid};
+ {ok,[]} ->
+ {error,{no_connection_found,Client}};
+ {ok,Conns} ->
+ {error,{multiple_connections_found,Client,Conns}};
+ Error ->
+ Error
+ end.
+
+check_options([], undefined, _Port, _Options) ->
+ {error, no_host_address};
+check_options([], _Host, undefined, _Options) ->
+ {error, no_port};
+check_options([], Host, Port, Options) ->
+ {Host,Port,Options};
+check_options([{ssh, Host}|T], _, Port, #options{} = Options) ->
+ check_options(T, Host, Port, Options#options{host=Host});
+check_options([{port,Port}|T], Host, _, #options{} = Options) ->
+ check_options(T, Host, Port, Options#options{port=Port});
+check_options([{timeout, Timeout}|T], Host, Port, Options)
+ when is_integer(Timeout); Timeout==infinity ->
+ check_options(T, Host, Port, Options#options{timeout = Timeout});
+check_options([{X,_}=Opt|T], Host, Port, #options{ssh=SshOpts}=Options) ->
+ case lists:member(X,?VALID_SSH_OPTS) of
+ true ->
+ check_options(T, Host, Port, Options#options{ssh=[Opt|SshOpts]});
+ false ->
+ {error, {invalid_option, Opt}}
+ end.
+
+%%%-----------------------------------------------------------------
+set_request_timer(infinity) ->
+ {undefined,undefined};
+set_request_timer(T) ->
+ Ref = make_ref(),
+ {ok,TRef} = timer:send_after(T,{Ref,timeout}),
+ {Ref,TRef}.
+
+
+%%%-----------------------------------------------------------------
+client_hello() ->
+ {hello, ?NETCONF_NAMESPACE_ATTR,
+ [{capabilities,
+ [{capability,[?NETCONF_BASE_CAP++?NETCONF_BASE_CAP_VSN]}]}]}.
+
+%%%-----------------------------------------------------------------
+
+encode_rpc_operation(Lock,[Target]) when Lock==lock; Lock==unlock ->
+ {Lock,[{target,[Target]}]};
+encode_rpc_operation(get,[Filter]) ->
+ {get,filter(Filter)};
+encode_rpc_operation(get_config,[Source,Filter]) ->
+ {'get-config',[{source,[Source]}] ++ filter(Filter)};
+encode_rpc_operation(edit_config,[Target,Config]) ->
+ {'edit-config',[{target,[Target]},{config,[Config]}]};
+encode_rpc_operation(delete_config,[Target]) ->
+ {'delete-config',[{target,[Target]}]};
+encode_rpc_operation(copy_config,[Target,Source]) ->
+ {'copy-config',[{target,[Target]},{source,[Source]}]};
+encode_rpc_operation(action,[Action]) ->
+ {action,?ACTION_NAMESPACE_ATTR,[{data,[Action]}]};
+encode_rpc_operation(kill_session,[SessionId]) ->
+ {'kill-session',[{'session-id',[integer_to_list(SessionId)]}]};
+encode_rpc_operation(close_session,[]) ->
+ 'close-session';
+encode_rpc_operation({create_subscription,_},
+ [Stream,Filter,StartTime,StopTime]) ->
+ {'create-subscription',?NETCONF_NOTIF_NAMESPACE_ATTR,
+ [{stream,[Stream]}] ++
+ filter(Filter) ++
+ maybe_element(startTime,StartTime) ++
+ maybe_element(stopTime,StopTime)}.
+
+filter(undefined) ->
+ [];
+filter({xpath,Filter}) when ?is_string(Filter) ->
+ [{filter,[{type,"xpath"},{select, Filter}],[]}];
+filter(Filter) ->
+ [{filter,[{type,"subtree"}],[Filter]}].
+
+maybe_element(_,undefined) ->
+ [];
+maybe_element(Tag,Value) ->
+ [{Tag,[Value]}].
+
+%%%-----------------------------------------------------------------
+%%% Send XML data to server
+do_send_rpc(PendingOp,SimpleXml,Timeout,Caller,
+ #state{connection=Connection,msg_id=MsgId,pending=Pending} = State) ->
+ case do_send_rpc(Connection, MsgId, SimpleXml) of
+ ok ->
+ {Ref,TRef} = set_request_timer(Timeout),
+ {noreply, State#state{msg_id=MsgId+1,
+ pending=[#pending{tref=TRef,
+ ref=Ref,
+ msg_id=MsgId,
+ op=PendingOp,
+ caller=Caller} | Pending]}};
+ Error ->
+ {reply, Error, State#state{msg_id=MsgId+1}}
+ end.
+
+do_send_rpc(Connection, MsgId, SimpleXml) ->
+ do_send(Connection,
+ {rpc,
+ [{'message-id',MsgId} | ?NETCONF_NAMESPACE_ATTR],
+ [SimpleXml]}).
+
+do_send(Connection, SimpleXml) ->
+ Xml=to_xml_doc(SimpleXml),
+ log(Connection,send,Xml),
+ ssh_send(Connection, Xml).
+
+to_xml_doc(Simple) ->
+ Prolog = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
+ Xml = list_to_binary(xmerl:export_simple([Simple],
+ xmerl_xml,
+ [#xmlAttribute{name=prolog,
+ value=Prolog}])),
+ <<Xml/binary,?END_TAG/binary>>.
+
+%%%-----------------------------------------------------------------
+%%% Parse and handle received XML data
+handle_data(NewData,#state{connection=Connection,buff=Buff} = State) ->
+ log(Connection,recv,NewData),
+ Data = <<Buff/binary,NewData/binary>>,
+ case xmerl_sax_parser:stream(<<>>,
+ [{continuation_fun,fun sax_cont/1},
+ {continuation_state,{Data,Connection,false}},
+ {event_fun,fun sax_event/3},
+ {event_state,[]}]) of
+ {ok, Simple, Rest} ->
+ decode(Simple,State#state{buff=Rest});
+ {fatal_error,_Loc,Reason,_EndTags,_EventState} ->
+ ?error(Connection#connection.name,[{parse_error,Reason},
+ {data,Data}]),
+ case Reason of
+ {could_not_fetch_data,Msg} ->
+ handle_msg(Msg,State#state{buff = <<>>});
+ _Other ->
+ Pending1 =
+ case State#state.pending of
+ [] ->
+ [];
+ Pending ->
+ %% Assuming the first request gets the
+ %% first answer
+ P=#pending{tref=TRef,caller=Caller} =
+ lists:last(Pending),
+ timer:cancel(TRef),
+ Reason1 = {failed_to_parse_received_data,Reason},
+ ct_gen_conn:return(Caller,{error,Reason1}),
+ lists:delete(P,Pending)
+ end,
+ {noreply,State#state{pending=Pending1,buff = <<>>}}
+ end
+ end.
+
+%%%-----------------------------------------------------------------
+%%% Parsing of XML data
+%% Contiuation function for the sax parser
+sax_cont(done) ->
+ {<<>>,done};
+sax_cont({Data,Connection,false}) ->
+ case binary:split(Data,[?END_TAG],[]) of
+ [All] ->
+ %% No end tag found. Remove what could be a part
+ %% of an end tag from the data and save for next
+ %% iteration
+ SafeSize = size(All)-5,
+ <<New:SafeSize/binary,Save:5/binary>> = All,
+ {New,{Save,Connection,true}};
+ [_Msg,_Rest]=Msgs ->
+ %% We have at least one full message. Any excess data will
+ %% be returned from xmerl_sax_parser:stream/2 in the Rest
+ %% parameter.
+ {list_to_binary(Msgs),done}
+ end;
+sax_cont({Data,Connection,true}) ->
+ case ssh_receive_data() of
+ {ok,Bin} ->
+ log(Connection,recv,Bin),
+ sax_cont({<<Data/binary,Bin/binary>>,Connection,false});
+ {error,Reason} ->
+ throw({could_not_fetch_data,Reason})
+ end.
+
+
+
+%% Event function for the sax parser. It builds a simple XML structure.
+%% Care is taken to keep namespace attributes and prefixes as in the original XML.
+sax_event(Event,_Loc,State) ->
+ sax_event(Event,State).
+
+sax_event({startPrefixMapping, Prefix, Uri},Acc) ->
+ %% startPrefixMapping will always come immediately before the
+ %% startElement where the namespace is defined.
+ [{xmlns,{Prefix,Uri}}|Acc];
+sax_event({startElement,_Uri,_Name,QN,Attrs},Acc) ->
+ %% Pick out any namespace attributes inserted due to a
+ %% startPrefixMapping event.The rest of Acc will then be only
+ %% elements.
+ {NsAttrs,NewAcc} = split_attrs_and_elements(Acc,[]),
+ Tag = qn_to_tag(QN),
+ [{Tag,NsAttrs ++ parse_attrs(Attrs),[]}|NewAcc];
+sax_event({endElement,_Uri,_Name,_QN},[{Name,Attrs,Cont},{Parent,PA,PC}|Acc]) ->
+ [{Parent,PA,[{Name,Attrs,lists:reverse(Cont)}|PC]}|Acc];
+sax_event(endDocument,[{Tag,Attrs,Cont}]) ->
+ {Tag,Attrs,lists:reverse(Cont)};
+sax_event({characters,String},[{Name,Attrs,Cont}|Acc]) ->
+ [{Name,Attrs,[String|Cont]}|Acc];
+sax_event(_Event,State) ->
+ State.
+
+split_attrs_and_elements([{xmlns,{Prefix,Uri}}|Rest],Attrs) ->
+ split_attrs_and_elements(Rest,[{xmlnstag(Prefix),Uri}|Attrs]);
+split_attrs_and_elements(Elements,Attrs) ->
+ {Attrs,Elements}.
+
+xmlnstag([]) ->
+ xmlns;
+xmlnstag(Prefix) ->
+ list_to_atom("xmlns:"++Prefix).
+
+qn_to_tag({[],Name}) ->
+ list_to_atom(Name);
+qn_to_tag({Prefix,Name}) ->
+ list_to_atom(Prefix ++ ":" ++ Name).
+
+parse_attrs([{_Uri, [], Name, Value}|Attrs]) ->
+ [{list_to_atom(Name),Value}|parse_attrs(Attrs)];
+parse_attrs([{_Uri, Prefix, Name, Value}|Attrs]) ->
+ [{list_to_atom(Prefix ++ ":" ++ Name),Value}|parse_attrs(Attrs)];
+parse_attrs([]) ->
+ [].
+
+
+%%%-----------------------------------------------------------------
+%%% Decoding of parsed XML data
+decode({Tag,Attrs,_}=E, #state{connection=Connection,pending=Pending}=State) ->
+ ConnName = Connection#connection.name,
+ case get_local_name_atom(Tag) of
+ 'rpc-reply' ->
+ case get_msg_id(Attrs) of
+ undefined ->
+ case Pending of
+ [#pending{msg_id=MsgId}] ->
+ ?error(ConnName,[{warning,rpc_reply_missing_msg_id},
+ {assuming,MsgId}]),
+ decode_rpc_reply(MsgId,E,State);
+ _ ->
+ ?error(ConnName,[{error,rpc_reply_missing_msg_id}]),
+ {noreply,State}
+ end;
+ MsgId ->
+ decode_rpc_reply(MsgId,E,State)
+ end;
+ hello ->
+ case State#state.hello_status of
+ undefined ->
+ case decode_hello(E) of
+ {ok,SessionId,Capabilities} ->
+ {noreply,State#state{session_id = SessionId,
+ capabilities = Capabilities,
+ hello_status = received}};
+ {error,Reason} ->
+ {noreply,State#state{hello_status = {error,Reason}}}
+ end;
+ #pending{tref=TRef,caller=Caller} ->
+ timer:cancel(TRef),
+ case decode_hello(E) of
+ {ok,SessionId,Capabilities} ->
+ ct_gen_conn:return(Caller,ok),
+ {noreply,State#state{session_id = SessionId,
+ capabilities = Capabilities,
+ hello_status = done}};
+ {error,Reason} ->
+ ct_gen_conn:return(Caller,{error,Reason}),
+ {stop,State#state{hello_status={error,Reason}}}
+ end;
+ Other ->
+ ?error(ConnName,[{got_unexpected_hello,E},
+ {hello_status,Other}]),
+ {noreply,State}
+ end;
+ notification ->
+ EventReceiver = State#state.event_receiver,
+ EventReceiver ! E,
+ {noreply,State};
+ Other ->
+ %% Result of send/2, when not sending an rpc request - or
+ %% if netconf server sends noise. Can handle this only if
+ %% there is just one pending that matches (i.e. has
+ %% undefined msg_id and op)
+ case [P || P = #pending{msg_id=undefined,op=undefined} <- Pending] of
+ [#pending{tref=TRef,
+ caller=Caller}] ->
+ timer:cancel(TRef),
+ ct_gen_conn:return(Caller,E),
+ {noreply,State#state{pending=[]}};
+ _ ->
+ ?error(ConnName,[{got_unexpected_msg,Other},
+ {expecting,Pending}]),
+ {noreply,State}
+ end
+
+ end.
+
+get_msg_id(Attrs) ->
+ case lists:keyfind('message-id',1,Attrs) of
+ {_,Str} ->
+ list_to_integer(Str);
+ false ->
+ undefined
+ end.
+
+decode_rpc_reply(MsgId,{_,Attrs,Content0}=E,#state{pending=Pending} = State) ->
+ case lists:keytake(MsgId,#pending.msg_id,Pending) of
+ {value, #pending{tref=TRef,op=Op,caller=Caller}, Pending1} ->
+ timer:cancel(TRef),
+ Content = forward_xmlns_attr(Attrs,Content0),
+ {CallerReply,{ServerReply,State2}} =
+ do_decode_rpc_reply(Op,Content,State#state{pending=Pending1}),
+ ct_gen_conn:return(Caller,CallerReply),
+ {ServerReply,State2};
+ false ->
+ %% Result of send/2, when receiving a correct
+ %% rpc-reply. Can handle this only if there is just one
+ %% pending that matches (i.e. has undefined msg_id and op)
+ case [P || P = #pending{msg_id=undefined,op=undefined} <- Pending] of
+ [#pending{tref=TRef,
+ msg_id=undefined,
+ op=undefined,
+ caller=Caller}] ->
+ timer:cancel(TRef),
+ ct_gen_conn:return(Caller,E),
+ {noreply,State#state{pending=[]}};
+ _ ->
+ ConnName = (State#state.connection)#connection.name,
+ ?error(ConnName,[{got_unexpected_msg_id,MsgId},
+ {expecting,Pending}]),
+ {noreply,State}
+ end
+ end.
+
+do_decode_rpc_reply(Op,Result,State)
+ when Op==lock; Op==unlock; Op==edit_config; Op==delete_config;
+ Op==copy_config; Op==kill_session ->
+ {decode_ok(Result),{noreply,State}};
+do_decode_rpc_reply(Op,Result,State)
+ when Op==get; Op==get_config; Op==action ->
+ {decode_data(Result),{noreply,State}};
+do_decode_rpc_reply(close_session,Result,State) ->
+ case decode_ok(Result) of
+ ok -> {ok,{stop,State}};
+ Other -> {Other,{noreply,State}}
+ end;
+do_decode_rpc_reply({create_subscription,Caller},Result,State) ->
+ case decode_ok(Result) of
+ ok ->
+ {ok,{noreply,State#state{event_receiver=Caller}}};
+ Other ->
+ {Other,{noreply,State}}
+ end;
+do_decode_rpc_reply(get_event_streams,Result,State) ->
+ {decode_streams(decode_data(Result)),{noreply,State}};
+do_decode_rpc_reply(undefined,Result,State) ->
+ {Result,{noreply,State}}.
+
+
+
+decode_ok([{Tag,Attrs,Content}]) ->
+ case get_local_name_atom(Tag) of
+ ok ->
+ ok;
+ 'rpc-error' ->
+ {error,forward_xmlns_attr(Attrs,Content)};
+ _Other ->
+ {error,{unexpected_rpc_reply,[{Tag,Attrs,Content}]}}
+ end;
+decode_ok(Other) ->
+ {error,{unexpected_rpc_reply,Other}}.
+
+decode_data([{Tag,Attrs,Content}]) ->
+ case get_local_name_atom(Tag) of
+ data ->
+ %% Since content of data has nothing from the netconf
+ %% namespace, we remove the parent's xmlns attribute here
+ %% - just to make the result cleaner
+ {ok,forward_xmlns_attr(remove_xmlnsattr_for_tag(Tag,Attrs),Content)};
+ 'rpc-error' ->
+ {error,forward_xmlns_attr(Attrs,Content)};
+ _Other ->
+ {error,{unexpected_rpc_reply,[{Tag,Attrs,Content}]}}
+ end;
+decode_data(Other) ->
+ {error,{unexpected_rpc_reply,Other}}.
+
+get_qualified_name(Tag) ->
+ case string:tokens(atom_to_list(Tag),":") of
+ [TagStr] -> {[],TagStr};
+ [PrefixStr,TagStr] -> {PrefixStr,TagStr}
+ end.
+
+get_local_name_atom(Tag) ->
+ {_,TagStr} = get_qualified_name(Tag),
+ list_to_atom(TagStr).
+
+
+%% Remove the xmlns attr that points to the tag. I.e. if the tag has a
+%% prefix, remove {'xmlns:prefix',_}, else remove default {xmlns,_}.
+remove_xmlnsattr_for_tag(Tag,Attrs) ->
+ {Prefix,_TagStr} = get_qualified_name(Tag),
+ XmlnsTag = xmlnstag(Prefix),
+ case lists:keytake(XmlnsTag,1,Attrs) of
+ {value,_,NoNsAttrs} ->
+ NoNsAttrs;
+ false ->
+ Attrs
+ end.
+
+%% Take all xmlns attributes from the parent's attribute list and
+%% forward into all childrens' attribute lists. But do not overwrite
+%% any.
+forward_xmlns_attr(ParentAttrs,Children) ->
+ do_forward_xmlns_attr(get_all_xmlns_attrs(ParentAttrs,[]),Children).
+
+do_forward_xmlns_attr(XmlnsAttrs,[{ChT,ChA,ChC}|Children]) ->
+ ChA1 = add_xmlns_attrs(XmlnsAttrs,ChA),
+ [{ChT,ChA1,ChC} | do_forward_xmlns_attr(XmlnsAttrs,Children)];
+do_forward_xmlns_attr(_XmlnsAttrs,[]) ->
+ [].
+
+add_xmlns_attrs([{Key,_}=A|XmlnsAttrs],ChA) ->
+ case lists:keymember(Key,1,ChA) of
+ true ->
+ add_xmlns_attrs(XmlnsAttrs,ChA);
+ false ->
+ add_xmlns_attrs(XmlnsAttrs,[A|ChA])
+ end;
+add_xmlns_attrs([],ChA) ->
+ ChA.
+
+get_all_xmlns_attrs([{xmlns,_}=Default|Attrs],XmlnsAttrs) ->
+ get_all_xmlns_attrs(Attrs,[Default|XmlnsAttrs]);
+get_all_xmlns_attrs([{Key,_}=Attr|Attrs],XmlnsAttrs) ->
+ case atom_to_list(Key) of
+ "xmlns:"++_Prefix ->
+ get_all_xmlns_attrs(Attrs,[Attr|XmlnsAttrs]);
+ _ ->
+ get_all_xmlns_attrs(Attrs,XmlnsAttrs)
+ end;
+get_all_xmlns_attrs([],XmlnsAttrs) ->
+ XmlnsAttrs.
+
+
+%% Decode server hello to pick out session id and capabilities
+decode_hello({hello,_Attrs,Hello}) ->
+ case lists:keyfind('session-id',1,Hello) of
+ {'session-id',_,[SessionId]} ->
+ case lists:keyfind(capabilities,1,Hello) of
+ {capabilities,_,Capabilities} ->
+ case decode_caps(Capabilities,[],false) of
+ {ok,Caps} ->
+ {ok,list_to_integer(SessionId),Caps};
+ Error ->
+ Error
+ end;
+ false ->
+ {error,{incorrect_hello,capabilities_not_found}}
+ end;
+ false ->
+ {error,{incorrect_hello,no_session_id_found}}
+ end.
+
+decode_caps([{capability,[],[?NETCONF_BASE_CAP++Vsn=Cap]} |Caps], Acc, _) ->
+ case Vsn of
+ ?NETCONF_BASE_CAP_VSN ->
+ decode_caps(Caps, [Cap|Acc], true);
+ _ ->
+ {error,{incompatible_base_capability_vsn,Vsn}}
+ end;
+decode_caps([{capability,[],[Cap]}|Caps],Acc,Base) ->
+ decode_caps(Caps,[Cap|Acc],Base);
+decode_caps([H|_T],_,_) ->
+ {error,{unexpected_capability_element,H}};
+decode_caps([],_,false) ->
+ {error,{incorrect_hello,no_base_capability_found}};
+decode_caps([],Acc,true) ->
+ {ok,lists:reverse(Acc)}.
+
+
+%% Return a list of {Name,Data}, where data is a {Tag,Value} list for each stream
+decode_streams({error,Reason}) ->
+ {error,Reason};
+decode_streams({ok,[{netconf,_,Streams}]}) ->
+ {ok,decode_streams(Streams)};
+decode_streams([{streams,_,Streams}]) ->
+ decode_streams(Streams);
+decode_streams([{stream,_,Stream} | Streams]) ->
+ {name,_,[Name]} = lists:keyfind(name,1,Stream),
+ [{Name,[{Tag,Value} || {Tag,_,[Value]} <- Stream, Tag /= name]}
+ | decode_streams(Streams)];
+decode_streams([]) ->
+ [].
+
+
+%%%-----------------------------------------------------------------
+%%% Logging
+
+log(Connection,Action) ->
+ log(Connection,Action,<<>>).
+log(#connection{host=Host,port=Port,name=Name},Action,Data) ->
+ error_logger:info_report(#conn_log{client=self(),
+ address={Host,Port},
+ name=Name,
+ action=Action,
+ module=?MODULE},
+ Data).
+
+
+%% Log callback - called from the error handler process
+format_data(raw,Data) ->
+ io_lib:format("~n~s~n",[hide_password(Data)]);
+format_data(pretty,Data) ->
+ io_lib:format("~n~s~n",[indent(Data)]);
+format_data(html,Data) ->
+ io_lib:format("~n~s~n",[html_format(Data)]).
+
+%%%-----------------------------------------------------------------
+%%% Hide password elements from XML data
+hide_password(Bin) ->
+ re:replace(Bin,<<"(<password[^>]*>)[^<]*(</password>)">>,<<"\\1*****\\2">>,
+ [global,{return,binary}]).
+
+%%%-----------------------------------------------------------------
+%%% HTML formatting
+html_format(Bin) ->
+ binary:replace(indent(Bin),<<"<">>,<<"&lt;">>,[global]).
+
+%%%-----------------------------------------------------------------
+%%% Indentation of XML code
+indent(Bin) ->
+ String = normalize(hide_password(Bin)),
+ IndentedString =
+ case erase(part_of_line) of
+ undefined ->
+ indent1(String,[]);
+ Part ->
+ indent1(lists:reverse(Part)++String,erase(indent))
+ end,
+ list_to_binary(IndentedString).
+
+%% Normalizes the XML document by removing all space and newline
+%% between two XML tags.
+%% Returns a list, no matter if the input was a list or a binary.
+normalize(Str) ->
+ re:replace(Str,<<">[ \r\n\t]+<">>,<<"><">>,[global,{return,list}]).
+
+
+indent1("<?"++Rest1,Indent1) ->
+ %% Prolog
+ {Line,Rest2,Indent2} = indent_line(Rest1,Indent1,[$?,$<]),
+ Line++indent1(Rest2,Indent2);
+indent1("</"++Rest1,Indent1) ->
+ %% Stop tag
+ {Line,Rest2,Indent2} = indent_line1(Rest1,Indent1,[$/,$<]),
+ "\n"++Line++indent1(Rest2,Indent2);
+indent1("<"++Rest1,Indent1) ->
+ %% Start- or empty tag
+ put(tag,get_tag(Rest1)),
+ {Line,Rest2,Indent2} = indent_line(Rest1,Indent1,[$<]),
+ "\n"++Line++indent1(Rest2,Indent2);
+indent1([H|T],Indent) ->
+ [H|indent1(T,Indent)];
+indent1([],_Indent) ->
+ [].
+
+indent_line("?>"++Rest,Indent,Line) ->
+ %% Prolog
+ {lists:reverse(Line)++"?>",Rest,Indent};
+indent_line("/></"++Rest,Indent,Line) ->
+ %% Empty tag, and stop of parent tag -> one step out in indentation
+ {Indent++lists:reverse(Line)++"/>","</"++Rest,Indent--" "};
+indent_line("/>"++Rest,Indent,Line) ->
+ %% Empty tag, then probably next tag -> keep indentation
+ {Indent++lists:reverse(Line)++"/>",Rest,Indent};
+indent_line("></"++Rest,Indent,Line) ->
+ LastTag = erase(tag),
+ case get_tag(Rest) of
+ LastTag ->
+ %% Start and stop tag, but no content
+ indent_line1(Rest,Indent,[$/,$<,$>|Line]);
+ _ ->
+ %% Stop tag completed, and then stop tag of parent -> one step out
+ {Indent++lists:reverse(Line)++">","</"++Rest,Indent--" "}
+ end;
+indent_line("><"++Rest,Indent,Line) ->
+ %% Stop tag completed, and new tag comming -> keep indentation
+ {Indent++lists:reverse(Line)++">","<"++Rest," "++Indent};
+indent_line("</"++Rest,Indent,Line) ->
+ %% Stop tag starting -> search for end of this tag
+ indent_line1(Rest,Indent,[$/,$<|Line]);
+indent_line([H|T],Indent,Line) ->
+ indent_line(T,Indent,[H|Line]);
+indent_line([],Indent,Line) ->
+ %% The line is not complete - will be continued later
+ put(part_of_line,Line),
+ put(indent,Indent),
+ {[],[],Indent}.
+
+indent_line1("></"++Rest,Indent,Line) ->
+ %% Stop tag completed, and then stop tag of parent -> one step out
+ {Indent++lists:reverse(Line)++">","</"++Rest,Indent--" "};
+indent_line1(">"++Rest,Indent,Line) ->
+ %% Stop tag completed -> keep indentation
+ {Indent++lists:reverse(Line)++">",Rest,Indent};
+indent_line1([H|T],Indent,Line) ->
+ indent_line1(T,Indent,[H|Line]);
+indent_line1([],Indent,Line) ->
+ %% The line is not complete - will be continued later
+ put(part_of_line,Line),
+ put(indent,Indent),
+ {[],[],Indent}.
+
+get_tag("/>"++_) ->
+ [];
+get_tag(">"++_) ->
+ [];
+get_tag([H|T]) ->
+ [H|get_tag(T)];
+get_tag([]) ->
+ %% The line is not complete - will be continued later.
+ [].
+
+
+%%%-----------------------------------------------------------------
+%%% SSH stuff
+ssh_receive_data() ->
+ receive
+ {ssh_cm, _CM, {data, _Ch, _Type, Data}} ->
+ {ok, Data};
+ {ssh_cm, _CM, {Closed, _Ch}} = X when Closed == closed; Closed == eof ->
+ {error,X};
+ {_Ref,timeout} = X ->
+ {error,X}
+ end.
+
+ssh_open(#options{host=Host,timeout=Timeout,port=Port,ssh=SshOpts,name=Name}) ->
+ case ssh:connect(Host, Port,
+ [{user_interaction,false},
+ {silently_accept_hosts, true}|SshOpts]) of
+ {ok,CM} ->
+ case ssh_connection:session_channel(CM, Timeout) of
+ {ok,Ch} ->
+ case ssh_connection:subsystem(CM, Ch, "netconf", Timeout) of
+ success ->
+ {ok, #connection{reference = {CM,Ch},
+ host = Host,
+ port = Port,
+ name = Name}};
+ failure ->
+ ssh:close(CM),
+ {error,{ssh,could_not_execute_netconf_subsystem}}
+ end;
+ {error, Reason} ->
+ ssh:close(CM),
+ {error,{ssh,could_not_open_channel,Reason}};
+ Other ->
+ %% Bug in ssh?? got {closed,0} here once...
+ {error,{ssh,unexpected_from_session_channel,Other}}
+ end;
+ {error,Reason} ->
+ {error,{ssh,could_not_connect_to_server,Reason}}
+ end.
+
+ssh_send(#connection{reference = {CM,Ch}}, Data) ->
+ case ssh_connection:send(CM, Ch, Data) of
+ ok -> ok;
+ {error,Reason} -> {error,{ssh,failed_to_send_data,Reason}}
+ end.
+
+ssh_close(#connection{reference = {CM,_Ch}}) ->
+ ssh:close(CM).
+
+
+%%----------------------------------------------------------------------
+%% END OF MODULE
+%%----------------------------------------------------------------------
diff --git a/lib/common_test/src/ct_netconfc.hrl b/lib/common_test/src/ct_netconfc.hrl
new file mode 100644
index 0000000000..295a61a98b
--- /dev/null
+++ b/lib/common_test/src/ct_netconfc.hrl
@@ -0,0 +1,58 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%----------------------------------------------------------------------
+%% File: ct_netconfc.hrl
+%%
+%% Description:
+%% This file defines constant values and records used by the
+%% netconf client ct_netconfc.
+%%
+%% @author Support
+%% @doc Netconf Client Interface.
+%% @end
+%%----------------------------------------------------------------------
+%%----------------------------------------------------------------------
+
+
+%% Default port number (RFC 4742/IANA).
+-define(DEFAULT_PORT, 830).
+
+%% Default timeout to wait for netconf server to reply to a request
+-define(DEFAULT_TIMEOUT, infinity). %% msec
+
+%% Namespaces
+-define(NETCONF_NAMESPACE_ATTR,[{xmlns,?NETCONF_NAMESPACE}]).
+-define(ACTION_NAMESPACE_ATTR,[{xmlns,?ACTION_NAMESPACE}]).
+-define(NETCONF_NOTIF_NAMESPACE_ATTR,[{xmlns,?NETCONF_NOTIF_NAMESPACE}]).
+-define(NETMOD_NOTIF_NAMESPACE_ATTR,[{xmlns,?NETMOD_NOTIF_NAMESPACE}]).
+
+-define(NETCONF_NAMESPACE,"urn:ietf:params:xml:ns:netconf:base:1.0").
+-define(ACTION_NAMESPACE,"urn:com:ericsson:ecim:1.0").
+-define(NETCONF_NOTIF_NAMESPACE,
+ "urn:ietf:params:xml:ns:netconf:notification:1.0").
+-define(NETMOD_NOTIF_NAMESPACE,"urn:ietf:params:xml:ns:netmod:notification").
+
+%% Capabilities
+-define(NETCONF_BASE_CAP,"urn:ietf:params:netconf:base:").
+-define(NETCONF_BASE_CAP_VSN,"1.0").
+
+%% Misc
+-define(END_TAG,<<"]]>]]>">>).
+
+-define(FORMAT(_F, _A), lists:flatten(io_lib:format(_F, _A))).
diff --git a/lib/common_test/src/ct_repeat.erl b/lib/common_test/src/ct_repeat.erl
index 8ecd82f771..a47309c6ee 100644
--- a/lib/common_test/src/ct_repeat.erl
+++ b/lib/common_test/src/ct_repeat.erl
@@ -41,72 +41,86 @@ loop_test(If,Args) when is_list(Args) ->
case get_loop_info(Args) of
no_loop ->
false;
- {error,E} ->
+ E = {error,_} ->
io:format("Common Test error: ~p\n\n",[E]),
file:set_cwd(Cwd),
E;
{repeat,N} ->
io:format("\nCommon Test: Will repeat tests ~w times.\n\n",[N]),
Args1 = [{loop_info,[{repeat,1,N}]} | Args],
- loop(If,repeat,0,N,undefined,Args1,undefined),
- file:set_cwd(Cwd);
+ Result = loop(If,repeat,0,N,undefined,Args1,undefined,[]),
+ file:set_cwd(Cwd),
+ Result;
{stop_time,StopTime} ->
- case remaining_time(StopTime) of
- 0 ->
- io:format("\nCommon Test: No time left to run tests.\n\n",[]),
- ok;
- Secs ->
- io:format("\nCommon Test: Will repeat tests for ~s.\n\n",
- [ts(Secs)]),
- TPid =
- case lists:keymember(force_stop,1,Args) of
- true ->
- CtrlPid = self(),
- spawn(fun() -> stop_after(CtrlPid,Secs) end);
- false ->
- undefined
- end,
- Args1 = [{loop_info,[{stop_time,Secs,StopTime,1}]} | Args],
- loop(If,stop_time,0,Secs,StopTime,Args1,TPid)
- end,
- file:set_cwd(Cwd)
+ Result =
+ case remaining_time(StopTime) of
+ 0 ->
+ io:format("\nCommon Test: "
+ "No time left to run tests.\n\n",[]),
+ {error,not_enough_time};
+ Secs ->
+ io:format("\nCommon Test: "
+ "Will repeat tests for ~s.\n\n",[ts(Secs)]),
+ TPid =
+ case lists:keymember(force_stop,1,Args) of
+ true ->
+ CtrlPid = self(),
+ spawn(fun() -> stop_after(CtrlPid,Secs) end);
+ false ->
+ undefined
+ end,
+ Args1 = [{loop_info,[{stop_time,Secs,StopTime,1}]} | Args],
+ loop(If,stop_time,0,Secs,StopTime,Args1,TPid,[])
+ end,
+ file:set_cwd(Cwd),
+ Result
end.
-loop(_,repeat,N,N,_,_Args,_) ->
- ok;
+loop(_,repeat,N,N,_,_Args,_,AccResult) ->
+ lists:reverse(AccResult);
-loop(If,Type,N,Data0,Data1,Args,TPid) ->
+loop(If,Type,N,Data0,Data1,Args,TPid,AccResult) ->
Pid = spawn_tester(If,self(),Args),
receive
{'EXIT',Pid,Reason} ->
- io:format("Test run crashed! This could be an internal error "
- "- please report!\n\n"
- "~p\n\n",[Reason]),
- cancel(TPid),
- {error,Reason};
+ case Reason of
+ {user_error,What} ->
+ io:format("\nTest run failed!\nReason: ~p\n\n\n", [What]),
+ cancel(TPid),
+ {error,What};
+ _ ->
+ io:format("Test run crashed! This could be an internal error "
+ "- please report!\n\n"
+ "~p\n\n\n",[Reason]),
+ cancel(TPid),
+ {error,Reason}
+ end;
{Pid,{error,Reason}} ->
- io:format("\nTest run failed!\nReason: ~p\n\n",[Reason]),
+ io:format("\nTest run failed!\nReason: ~p\n\n\n",[Reason]),
cancel(TPid),
{error,Reason};
{Pid,Result} ->
if Type == repeat ->
- io:format("\nTest run ~w(~w) complete.\n\n",[N+1,Data0]),
+ io:format("\nTest run ~w(~w) complete.\n\n\n",[N+1,Data0]),
lists:keydelete(loop_info,1,Args),
Args1 = [{loop_info,[{repeat,N+2,Data0}]} | Args],
- loop(If,repeat,N+1,Data0,Data1,Args1,TPid);
+ loop(If,repeat,N+1,Data0,Data1,Args1,TPid,[Result|AccResult]);
Type == stop_time ->
case remaining_time(Data1) of
0 ->
- io:format("\nTest time (~s) has run out.\n\n",[ts(Data0)]),
+ io:format("\nTest time (~s) has run out.\n\n\n",
+ [ts(Data0)]),
cancel(TPid),
- Result;
+ lists:reverse([Result|AccResult]);
Secs ->
io:format("\n~s of test time remaining, "
- "starting run #~w...\n\n",[ts(Secs),N+2]),
+ "starting run #~w...\n\n\n",
+ [ts(Secs),N+2]),
lists:keydelete(loop_info,1,Args),
ST = {stop_time,Data0,Data1,N+2},
Args1 = [{loop_info,[ST]} | Args],
- loop(If,stop_time,N+1,Data0,Data1,Args1,TPid)
+ loop(If,stop_time,N+1,Data0,Data1,Args1,TPid,
+ [Result|AccResult])
end
end
end.
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index 46aec04ec1..4a6a3cdcac 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -39,12 +39,20 @@
%% Misc internal functions
-export([variables_file_name/1,script_start1/2,run_test2/1]).
+-include("ct.hrl").
-include("ct_event.hrl").
-include("ct_util.hrl").
-define(abs(Name), filename:absname(Name)).
-define(testdir(Name, Suite), ct_util:get_testdir(Name, Suite)).
+-define(EXIT_STATUS_TEST_SUCCESSFUL, 0).
+-define(EXIT_STATUS_TEST_CASE_FAILED, 1).
+-define(EXIT_STATUS_TEST_RUN_FAILED, 2).
+
+-define(default_verbosity, [{default,?MAX_VERBOSITY},
+ {'$unspecified',?MAX_VERBOSITY}]).
+
-record(opts, {label,
profile,
vts,
@@ -54,18 +62,22 @@
step,
logdir,
logopts = [],
+ basic_html,
+ verbosity = [],
config = [],
event_handlers = [],
ct_hooks = [],
enable_builtin_hooks,
include = [],
- silent_connections,
+ auto_compile,
+ silent_connections = [],
stylesheet,
multiply_timetraps = 1,
scale_timetraps = false,
create_priv_dir,
testspecs = [],
- tests}).
+ tests,
+ starter}).
%%%-----------------------------------------------------------------
%%% @spec script_start() -> void()
@@ -102,7 +114,8 @@ script_start() ->
end, Flags)
end,
%% used for purpose of testing the run_test interface
- io:format(user, "~n-------------------- START ARGS --------------------~n", []),
+ io:format(user, "~n-------------------- START ARGS "
+ "--------------------~n", []),
io:format(user, "--- Init args:~n~p~n", [FlagFilter(Init)]),
io:format(user, "--- CT args:~n~p~n", [FlagFilter(CtArgs)]),
EnvArgs = opts2args(EnvStartOpts),
@@ -110,7 +123,8 @@ script_start() ->
[EnvStartOpts,EnvArgs]),
Merged = merge_arguments(CtArgs ++ EnvArgs),
io:format(user, "--- Merged args:~n~p~n", [FlagFilter(Merged)]),
- io:format(user, "----------------------------------------------------~n~n", []),
+ io:format(user, "-----------------------------------"
+ "-----------------~n~n", []),
Merged;
_ ->
merge_arguments(CtArgs)
@@ -122,46 +136,107 @@ script_start() ->
script_start(Args) ->
Tracing = start_trace(Args),
- Res =
- case ct_repeat:loop_test(script, Args) of
- false ->
- {ok,Cwd} = file:get_cwd(),
- CTVsn =
- case filename:basename(code:lib_dir(common_test)) of
- CTBase when is_list(CTBase) ->
- case string:tokens(CTBase, "-") of
- ["common_test",Vsn] -> " v"++Vsn;
- _ -> ""
- end
- end,
- io:format("~nCommon Test~s starting (cwd is ~s)~n~n", [CTVsn,Cwd]),
- Self = self(),
- Pid = spawn_link(fun() -> script_start1(Self, Args) end),
- receive
- {'EXIT',Pid,Reason} ->
- case Reason of
- {user_error,What} ->
- io:format("\nTest run failed!\nReason: ~p\n\n", [What]),
- {error,What};
- _ ->
- io:format("Test run crashed! This could be an internal error "
- "- please report!\n\n"
- "~p\n\n", [Reason]),
- {error,Reason}
- end;
- {Pid,{error,Reason}} ->
- io:format("\nTest run failed! Reason:\n~p\n\n",[Reason]),
- {error,Reason};
- {Pid,Result} ->
- Result
- end;
- Result ->
- Result
- end,
+ case ct_repeat:loop_test(script, Args) of
+ false ->
+ {ok,Cwd} = file:get_cwd(),
+ CTVsn =
+ case filename:basename(code:lib_dir(common_test)) of
+ CTBase when is_list(CTBase) ->
+ case string:tokens(CTBase, "-") of
+ ["common_test",Vsn] -> " v"++Vsn;
+ _ -> ""
+ end
+ end,
+ io:format("~nCommon Test~s starting (cwd is ~s)~n~n",
+ [CTVsn,Cwd]),
+ Self = self(),
+ Pid = spawn_link(fun() -> script_start1(Self, Args) end),
+ receive
+ {'EXIT',Pid,Reason} ->
+ case Reason of
+ {user_error,What} ->
+ io:format("\nTest run failed!\nReason: ~p\n\n\n",
+ [What]),
+ finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args);
+ _ ->
+ io:format("Test run crashed! "
+ "This could be an internal error "
+ "- please report!\n\n"
+ "~p\n\n\n", [Reason]),
+ finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args)
+ end;
+ {Pid,{error,Reason}} ->
+ io:format("\nTest run failed! Reason:\n~p\n\n\n",[Reason]),
+ finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args);
+ {Pid,Result} ->
+ io:nl(),
+ finish(Tracing, analyze_test_result(Result, Args), Args)
+ end;
+ {error,_LoopReason} ->
+ finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args);
+ Result ->
+ io:nl(),
+ finish(Tracing, analyze_test_result(Result, Args), Args)
+ end.
+
+%% analyze the result of one test run, or many (in case of looped test)
+analyze_test_result(ok, _) ->
+ ?EXIT_STATUS_TEST_SUCCESSFUL;
+analyze_test_result({error,_Reason}, _) ->
+ ?EXIT_STATUS_TEST_RUN_FAILED;
+analyze_test_result({_Ok,Failed,{_UserSkipped,AutoSkipped}}, Args) ->
+ if Failed > 0 ->
+ ?EXIT_STATUS_TEST_CASE_FAILED;
+ true ->
+ case AutoSkipped of
+ 0 ->
+ ?EXIT_STATUS_TEST_SUCCESSFUL;
+ _ ->
+ case get_start_opt(exit_status,
+ fun([ExitOpt]) -> ExitOpt end,
+ Args) of
+ undefined ->
+ ?EXIT_STATUS_TEST_CASE_FAILED;
+ "ignore_config" ->
+ ?EXIT_STATUS_TEST_SUCCESSFUL
+ end
+ end
+ end;
+analyze_test_result([Result|Rs], Args) ->
+ case analyze_test_result(Result, Args) of
+ ?EXIT_STATUS_TEST_SUCCESSFUL ->
+ analyze_test_result(Rs, Args);
+ Other ->
+ Other
+ end;
+analyze_test_result([], _) ->
+ ?EXIT_STATUS_TEST_SUCCESSFUL;
+analyze_test_result(interactive_mode, _) ->
+ interactive_mode;
+analyze_test_result(Unknown, _) ->
+ io:format("\nTest run failed! Reason:\n~p\n\n\n",[Unknown]),
+ ?EXIT_STATUS_TEST_RUN_FAILED.
+
+finish(Tracing, ExitStatus, Args) ->
stop_trace(Tracing),
timer:sleep(1000),
- io:nl(),
- Res.
+ if ExitStatus == interactive_mode ->
+ interactive_mode;
+ true ->
+ %% it's possible to tell CT to finish execution with a call
+ %% to a different function than the normal halt/1 BIF
+ %% (meant to be used mainly for reading the CT exit status)
+ case get_start_opt(halt_with,
+ fun([HaltMod,HaltFunc]) ->
+ {list_to_atom(HaltMod),
+ list_to_atom(HaltFunc)} end,
+ Args) of
+ undefined ->
+ halt(ExitStatus);
+ {M,F} ->
+ apply(M, F, [ExitStatus])
+ end
+ end.
script_start1(Parent, Args) ->
%% read general start flags
@@ -173,6 +248,7 @@ script_start1(Parent, Args) ->
LogDir = get_start_opt(logdir, fun([LogD]) -> LogD end, Args),
LogOpts = get_start_opt(logopts, fun(Os) -> [list_to_atom(O) || O <- Os] end,
[], Args),
+ Verbosity = verbosity_args2opts(Args),
MultTT = get_start_opt(multiply_timetraps,
fun([MT]) -> list_to_integer(MT) end, 1, Args),
ScaleTT = get_start_opt(scale_timetraps,
@@ -206,7 +282,7 @@ script_start1(Parent, Args) ->
end
end,
%% no_auto_compile + include
- IncludeDirs =
+ {AutoCompile,IncludeDirs} =
case proplists:get_value(no_auto_compile, Args) of
undefined ->
application:set_env(common_test, auto_compile, true),
@@ -222,46 +298,52 @@ script_start1(Parent, Args) ->
case os:getenv("CT_INCLUDE_PATH") of
false ->
application:set_env(common_test, include, InclDirs),
- InclDirs;
+ {undefined,InclDirs};
CtInclPath ->
AllInclDirs =
string:tokens(CtInclPath,[$:,$ ,$,]) ++ InclDirs,
application:set_env(common_test, include, AllInclDirs),
- AllInclDirs
+ {undefined,AllInclDirs}
end;
_ ->
application:set_env(common_test, auto_compile, false),
- []
+ {false,[]}
end,
%% silent connections
SilentConns =
get_start_opt(silent_connections,
- fun(["all"]) -> [];
+ fun(["all"]) -> [all];
(Conns) -> [list_to_atom(Conn) || Conn <- Conns]
- end, Args),
+ end, [], Args),
%% stylesheet
Stylesheet = get_start_opt(stylesheet,
fun([SS]) -> ?abs(SS) end, Args),
%% basic_html - used by ct_logs
- case proplists:get_value(basic_html, Args) of
- undefined ->
- application:set_env(common_test, basic_html, false);
- _ ->
- application:set_env(common_test, basic_html, true)
- end,
+ BasicHtml = case proplists:get_value(basic_html, Args) of
+ undefined ->
+ application:set_env(common_test, basic_html, false),
+ undefined;
+ _ ->
+ application:set_env(common_test, basic_html, true),
+ true
+ end,
StartOpts = #opts{label = Label, profile = Profile,
vts = Vts, shell = Shell, cover = Cover,
logdir = LogDir, logopts = LogOpts,
+ basic_html = BasicHtml,
+ verbosity = Verbosity,
event_handlers = EvHandlers,
ct_hooks = CTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
+ auto_compile = AutoCompile,
include = IncludeDirs,
silent_connections = SilentConns,
stylesheet = Stylesheet,
multiply_timetraps = MultTT,
scale_timetraps = ScaleTT,
- create_priv_dir = CreatePrivDir},
+ create_priv_dir = CreatePrivDir,
+ starter = script},
%% check if log files should be refreshed or go on to run tests...
Result = run_or_refresh(StartOpts, Args),
@@ -325,9 +407,15 @@ script_start2(StartOpts = #opts{vts = undefined,
AllLogOpts = merge_vals([StartOpts#opts.logopts,
SpecStartOpts#opts.logopts]),
-
- Cover = choose_val(StartOpts#opts.cover,
- SpecStartOpts#opts.cover),
+ AllVerbosity =
+ merge_keyvals([StartOpts#opts.verbosity,
+ SpecStartOpts#opts.verbosity]),
+ AllSilentConns =
+ merge_vals([StartOpts#opts.silent_connections,
+ SpecStartOpts#opts.silent_connections]),
+ Cover =
+ choose_val(StartOpts#opts.cover,
+ SpecStartOpts#opts.cover),
MultTT =
choose_val(StartOpts#opts.multiply_timetraps,
SpecStartOpts#opts.multiply_timetraps),
@@ -352,9 +440,36 @@ script_start2(StartOpts = #opts{vts = undefined,
StartOpts#opts.enable_builtin_hooks,
SpecStartOpts#opts.enable_builtin_hooks),
+ Stylesheet =
+ choose_val(StartOpts#opts.stylesheet,
+ SpecStartOpts#opts.stylesheet),
+
AllInclude = merge_vals([StartOpts#opts.include,
SpecStartOpts#opts.include]),
application:set_env(common_test, include, AllInclude),
+
+ AutoCompile =
+ case choose_val(StartOpts#opts.auto_compile,
+ SpecStartOpts#opts.auto_compile) of
+ undefined ->
+ true;
+ ACBool ->
+ application:set_env(common_test,
+ auto_compile,
+ ACBool),
+ ACBool
+ end,
+
+ BasicHtml =
+ case choose_val(StartOpts#opts.basic_html,
+ SpecStartOpts#opts.basic_html) of
+ undefined ->
+ false;
+ BHBool ->
+ application:set_env(common_test, basic_html,
+ BHBool),
+ BHBool
+ end,
{TS,StartOpts#opts{label = Label,
profile = Profile,
@@ -362,11 +477,16 @@ script_start2(StartOpts = #opts{vts = undefined,
cover = Cover,
logdir = LogDir,
logopts = AllLogOpts,
+ basic_html = BasicHtml,
+ verbosity = AllVerbosity,
+ silent_connections = AllSilentConns,
config = SpecStartOpts#opts.config,
event_handlers = AllEvHs,
ct_hooks = AllCTHooks,
enable_builtin_hooks =
EnableBuiltinHooks,
+ stylesheet = Stylesheet,
+ auto_compile = AutoCompile,
include = AllInclude,
multiply_timetraps = MultTT,
scale_timetraps = ScaleTT,
@@ -519,8 +639,10 @@ script_start4(#opts{label = Label, profile = Profile,
event_handlers = EvHandlers,
ct_hooks = CTHooks,
logopts = LogOpts,
+ verbosity = Verbosity,
enable_builtin_hooks = EnableBuiltinHooks,
logdir = LogDir, testspecs = Specs}, _Args) ->
+
%% label - used by ct_logs
application:set_env(common_test, test_label, Label),
@@ -536,11 +658,12 @@ script_start4(#opts{label = Label, profile = Profile,
{ct_hooks, CTHooks},
{enable_builtin_hooks,EnableBuiltinHooks}]) of
ok ->
- ct_util:start(interactive, LogDir),
+ ct_util:start(interactive, LogDir,
+ add_verbosity_defaults(Verbosity)),
ct_util:set_testdata({logopts, LogOpts}),
log_ts_names(Specs),
io:nl(),
- ok;
+ interactive_mode;
Error ->
Error
end;
@@ -553,7 +676,7 @@ script_start4(#opts{vts = true, cover = Cover}, _) ->
%% Add support later (maybe).
io:format("\nCan't run cover in vts mode.\n\n", [])
end,
- erlang:halt();
+ {error,no_cover_in_vts_mode};
script_start4(#opts{shell = true, cover = Cover}, _) ->
case Cover of
@@ -562,7 +685,8 @@ script_start4(#opts{shell = true, cover = Cover}, _) ->
_ ->
%% Add support later (maybe).
io:format("\nCan't run cover in interactive mode.\n\n", [])
- end;
+ end,
+ {error,no_cover_in_interactive_mode};
script_start4(Opts = #opts{tests = Tests}, Args) ->
do_run(Tests, [], Opts, Args).
@@ -579,6 +703,7 @@ script_usage() ->
"\n\t[-dir TestDir1 TestDir2 .. TestDirN] |"
"\n\t[-suite Suite [-case Case]]"
"\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]"
+ "\n\t[-verbosity GenVLvl | [CategoryVLvl1 .. CategoryVLvlN]]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
"\n\t[-no_auto_compile]"
"\n\t[-multiply_timetraps N]"
@@ -593,11 +718,12 @@ script_usage() ->
"\n\t[-userconfig CallbackModule ConfigFile1 .. ConfigFileN]"
"\n\t[-decrypt_key Key] | [-decrypt_file KeyFile]"
"\n\t[-logdir LogDir]"
+ "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]"
+ "\n\t[-verbosity GenVLvl | [CategoryVLvl1 .. CategoryVLvlN]]"
"\n\t[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]"
- "\n\t[-stylesheet CSSFile]"
+ "\n\t[-stylesheet CSSFile]"
"\n\t[-cover CoverCfgFile]"
"\n\t[-event_handler EvHandler1 EvHandler2 .. EvHandlerN]"
- "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]"
"\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
"\n\t[-no_auto_compile]"
@@ -613,12 +739,13 @@ script_usage() ->
"\n\t[-config ConfigFile1 ConfigFile2 .. ConfigFileN]"
"\n\t[-decrypt_key Key] | [-decrypt_file KeyFile]"
"\n\t[-logdir LogDir]"
+ "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]"
+ "\n\t[-verbosity GenVLvl | [CategoryVLvl1 .. CategoryVLvlN]]"
"\n\t[-allow_user_terms]"
"\n\t[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]"
"\n\t[-stylesheet CSSFile]"
"\n\t[-cover CoverCfgFile]"
"\n\t[-event_handler EvHandler1 EvHandler2 .. EvHandlerN]"
- "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]"
"\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
"\n\t[-no_auto_compile]"
@@ -702,7 +829,7 @@ run_test(StartOpts) when is_list(StartOpts) ->
Ref = monitor(process, CTPid),
receive
{'DOWN',Ref,process,CTPid,{user_error,Error}} ->
- Error;
+ {error,Error};
{'DOWN',Ref,process,CTPid,Other} ->
Other
end.
@@ -739,8 +866,10 @@ run_test2(StartOpts) ->
(Lbl) when is_atom(Lbl) -> atom_to_list(Lbl)
end, StartOpts),
%% profile
- Profile = get_start_opt(profile, fun(Prof) when is_list(Prof) -> Prof;
- (Prof) when is_atom(Prof) -> atom_to_list(Prof)
+ Profile = get_start_opt(profile, fun(Prof) when is_list(Prof) ->
+ Prof;
+ (Prof) when is_atom(Prof) ->
+ atom_to_list(Prof)
end, StartOpts),
%% logdir
LogDir = get_start_opt(logdir, fun(LD) when is_list(LD) -> LD end,
@@ -748,6 +877,19 @@ run_test2(StartOpts) ->
%% logopts
LogOpts = get_start_opt(logopts, value, [], StartOpts),
+ %% verbosity
+ Verbosity =
+ get_start_opt(verbosity,
+ fun(VLvls) when is_list(VLvls) ->
+ lists:map(fun(VLvl = {_Cat,_Lvl}) ->
+ VLvl;
+ (Lvl) ->
+ {'$unspecified',Lvl}
+ end, VLvls);
+ (VLvl) when is_integer(VLvl) ->
+ [{'$unspecified',VLvl}]
+ end, [], StartOpts),
+
%% config & userconfig
CfgFiles = ct_config:get_config_file_list(StartOpts),
@@ -786,9 +928,9 @@ run_test2(StartOpts) ->
%% silent connections
SilentConns = get_start_opt(silent_connections,
- fun(all) -> [];
+ fun(all) -> [all];
(Conns) -> Conns
- end, StartOpts),
+ end, [], StartOpts),
%% stylesheet
Stylesheet = get_start_opt(stylesheet,
fun(SS) -> ?abs(SS) end,
@@ -805,7 +947,7 @@ run_test2(StartOpts) ->
CreatePrivDir = get_start_opt(create_priv_dir, value, StartOpts),
%% auto compile & include files
- Include =
+ {AutoCompile,Include} =
case proplists:get_value(auto_compile, StartOpts) of
undefined ->
application:set_env(common_test, auto_compile, true),
@@ -821,16 +963,16 @@ run_test2(StartOpts) ->
case os:getenv("CT_INCLUDE_PATH") of
false ->
application:set_env(common_test, include, InclDirs),
- InclDirs;
+ {undefined,InclDirs};
CtInclPath ->
InclDirs1 = string:tokens(CtInclPath, [$:,$ ,$,]),
AllInclDirs = InclDirs1++InclDirs,
application:set_env(common_test, include, AllInclDirs),
- AllInclDirs
+ {undefined,AllInclDirs}
end;
ACBool ->
application:set_env(common_test, auto_compile, ACBool),
- []
+ {ACBool,[]}
end,
%% decrypt config file
@@ -844,11 +986,14 @@ run_test2(StartOpts) ->
end,
%% basic html - used by ct_logs
- case proplists:get_value(basic_html, StartOpts) of
- undefined ->
- application:set_env(common_test, basic_html, false);
- BasicHtmlBool ->
- application:set_env(common_test, basic_html, BasicHtmlBool)
+ BasicHtml =
+ case proplists:get_value(basic_html, StartOpts) of
+ undefined ->
+ application:set_env(common_test, basic_html, false),
+ undefined;
+ BasicHtmlBool ->
+ application:set_env(common_test, basic_html, BasicHtmlBool),
+ BasicHtmlBool
end,
%% stepped execution
@@ -856,16 +1001,20 @@ run_test2(StartOpts) ->
Opts = #opts{label = Label, profile = Profile,
cover = Cover, step = Step, logdir = LogDir,
- logopts = LogOpts, config = CfgFiles,
+ logopts = LogOpts, basic_html = BasicHtml,
+ config = CfgFiles,
+ verbosity = Verbosity,
event_handlers = EvHandlers,
ct_hooks = CTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
+ auto_compile = AutoCompile,
include = Include,
silent_connections = SilentConns,
stylesheet = Stylesheet,
multiply_timetraps = MultiplyTT,
scale_timetraps = ScaleTT,
- create_priv_dir = CreatePrivDir},
+ create_priv_dir = CreatePrivDir,
+ starter = ct},
%% test specification
case proplists:get_value(spec, StartOpts) of
@@ -894,7 +1043,7 @@ run_spec_file(Relaxed,
log_ts_names(AbsSpecs),
case catch ct_testspec:collect_tests_from_file(AbsSpecs, Relaxed) of
{Error,CTReason} when Error == error ; Error == 'EXIT' ->
- exit(CTReason);
+ exit({error,CTReason});
TS ->
SpecOpts = get_data_for_node(TS, node()),
Label = choose_val(Opts#opts.label,
@@ -905,6 +1054,12 @@ run_spec_file(Relaxed,
SpecOpts#opts.logdir),
AllLogOpts = merge_vals([Opts#opts.logopts,
SpecOpts#opts.logopts]),
+ Stylesheet = choose_val(Opts#opts.stylesheet,
+ SpecOpts#opts.stylesheet),
+ AllVerbosity = merge_keyvals([Opts#opts.verbosity,
+ SpecOpts#opts.verbosity]),
+ AllSilentConns = merge_vals([Opts#opts.silent_connections,
+ SpecOpts#opts.silent_connections]),
AllConfig = merge_vals([CfgFiles, SpecOpts#opts.config]),
Cover = choose_val(Opts#opts.cover,
SpecOpts#opts.cover),
@@ -918,21 +1073,45 @@ run_spec_file(Relaxed,
SpecOpts#opts.event_handlers]),
AllInclude = merge_vals([Opts#opts.include,
SpecOpts#opts.include]),
-
AllCTHooks = merge_vals([Opts#opts.ct_hooks,
- SpecOpts#opts.ct_hooks]),
+ SpecOpts#opts.ct_hooks]),
EnableBuiltinHooks = choose_val(Opts#opts.enable_builtin_hooks,
SpecOpts#opts.enable_builtin_hooks),
application:set_env(common_test, include, AllInclude),
+ AutoCompile = case choose_val(Opts#opts.auto_compile,
+ SpecOpts#opts.auto_compile) of
+ undefined ->
+ true;
+ ACBool ->
+ application:set_env(common_test, auto_compile,
+ ACBool),
+ ACBool
+ end,
+
+ BasicHtml = case choose_val(Opts#opts.basic_html,
+ SpecOpts#opts.basic_html) of
+ undefined ->
+ false;
+ BHBool ->
+ application:set_env(common_test, basic_html,
+ BHBool),
+ BHBool
+ end,
+
Opts1 = Opts#opts{label = Label,
profile = Profile,
cover = Cover,
logdir = which(logdir, LogDir),
logopts = AllLogOpts,
+ stylesheet = Stylesheet,
+ basic_html = BasicHtml,
+ verbosity = AllVerbosity,
+ silent_connections = AllSilentConns,
config = AllConfig,
event_handlers = AllEvHs,
+ auto_compile = AutoCompile,
include = AllInclude,
testspecs = AbsSpecs,
multiply_timetraps = MultTT,
@@ -948,20 +1127,20 @@ run_spec_file(Relaxed,
{Run,Skip} = ct_testspec:prepare_tests(TS, node()),
reformat_result(catch do_run(Run, Skip, Opts1, StartOpts));
{error,GCFReason} ->
- exit(GCFReason)
+ exit({error,GCFReason})
end
end.
run_prepared(Run, Skip, Opts = #opts{logdir = LogDir,
- config = CfgFiles },
+ config = CfgFiles},
StartOpts) ->
LogDir1 = which(logdir, LogDir),
case check_and_install_configfiles(CfgFiles, LogDir1, Opts) of
ok ->
reformat_result(catch do_run(Run, Skip, Opts#opts{logdir = LogDir1},
StartOpts));
- {error,Reason} ->
- exit(Reason)
+ {error,_Reason} = Error ->
+ exit(Error)
end.
check_config_file(Callback, File)->
@@ -969,7 +1148,7 @@ check_config_file(Callback, File)->
false ->
case code:load_file(Callback) of
{module,_} -> ok;
- {error,Why} -> exit({cant_load_callback_module,Why})
+ {error,Why} -> exit({error,{cant_load_callback_module,Why}})
end;
_ ->
ok
@@ -980,16 +1159,17 @@ check_config_file(Callback, File)->
{ok,{config,_}}->
File;
{error,{wrong_config,Message}}->
- exit({wrong_config,{Callback,Message}});
+ exit({error,{wrong_config,{Callback,Message}}});
{error,{nofile,File}}->
- exit({no_such_file,?abs(File)})
+ exit({error,{no_such_file,?abs(File)}})
end.
run_dir(Opts = #opts{logdir = LogDir,
config = CfgFiles,
event_handlers = EvHandlers,
ct_hooks = CTHook,
- enable_builtin_hooks = EnableBuiltinHooks }, StartOpts) ->
+ enable_builtin_hooks = EnableBuiltinHooks},
+ StartOpts) ->
LogDir1 = which(logdir, LogDir),
Opts1 = Opts#opts{logdir = LogDir1},
AbsCfgFiles =
@@ -1002,7 +1182,8 @@ run_dir(Opts = #opts{logdir = LogDir,
{module,Callback}->
ok;
{error,_}->
- exit({no_such_module,Callback})
+ exit({error,{no_such_module,
+ Callback}})
end
end,
{Callback,
@@ -1015,7 +1196,7 @@ run_dir(Opts = #opts{logdir = LogDir,
{ct_hooks, CTHook},
{enable_builtin_hooks,EnableBuiltinHooks}], LogDir1) of
ok -> ok;
- {error,IReason} -> exit(IReason)
+ {error,_IReason} = IError -> exit(IError)
end,
case {proplists:get_value(dir, StartOpts),
proplists:get_value(suite, StartOpts),
@@ -1057,7 +1238,7 @@ run_dir(Opts = #opts{logdir = LogDir,
[], Opts1, StartOpts));
{undefined,[Hd,_|_],_GsAndCs} when not is_integer(Hd) ->
- exit(multiple_suites_and_cases);
+ exit({error,multiple_suites_and_cases});
{undefined,Suite=[Hd|Tl],GsAndCs} when is_integer(Hd) ;
(is_list(Hd) and (Tl == [])) ;
@@ -1067,10 +1248,10 @@ run_dir(Opts = #opts{logdir = LogDir,
[], Opts1, StartOpts));
{[Hd,_|_],_Suites,[]} when is_list(Hd) ; not is_integer(Hd) ->
- exit(multiple_dirs_and_suites);
+ exit({error,multiple_dirs_and_suites});
{undefined,undefined,GsAndCs} when GsAndCs /= [] ->
- exit(incorrect_start_options);
+ exit({error,incorrect_start_options});
{Dir,Suite,GsAndCs} when is_integer(hd(Dir)) ;
(is_atom(Dir) and (Dir /= undefined)) ;
@@ -1079,7 +1260,7 @@ run_dir(Opts = #opts{logdir = LogDir,
Dir1 = if is_atom(Dir) -> atom_to_list(Dir);
true -> Dir end,
if Suite == undefined ->
- exit(incorrect_start_options);
+ exit({error,incorrect_start_options});
is_integer(hd(Suite)) ;
(is_atom(Suite) and (Suite /= undefined)) ;
@@ -1098,7 +1279,7 @@ run_dir(Opts = #opts{logdir = LogDir,
is_list(Suite) -> % multiple suites
case [suite_to_test(Dir1, S) || S <- Suite] of
[_,_|_] when GsAndCs /= [] ->
- exit(multiple_suites_and_cases);
+ exit({error,multiple_suites_and_cases});
[{Dir2,Mod}] when GsAndCs /= [] ->
reformat_result(catch do_run(tests(Dir2, Mod, GsAndCs),
[], Opts1, StartOpts));
@@ -1109,10 +1290,10 @@ run_dir(Opts = #opts{logdir = LogDir,
end;
{undefined,undefined,[]} ->
- exit(no_test_specified);
+ exit({error,no_test_specified});
{Dir,Suite,GsAndCs} ->
- exit({incorrect_start_options,{Dir,Suite,GsAndCs}})
+ exit({error,{incorrect_start_options,{Dir,Suite,GsAndCs}}})
end.
%%%-----------------------------------------------------------------
@@ -1157,7 +1338,7 @@ run_testspec2(File) when is_list(File), is_integer(hd(File)) ->
run_testspec2(TestSpec) ->
case catch ct_testspec:collect_tests_from_list(TestSpec, false) of
{E,CTReason} when E == error ; E == 'EXIT' ->
- exit(CTReason);
+ exit({error,CTReason});
TS ->
Opts = get_data_for_node(TS, node()),
@@ -1179,8 +1360,8 @@ run_testspec2(TestSpec) ->
include = AllInclude},
{Run,Skip} = ct_testspec:prepare_tests(TS, node()),
reformat_result(catch do_run(Run, Skip, Opts1, []));
- {error,GCFReason} ->
- exit(GCFReason)
+ {error,_GCFReason} = GCFError ->
+ exit(GCFError)
end
end.
@@ -1188,12 +1369,17 @@ get_data_for_node(#testspec{label = Labels,
profile = Profiles,
logdir = LogDirs,
logopts = LogOptsList,
+ basic_html = BHs,
+ stylesheet = SSs,
+ verbosity = VLvls,
+ silent_connections = SilentConnsList,
cover = CoverFs,
config = Cfgs,
userconfig = UsrCfgs,
event_handler = EvHs,
ct_hooks = CTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
+ auto_compile = ACs,
include = Incl,
multiply_timetraps = MTs,
scale_timetraps = STs,
@@ -1208,6 +1394,16 @@ get_data_for_node(#testspec{label = Labels,
undefined -> [];
LOs -> LOs
end,
+ BasicHtml = proplists:get_value(Node, BHs),
+ Stylesheet = proplists:get_value(Node, SSs),
+ Verbosity = case proplists:get_value(Node, VLvls) of
+ undefined -> [];
+ Lvls -> Lvls
+ end,
+ SilentConns = case proplists:get_value(Node, SilentConnsList) of
+ undefined -> [];
+ SCs -> SCs
+ end,
Cover = proplists:get_value(Node, CoverFs),
MT = proplists:get_value(Node, MTs),
ST = proplists:get_value(Node, STs),
@@ -1216,16 +1412,22 @@ get_data_for_node(#testspec{label = Labels,
[CBF || {N,CBF} <- UsrCfgs, N==Node],
EvHandlers = [{H,A} || {N,H,A} <- EvHs, N==Node],
FiltCTHooks = [Hook || {N,Hook} <- CTHooks, N==Node],
+ AutoCompile = proplists:get_value(Node, ACs),
Include = [I || {N,I} <- Incl, N==Node],
#opts{label = Label,
profile = Profile,
logdir = LogDir,
logopts = LogOpts,
+ basic_html = BasicHtml,
+ stylesheet = Stylesheet,
+ verbosity = Verbosity,
+ silent_connections = SilentConns,
cover = Cover,
config = ConfigFiles,
event_handlers = EvHandlers,
ct_hooks = FiltCTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
+ auto_compile = AutoCompile,
include = Include,
multiply_timetraps = MT,
scale_timetraps = ST,
@@ -1267,6 +1469,14 @@ choose_val(V0, _V1) ->
merge_vals(Vs) ->
lists:append(Vs).
+merge_keyvals(Vs) ->
+ make_unique(lists:append(Vs)).
+
+make_unique([Elem={Key,_} | Elems]) ->
+ [Elem | make_unique(proplists:delete(Key, Elems))];
+make_unique([]) ->
+ [].
+
listify([C|_]=Str) when is_integer(C) -> [Str];
listify(L) when is_list(L) -> L;
listify(E) -> [E].
@@ -1376,7 +1586,8 @@ do_run(Tests, Misc, LogDir, LogOpts) when is_list(Misc),
do_run(Tests, [], Opts1#opts{logdir = LogDir}, []);
do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) ->
- #opts{label = Label, profile = Profile, cover = Cover} = Opts,
+ #opts{label = Label, profile = Profile, cover = Cover,
+ verbosity = VLvls} = Opts,
%% label - used by ct_logs
TestLabel =
if Label == undefined -> undefined;
@@ -1397,7 +1608,7 @@ do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) ->
case code:which(test_server) of
non_existing ->
- exit({error,no_path_to_test_server});
+ {error,no_path_to_test_server};
_ ->
Opts1 = if Cover == undefined ->
Opts;
@@ -1418,77 +1629,131 @@ do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) ->
"ct_framework" ->
ok;
Other ->
- erlang:display(list_to_atom("Note: TEST_SERVER_FRAMEWORK = " ++ Other))
+ erlang:display(
+ list_to_atom(
+ "Note: TEST_SERVER_FRAMEWORK = " ++ Other))
end,
- case ct_util:start(Opts#opts.logdir) of
+ Verbosity = add_verbosity_defaults(VLvls),
+ case ct_util:start(Opts#opts.logdir, Verbosity) of
{error,interactive_mode} ->
io:format("CT is started in interactive mode. "
- "To exit this mode, run ct:stop_interactive().\n"
+ "To exit this mode, "
+ "run ct:stop_interactive().\n"
"To enter the interactive mode again, "
"run ct:start_interactive()\n\n",[]),
{error,interactive_mode};
_Pid ->
- %% save stylesheet info
- ct_util:set_testdata({stylesheet,Opts#opts.stylesheet}),
- %% save logopts
- ct_util:set_testdata({logopts,Opts#opts.logopts}),
- %% enable silent connections
- case Opts#opts.silent_connections of
- [] ->
- Conns = ct_util:override_silence_all_connections(),
- ct_logs:log("Silent connections", "~p", [Conns]);
- Conns when is_list(Conns) ->
- ct_util:override_silence_connections(Conns),
- ct_logs:log("Silent connections", "~p", [Conns]);
- _ ->
- ok
- end,
- log_ts_names(Opts1#opts.testspecs),
- TestSuites = suite_tuples(Tests),
-
- {_TestSuites1,SuiteMakeErrors,AllMakeErrors} =
- case application:get_env(common_test, auto_compile) of
- {ok,false} ->
- {TestSuites1,SuitesNotFound} =
- verify_suites(TestSuites),
- {TestSuites1,SuitesNotFound,SuitesNotFound};
- _ ->
- {SuiteErrs,HelpErrs} = auto_compile(TestSuites),
- {TestSuites,SuiteErrs,SuiteErrs++HelpErrs}
- end,
+ ct_util:set_testdata({starter,Opts#opts.starter}),
+ compile_and_run(Tests, Skip,
+ Opts1#opts{verbosity=Verbosity}, Args)
+ end
+ end.
- case continue(AllMakeErrors) of
- true ->
- SavedErrors = save_make_errors(SuiteMakeErrors),
- ct_repeat:log_loop_info(Args),
+compile_and_run(Tests, Skip, Opts, Args) ->
+ %% save stylesheet info
+ ct_util:set_testdata({stylesheet,Opts#opts.stylesheet}),
+ %% save logopts
+ ct_util:set_testdata({logopts,Opts#opts.logopts}),
+ %% enable silent connections
+ case Opts#opts.silent_connections of
+ [] ->
+ ok;
+ Conns ->
+ case lists:member(all, Conns) of
+ true ->
+ Conns1 = ct_util:override_silence_all_connections(),
+ ct_logs:log("Silent connections", "~p", [Conns1]);
+ false ->
+ ct_util:override_silence_connections(Conns),
+ ct_logs:log("Silent connections", "~p", [Conns])
+ end
+ end,
+ log_ts_names(Opts#opts.testspecs),
+ TestSuites = suite_tuples(Tests),
+
+ {_TestSuites1,SuiteMakeErrors,AllMakeErrors} =
+ case application:get_env(common_test, auto_compile) of
+ {ok,false} ->
+ {TestSuites1,SuitesNotFound} =
+ verify_suites(TestSuites),
+ {TestSuites1,SuitesNotFound,SuitesNotFound};
+ _ ->
+ {SuiteErrs,HelpErrs} = auto_compile(TestSuites),
+ {TestSuites,SuiteErrs,SuiteErrs++HelpErrs}
+ end,
+
+ case continue(AllMakeErrors) of
+ true ->
+ SavedErrors = save_make_errors(SuiteMakeErrors),
+ ct_repeat:log_loop_info(Args),
+
+ {Tests1,Skip1} = final_tests(Tests,Skip,SavedErrors),
+
+ ReleaseSh = proplists:get_value(release_shell, Args),
+ ct_util:set_testdata({release_shell,ReleaseSh}),
+ possibly_spawn(ReleaseSh == true, Tests1, Skip1, Opts);
+ false ->
+ io:nl(),
+ ct_util:stop(clean),
+ BadMods =
+ lists:foldr(
+ fun({{_,_},Ms}, Acc) ->
+ Ms ++ lists:foldl(
+ fun(M, Acc1) ->
+ lists:delete(M, Acc1)
+ end, Acc, Ms)
+ end, [], AllMakeErrors),
+ {error,{make_failed,BadMods}}
+ end.
- {Tests1,Skip1} = final_tests(Tests,Skip,SavedErrors),
+%% keep the shell as the top controlling process
+possibly_spawn(false, Tests, Skip, Opts) ->
+ TestResult = (catch do_run_test(Tests, Skip, Opts)),
+ case TestResult of
+ {EType,_} = Error when EType == user_error;
+ EType == error ->
+ ct_util:stop(clean),
+ exit(Error);
+ _ ->
+ ct_util:stop(normal),
+ TestResult
+ end;
- R = (catch do_run_test(Tests1, Skip1, Opts1)),
- case R of
- {EType,_} = Error when EType == user_error ;
+%% we must return control to the shell now, so we spawn
+%% a test supervisor process to keep an eye on the test run
+possibly_spawn(true, Tests, Skip, Opts) ->
+ CTUtilSrv = whereis(ct_util_server),
+ Supervisor =
+ fun() ->
+ process_flag(trap_exit, true),
+ link(CTUtilSrv),
+ TestRun =
+ fun() ->
+ TestResult = (catch do_run_test(Tests, Skip, Opts)),
+ case TestResult of
+ {EType,_} = Error when EType == user_error;
EType == error ->
ct_util:stop(clean),
exit(Error);
_ ->
ct_util:stop(normal),
- R
- end;
- false ->
- io:nl(),
- ct_util:stop(clean),
- BadMods =
- lists:foldr(
- fun({{_,_},Ms}, Acc) ->
- Ms ++ lists:foldl(
- fun(M, Acc1) ->
- lists:delete(M, Acc1)
- end, Acc, Ms)
- end, [], AllMakeErrors),
- {error,{make_failed,BadMods}}
- end
- end
- end.
+ exit({ok,TestResult})
+ end
+ end,
+ TestRunPid = spawn_link(TestRun),
+ receive
+ {'EXIT',TestRunPid,{ok,TestResult}} ->
+ io:format(user, "~nCommon Test returned ~p~n~n",
+ [TestResult]);
+ {'EXIT',TestRunPid,Error} ->
+ exit(Error)
+ end
+ end,
+ unlink(CTUtilSrv),
+ SupPid = spawn(Supervisor),
+ io:format(user, "~nTest control handed over to process ~p~n~n",
+ [SupPid]),
+ SupPid.
%% attempt to compile the modules specified in TestSuites
auto_compile(TestSuites) ->
@@ -1504,11 +1769,13 @@ auto_compile(TestSuites) ->
end,
SuiteMakeErrors =
lists:flatmap(fun({TestDir,Suite} = TS) ->
- case run_make(suites, TestDir, Suite, UserInclude) of
+ case run_make(suites, TestDir,
+ Suite, UserInclude) of
{error,{make_failed,Bad}} ->
[{TS,Bad}];
{error,_} ->
- [{TS,[filename:join(TestDir,"*_SUITE")]}];
+ [{TS,[filename:join(TestDir,
+ "*_SUITE")]}];
_ ->
[]
end
@@ -1547,23 +1814,29 @@ verify_suites(TestSuites) ->
{[DS|Found],NotFound};
true ->
Beam = filename:join(TestDir,
- atom_to_list(Suite)++".beam"),
+ atom_to_list(Suite)++
+ ".beam"),
case filelib:is_regular(Beam) of
true ->
{[DS|Found],NotFound};
false ->
case code:is_loaded(Suite) of
{file,SuiteFile} ->
- %% test suite is already loaded and
- %% since auto_compile == false,
+ %% test suite is already
+ %% loaded and since
+ %% auto_compile == false,
%% let's assume the user has
- %% loaded the beam file explicitly
- ActualDir = filename:dirname(SuiteFile),
- {[{ActualDir,Suite}|Found],NotFound};
+ %% loaded the beam file
+ %% explicitly
+ ActualDir =
+ filename:dirname(SuiteFile),
+ {[{ActualDir,Suite}|Found],
+ NotFound};
false ->
Name =
filename:join(TestDir,
- atom_to_list(Suite)),
+ atom_to_list(
+ Suite)),
io:format(user,
"Suite ~w not found"
"in directory ~s~n",
@@ -1581,7 +1854,8 @@ verify_suites(TestSuites) ->
ActualDir = filename:dirname(SuiteFile),
{[{ActualDir,Suite}|Found],NotFound};
false ->
- io:format(user, "Directory ~s is invalid~n", [Dir]),
+ io:format(user, "Directory ~s is "
+ "invalid~n", [Dir]),
Name = filename:join(Dir, atom_to_list(Suite)),
{Found,[{DS,[Name]}|NotFound]}
end
@@ -1595,7 +1869,8 @@ save_make_errors([]) ->
save_make_errors(Errors) ->
Suites = get_bad_suites(Errors,[]),
ct_logs:log("MAKE RESULTS",
- "Error compiling or locating the following suites: ~n~p",[Suites]),
+ "Error compiling or locating the "
+ "following suites: ~n~p",[Suites]),
%% save the info for logger
file:write_file(?missing_suites_info,term_to_binary(Errors)),
Errors.
@@ -1616,8 +1891,9 @@ step(TestDir, Suite, Case) ->
%%%-----------------------------------------------------------------
%%% @hidden
%%% @equiv ct:step/4
-step(TestDir, Suite, Case, Opts) when is_list(TestDir), is_atom(Suite), is_atom(Case),
- Suite =/= all, Case =/= all ->
+step(TestDir, Suite, Case, Opts) when is_list(TestDir),
+ is_atom(Suite), is_atom(Case),
+ Suite =/= all, Case =/= all ->
do_run([{TestDir,Suite,Case}], [{step,Opts}]).
@@ -1735,9 +2011,11 @@ continue(_MakeErrors) ->
case set_group_leader_same_as_shell() of
true ->
S = self(),
- io:format("Failed to compile or locate one or more test suites\n"
+ io:format("Failed to compile or locate one "
+ "or more test suites\n"
"Press \'c\' to continue or \'a\' to abort.\n"
- "Will continue in 15 seconds if no answer is given!\n"),
+ "Will continue in 15 seconds if no "
+ "answer is given!\n"),
Pid = spawn(fun() ->
case io:get_line('(c/a) ') of
"c\n" ->
@@ -1769,7 +2047,8 @@ set_group_leader_same_as_shell() ->
end
end,
case [P || P <- processes(), GS2or3(P),
- true == lists:keymember(shell,1,element(2,process_info(P,dictionary)))] of
+ true == lists:keymember(shell,1,
+ element(2,process_info(P,dictionary)))] of
[GL|_] ->
group_leader(GL, self());
[] ->
@@ -1815,12 +2094,14 @@ do_run_test(Tests, Skip, Opts) ->
incl_mods = CovIncl,
cross = CovCross,
src = _CovSrc}} ->
- ct_logs:log("COVER INFO","Using cover specification file: ~s~n"
+ ct_logs:log("COVER INFO",
+ "Using cover specification file: ~s~n"
"App: ~w~n"
"Cross cover: ~w~n"
"Including ~w modules~n"
"Excluding ~w modules",
- [CovFile,CovApp,CovCross,length(CovIncl),length(CovExcl)]),
+ [CovFile,CovApp,CovCross,
+ length(CovIncl),length(CovExcl)]),
%% cover export file will be used for export and import
%% between tests so make sure it doesn't exist initially
@@ -1828,7 +2109,8 @@ do_run_test(Tests, Skip, Opts) ->
true ->
DelResult = file:delete(CovExport),
ct_logs:log("COVER INFO",
- "Warning! Export file ~s already exists. "
+ "Warning! "
+ "Export file ~s already exists. "
"Deleting with result: ~p",
[CovExport,DelResult]);
false ->
@@ -1844,7 +2126,8 @@ do_run_test(Tests, Skip, Opts) ->
%% start cover on specified nodes
if (CovNodes /= []) and (CovNodes /= undefined) ->
ct_logs:log("COVER INFO",
- "Nodes included in cover session: ~w",
+ "Nodes included in cover "
+ "session: ~w",
[CovNodes]),
cover:start(CovNodes);
true ->
@@ -1869,17 +2152,27 @@ do_run_test(Tests, Skip, Opts) ->
ct_logs:log("TEST INFO","~w test(s), ~w suite(s)",
[NoOfTests,NoOfSuites]);
true ->
- io:format("~nTEST INFO: ~w test(s), ~w case(s) in ~w suite(s)~n~n",
+ io:format("~nTEST INFO: ~w test(s), ~w case(s) "
+ "in ~w suite(s)~n~n",
[NoOfTests,NoOfCases,NoOfSuites]),
- ct_logs:log("TEST INFO","~w test(s), ~w case(s) in ~w suite(s)",
+ ct_logs:log("TEST INFO","~w test(s), ~w case(s) "
+ "in ~w suite(s)",
[NoOfTests,NoOfCases,NoOfSuites])
end,
-
+ %% if the verbosity level is set lower than ?STD_IMPORTANCE, tell
+ %% test_server to ignore stdout printouts to the test case log file
+ case proplists:get_value(default, Opts#opts.verbosity) of
+ VLvl when is_integer(VLvl), (?STD_IMPORTANCE < (100-VLvl)) ->
+ test_server_ctrl:reject_io_reqs(true);
+ _Lower ->
+ ok
+ end,
test_server_ctrl:multiply_timetraps(Opts#opts.multiply_timetraps),
test_server_ctrl:scale_timetraps(Opts#opts.scale_timetraps),
- test_server_ctrl:create_priv_dir(choose_val(Opts#opts.create_priv_dir,
- auto_per_run)),
+ test_server_ctrl:create_priv_dir(choose_val(
+ Opts#opts.create_priv_dir,
+ auto_per_run)),
ct_event:notify(#event{name=start_info,
node=node(),
data={NoOfTests,NoOfSuites,NoOfCases}}),
@@ -1898,9 +2191,24 @@ do_run_test(Tests, Skip, Opts) ->
maybe_cleanup_interpret(Suite, Opts#opts.step)
end, CleanUp),
[code:del_path(Dir) || Dir <- AddedToPath],
- ok;
+
+ %% If a severe error has occurred in the test_server,
+ %% we will generate an exception here.
+ case ct_util:get_testdata(severe_error) of
+ undefined -> ok;
+ SevereError ->
+ ct_logs:log("SEVERE ERROR", "~p\n", [SevereError]),
+ exit(SevereError)
+ end,
+
+ case ct_util:get_testdata(stats) of
+ Stats = {_Ok,_Failed,{_UserSkipped,_AutoSkipped}} ->
+ Stats;
+ _ ->
+ {error,test_result_unknown}
+ end;
Error ->
- Error
+ exit(Error)
end.
delete_dups([S | Suites]) ->
@@ -2357,7 +2665,6 @@ parse_cth_args(String) ->
String
end.
-
event_handler_args2opts(Args) ->
case proplists:get_value(event_handler, Args) of
undefined ->
@@ -2380,6 +2687,42 @@ event_handler_init_args2opts([EH, Arg]) ->
event_handler_init_args2opts([]) ->
[].
+verbosity_args2opts(Args) ->
+ case proplists:get_value(verbosity, Args) of
+ undefined ->
+ [];
+ VArgs ->
+ GetVLvls =
+ fun("and", {new,SoFar}) when is_list(SoFar) ->
+ {new,SoFar};
+ ("and", {Lvl,SoFar}) when is_list(SoFar) ->
+ {new,[{'$unspecified',list_to_integer(Lvl)} | SoFar]};
+ (CatOrLvl, {new,SoFar}) when is_list(SoFar) ->
+ {CatOrLvl,SoFar};
+ (Lvl, {Cat,SoFar}) ->
+ {new,[{list_to_atom(Cat),list_to_integer(Lvl)} | SoFar]}
+ end,
+ case lists:foldl(GetVLvls, {new,[]}, VArgs) of
+ {new,Parsed} ->
+ Parsed;
+ {Lvl,Parsed} ->
+ [{'$unspecified',list_to_integer(Lvl)} | Parsed]
+ end
+ end.
+
+add_verbosity_defaults(VLvls) ->
+ case {proplists:get_value('$unspecified', VLvls),
+ proplists:get_value(default, VLvls)} of
+ {undefined,undefined} ->
+ ?default_verbosity ++ VLvls;
+ {Lvl,undefined} ->
+ [{default,Lvl} | VLvls];
+ {undefined,_Lvl} ->
+ [{'$unspecified',?MAX_VERBOSITY} | VLvls];
+ _ ->
+ VLvls
+ end.
+
%% This function reads pa and pz arguments, converts dirs from relative
%% to absolute, and re-inserts them in the code path. The order of the
%% dirs in the code path remain the same. Note however that since this
@@ -2446,7 +2789,13 @@ make_abs1([], Path) ->
%% to ct_run start arguments (on the init arguments format) -
%% this is useful mainly for testing the ct_run start functions.
opts2args(EnvStartOpts) ->
- lists:flatmap(fun({config,CfgFiles}) ->
+ lists:flatmap(fun({exit_status,ExitStatusOpt}) when is_atom(ExitStatusOpt) ->
+ [{exit_status,[atom_to_list(ExitStatusOpt)]}];
+ ({halt_with,{HaltM,HaltF}}) ->
+ [{halt_with,[atom_to_list(HaltM),atom_to_list(HaltF)]}];
+ ({interactive_mode,true}) ->
+ [{shell,[]}];
+ ({config,CfgFiles}) ->
[{ct_config,[CfgFiles]}];
({userconfig,{CBM,CfgStr=[X|_]}}) when is_integer(X) ->
[{userconfig,[atom_to_list(CBM),CfgStr]}];
@@ -2454,10 +2803,14 @@ opts2args(EnvStartOpts) ->
[{userconfig,[atom_to_list(CBM) | CfgStrs]}];
({userconfig,UserCfg}) when is_list(UserCfg) ->
Strs =
- lists:map(fun({CBM,CfgStr=[X|_]}) when is_integer(X) ->
- [atom_to_list(CBM),CfgStr,"and"];
- ({CBM,CfgStrs}) when is_list(CfgStrs) ->
- [atom_to_list(CBM) | CfgStrs] ++ ["and"]
+ lists:map(fun({CBM,CfgStr=[X|_]})
+ when is_integer(X) ->
+ [atom_to_list(CBM),
+ CfgStr,"and"];
+ ({CBM,CfgStrs})
+ when is_list(CfgStrs) ->
+ [atom_to_list(CBM) | CfgStrs] ++
+ ["and"]
end, UserCfg),
[_LastAnd|StrsR] = lists:reverse(lists:flatten(Strs)),
[{userconfig,lists:reverse(StrsR)}];
@@ -2492,7 +2845,7 @@ opts2args(EnvStartOpts) ->
({decrypt,{file,File}}) ->
[{ct_decrypt_file,[File]}];
({basic_html,true}) ->
- ({basic_html,[]});
+ [{basic_html,[]}];
({basic_html,false}) ->
[];
({event_handler,EH}) when is_atom(EH) ->
@@ -2505,12 +2858,32 @@ opts2args(EnvStartOpts) ->
({event_handler,{EHs,Arg}}) when is_list(EHs) ->
ArgStr = lists:flatten(io_lib:format("~p", [Arg])),
Strs = lists:map(fun(EH) ->
- [atom_to_list(EH),ArgStr,"and"]
+ [atom_to_list(EH),
+ ArgStr,"and"]
end, EHs),
[_LastAnd|StrsR] = lists:reverse(lists:flatten(Strs)),
[{event_handler_init,lists:reverse(StrsR)}];
({logopts,LOs}) when is_list(LOs) ->
[{logopts,[atom_to_list(LO) || LO <- LOs]}];
+ ({verbosity,?default_verbosity}) ->
+ [];
+ ({verbosity,VLvl}) when is_integer(VLvl) ->
+ [{verbosity,[integer_to_list(VLvl)]}];
+ ({verbosity,VLvls}) when is_list(VLvls) ->
+ VLvlArgs =
+ lists:flatmap(fun({'$unspecified',Lvl}) ->
+ [integer_to_list(Lvl),
+ "and"];
+ ({Cat,Lvl}) ->
+ [atom_to_list(Cat),
+ integer_to_list(Lvl),
+ "and"];
+ (Lvl) ->
+ [integer_to_list(Lvl),
+ "and"]
+ end, VLvls),
+ [_LastAnd|VLvlArgsR] = lists:reverse(VLvlArgs),
+ [{verbosity,lists:reverse(VLvlArgsR)}];
({ct_hooks,[]}) ->
[];
({ct_hooks,CTHs}) when is_list(CTHs) ->
diff --git a/lib/common_test/src/ct_snmp.erl b/lib/common_test/src/ct_snmp.erl
index 8fe63e8ed1..02f849201d 100644
--- a/lib/common_test/src/ct_snmp.erl
+++ b/lib/common_test/src/ct_snmp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. 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
@@ -250,10 +250,8 @@ stop(Config) ->
%%%
%%% @doc Issues a synchronous snmp get request.
get_values(Agent, Oids, MgrAgentConfName) ->
- [Uid, AgentIp, AgentUdpPort | _] =
- agent_conf(Agent, MgrAgentConfName),
- {ok, SnmpReply, _} =
- snmpm:g(Uid, AgentIp, AgentUdpPort, Oids),
+ [Uid | _] = agent_conf(Agent, MgrAgentConfName),
+ {ok, SnmpReply, _} = snmpm:sync_get2(Uid, target_name(Agent), Oids),
SnmpReply.
%%% @spec get_next_values(Agent, Oids, MgrAgentConfName) -> SnmpReply
@@ -265,10 +263,8 @@ get_values(Agent, Oids, MgrAgentConfName) ->
%%%
%%% @doc Issues a synchronous snmp get next request.
get_next_values(Agent, Oids, MgrAgentConfName) ->
- [Uid, AgentIp, AgentUdpPort | _] =
- agent_conf(Agent, MgrAgentConfName),
- {ok, SnmpReply, _} =
- snmpm:gn(Uid, AgentIp, AgentUdpPort, Oids),
+ [Uid | _] = agent_conf(Agent, MgrAgentConfName),
+ {ok, SnmpReply, _} = snmpm:sync_get_next2(Uid, target_name(Agent), Oids),
SnmpReply.
%%% @spec set_values(Agent, VarsAndVals, MgrAgentConfName, Config) -> SnmpReply
@@ -282,13 +278,11 @@ get_next_values(Agent, Oids, MgrAgentConfName) ->
%%% @doc Issues a synchronous snmp set request.
set_values(Agent, VarsAndVals, MgrAgentConfName, Config) ->
PrivDir = ?config(priv_dir, Config),
- [Uid, AgentIp, AgentUdpPort | _] =
- agent_conf(Agent, MgrAgentConfName),
+ [Uid | _] = agent_conf(Agent, MgrAgentConfName),
Oids = lists:map(fun({Oid, _, _}) -> Oid end, VarsAndVals),
- {ok, SnmpGetReply, _} =
- snmpm:g(Uid, AgentIp, AgentUdpPort, Oids),
- {ok, SnmpSetReply, _} =
- snmpm:s(Uid, AgentIp, AgentUdpPort, VarsAndVals),
+ TargetName = target_name(Agent),
+ {ok, SnmpGetReply, _} = snmpm:sync_get2(Uid, TargetName, Oids),
+ {ok, SnmpSetReply, _} = snmpm:sync_set2(Uid, TargetName, VarsAndVals),
case SnmpSetReply of
{noError, 0, _} when PrivDir /= false ->
log(PrivDir, Agent, SnmpGetReply, VarsAndVals);
@@ -348,7 +342,7 @@ register_agents(MgrAgentConfName, ManagedAgents) ->
NewSnmpVals = lists:keyreplace(managed_agents, 1, SnmpVals,
{managed_agents, ManagedAgents}),
ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
- setup_managed_agents(ManagedAgents).
+ setup_managed_agents(MgrAgentConfName,ManagedAgents).
%%% @spec register_usm_users(MgrAgentConfName, UsmUsers) -> ok | {error, Reason}
%%%
@@ -486,9 +480,8 @@ setup_agent(true, AgentConfName, SnmpConfName,
file:make_dir(DbDir),
snmp_config:write_agent_snmp_files(ConfDir, Vsns, ManagerIP, TrapUdp,
AgentIP, AgentUdp, SysName,
- atom_to_list(NotifType),
- SecType, Passwd, AgentEngineID,
- AgentMaxMsgSize),
+ NotifType, SecType, Passwd,
+ AgentEngineID, AgentMaxMsgSize),
override_default_configuration(Config, AgentConfName),
@@ -497,7 +490,8 @@ setup_agent(true, AgentConfName, SnmpConfName,
{verbosity, trace}]},
{agent_type, master},
{agent_verbosity, trace},
- {net_if, [{verbosity, trace}]}],
+ {net_if, [{verbosity, trace}]},
+ {versions, Vsns}],
ct:get_config({SnmpConfName,agent})),
application:set_env(snmp, agent, SnmpEnv).
%%%---------------------------------------------------------------------------
@@ -535,7 +529,7 @@ manager_register(true, MgrAgentConfName) ->
setup_usm_users(UsmUsers, EngineID),
setup_users(Users),
- setup_managed_agents(Agents).
+ setup_managed_agents(MgrAgentConfName,Agents).
%%%---------------------------------------------------------------------------
setup_users(Users) ->
@@ -543,10 +537,11 @@ setup_users(Users) ->
snmpm:register_user(Id, Module, Data)
end, Users).
%%%---------------------------------------------------------------------------
-setup_managed_agents([]) ->
+setup_managed_agents(_,[]) ->
ok;
-setup_managed_agents([{_, [Uid, AgentIp, AgentUdpPort, AgentConf]} |
+setup_managed_agents(AgentConfName,
+ [{AgentName, [Uid, AgentIp, AgentUdpPort, AgentConf0]} |
Rest]) ->
NewAgentIp = case AgentIp of
IpTuple when is_tuple(IpTuple) ->
@@ -556,12 +551,19 @@ setup_managed_agents([{_, [Uid, AgentIp, AgentUdpPort, AgentConf]} |
[IpTuple|_] = Hostent#hostent.h_addr_list,
IpTuple
end,
- ok = snmpm:register_agent(Uid, NewAgentIp, AgentUdpPort),
- lists:foreach(fun({Item, Val}) ->
- snmpm:update_agent_info(Uid, NewAgentIp,
- AgentUdpPort, Item, Val)
- end, AgentConf),
- setup_managed_agents(Rest).
+ AgentConf =
+ case lists:keymember(engine_id,1,AgentConf0) of
+ true ->
+ AgentConf0;
+ false ->
+ DefaultEngineID = ct:get_config({AgentConfName,agent_engine_id},
+ ?AGENT_ENGINE_ID),
+ [{engine_id,DefaultEngineID}|AgentConf0]
+ end,
+ ok = snmpm:register_agent(Uid, target_name(AgentName),
+ [{address,NewAgentIp},{port,AgentUdpPort} |
+ AgentConf]),
+ setup_managed_agents(AgentConfName,Rest).
%%%---------------------------------------------------------------------------
setup_usm_users(UsmUsers, EngineID)->
lists:foreach(fun({UsmUser, Conf}) ->
@@ -769,3 +771,8 @@ override_vacm(Config, VacmConf) ->
File = filename:join(Dir,"vacm.conf"),
file:delete(File),
snmp_config:write_agent_vacm_config(Dir, "", VacmConf).
+
+%%%---------------------------------------------------------------------------
+
+target_name(Agent) ->
+ atom_to_list(Agent).
diff --git a/lib/common_test/src/ct_ssh.erl b/lib/common_test/src/ct_ssh.erl
index aebb28bc42..c6ea27b10e 100644
--- a/lib/common_test/src/ct_ssh.erl
+++ b/lib/common_test/src/ct_ssh.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. 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
@@ -133,10 +133,11 @@ connect(KeyOrName, ExtraOpts) when is_list(ExtraOpts) ->
%%% is used to identify the connection, this name may
%%% be used as connection reference for subsequent calls.
%%% It's only possible to have one open connection at a time
-%%% associated with <code>Name</code>. If <code>Key</code> is
+%%% associated with <code>Name</code>. If <code>Key</code> is
%%% used, the returned handle must be used for subsequent calls
%%% (multiple connections may be opened using the config
-%%% data specified by <code>Key</code>).</p>
+%%% data specified by <code>Key</code>). See <c>ct:require/2</c>
+%%% for how to create a new <c>Name</c></p>
%%%
%%% <p><code>ConnType</code> will always override the type
%%% specified in the address tuple in the configuration data (and
@@ -152,6 +153,8 @@ connect(KeyOrName, ExtraOpts) when is_list(ExtraOpts) ->
%%% The extra options will override any existing options with the
%%% same key in the config data. For details on valid SSH
%%% options, see the documentation for the OTP ssh application.</p>
+%%%
+%%% @see ct:require/2
connect(KeyOrName, ConnType, ExtraOpts) ->
case ct:get_config(KeyOrName) of
undefined ->
@@ -182,19 +185,22 @@ connect(KeyOrName, ConnType, ExtraOpts) ->
undefined ->
{ssh,undefined,AllOpts};
SFTPAddr ->
- log(heading(connect,KeyOrName),
- "Note: Opening ssh connection to sftp host.\n",
+ try_log(heading(connect,KeyOrName),
+ "Note: Opening ssh connection "
+ "to sftp host.\n",
[]),
{ssh,SFTPAddr,
- [{ssh,SFTPAddr}|proplists:delete(sftp, AllOpts)]}
+ [{ssh,SFTPAddr} |
+ proplists:delete(sftp, AllOpts)]}
end;
undefined when ConnType == sftp ->
case proplists:get_value(ssh, AllOpts) of
undefined ->
{sftp,undefined,AllOpts};
SSHAddr ->
- log(heading(connect,KeyOrName),
- "Note: Opening sftp connection to ssh host.\n",
+ try_log(heading(connect,KeyOrName),
+ "Note: Opening sftp connection "
+ "to ssh host.\n",
[]),
{sftp,SSHAddr,
[{sftp,SSHAddr}|proplists:delete(ssh, AllOpts)]}
@@ -209,15 +215,15 @@ connect(KeyOrName, ConnType, ExtraOpts) ->
[{not_available,{KeyOrName,ConnType1}}]),
{error,{not_available,{KeyOrName,ConnType1}}};
{_,undefined} ->
- log(heading(connect,KeyOrName),
- "Opening ~w connection to ~p:22\n",
- [ConnType1,Addr]),
+ try_log(heading(connect,KeyOrName),
+ "Opening ~w connection to ~p:22\n",
+ [ConnType1,Addr]),
ct_gen_conn:start(KeyOrName, {ConnType1,Addr,22},
AllOpts1, ?MODULE);
{_,Port} ->
- log(heading(connect,KeyOrName),
- "Opening ~w connection to ~p:~w\n",
- [ConnType1,Addr,Port]),
+ try_log(heading(connect,KeyOrName),
+ "Opening ~w connection to ~p:~w\n",
+ [ConnType1,Addr,Port]),
ct_gen_conn:start(KeyOrName, {ConnType1,Addr,Port},
AllOpts1, ?MODULE)
end
@@ -232,7 +238,7 @@ connect(KeyOrName, ConnType, ExtraOpts) ->
disconnect(SSH) ->
case get_handle(SSH) of
{ok,Pid} ->
- log(heading(disconnect,SSH), "Handle: ~p", [Pid]),
+ try_log(heading(disconnect,SSH), "Handle: ~p", [Pid], 5000),
case ct_gen_conn:stop(Pid) of
{error,{process_down,Pid,noproc}} ->
{error,already_closed};
@@ -968,8 +974,9 @@ init(KeyOrName, {ConnType,Addr,Port}, AllOpts) ->
Error;
Ok ->
SSHRef = element(2, Ok),
- log(heading(init,KeyOrName),
- "Opened ~w connection:\nHost: ~p (~p)\nUser: ~p\nPassword: ~p\n",
+ try_log(heading(init,KeyOrName),
+ "Opened ~w connection:\n"
+ "Host: ~p (~p)\nUser: ~p\nPassword: ~p\n",
[ConnType,Addr,Port,User,lists:duplicate(length(Password),$*)]),
{ok,SSHRef,#state{ssh_ref=SSHRef, conn_type=ConnType,
target=KeyOrName}}
@@ -978,25 +985,26 @@ init(KeyOrName, {ConnType,Addr,Port}, AllOpts) ->
%% @hidden
handle_msg(sftp_connect, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
- log(heading(sftp_connect,Target), "SSH Ref: ~p", [SSHRef]),
+ try_log(heading(sftp_connect,Target), "SSH Ref: ~p", [SSHRef]),
{ssh_sftp:start_channel(SSHRef),State};
handle_msg({session_open,TO}, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
- log(heading(session_open,Target), "SSH Ref: ~p, Timeout: ~p", [SSHRef,TO]),
+ try_log(heading(session_open,Target), "SSH Ref: ~p, Timeout: ~p",
+ [SSHRef,TO]),
{ssh_connection:session_channel(SSHRef, TO),State};
handle_msg({session_close,Chn}, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
- log(heading(session_close,Target), "SSH Ref: ~p, Chn: ~p", [SSHRef,Chn]),
+ try_log(heading(session_close,Target), "SSH Ref: ~p, Chn: ~p", [SSHRef,Chn]),
{ssh_connection:close(SSHRef, Chn),State};
handle_msg({exec,Chn,Command,TO}, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
Chn1 =
if Chn == undefined ->
- log(heading(exec,Target),
- "Opening channel for exec, SSH Ref: ~p", [SSHRef]),
+ try_log(heading(exec,Target),
+ "Opening channel for exec, SSH Ref: ~p", [SSHRef]),
case ssh_connection:session_channel(SSHRef, TO) of
{ok,C} -> C;
CErr -> CErr
@@ -1009,9 +1017,9 @@ handle_msg({exec,Chn,Command,TO}, State) ->
log(heading(exec,Target), "Opening channel failed: ~p", [ChnError]),
{ChnError,State};
_ ->
- log(heading(exec,Target),
- "SSH Ref: ~p, Chn: ~p, Command: ~p, Timeout: ~p",
- [SSHRef,Chn1,Command,TO]),
+ try_log(heading(exec,Target),
+ "SSH Ref: ~p, Chn: ~p, Command: ~p, Timeout: ~p",
+ [SSHRef,Chn1,Command,TO]),
case ssh_connection:exec(SSHRef, Chn1, Command, TO) of
success ->
Result = do_recv_response(SSHRef, Chn1, [], close, TO),
@@ -1024,24 +1032,24 @@ handle_msg({exec,Chn,Command,TO}, State) ->
handle_msg({receive_response,Chn,End,TO}, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
- log(heading(receive_response,Target),
- "SSH Ref: ~p, Chn: ~p, Timeout: ~p", [SSHRef,Chn,TO]),
+ try_log(heading(receive_response,Target),
+ "SSH Ref: ~p, Chn: ~p, Timeout: ~p", [SSHRef,Chn,TO]),
Result = do_recv_response(SSHRef, Chn, [], End, TO),
{Result,State};
handle_msg({send,Chn,Type,Data,TO}, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
- log(heading(send,Target),
- "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n"
- "Data: ~p", [SSHRef,Chn,Type,TO,Data]),
+ try_log(heading(send,Target),
+ "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n"
+ "Data: ~p", [SSHRef,Chn,Type,TO,Data]),
Result = ssh_connection:send(SSHRef, Chn, Type, Data, TO),
{Result,State};
handle_msg({send_and_receive,Chn,Type,Data,End,TO}, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
- log(heading(send_and_receive,Target),
- "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n"
- "Data: ~p", [SSHRef,Chn,Type,TO,Data]),
+ try_log(heading(send_and_receive,Target),
+ "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n"
+ "Data: ~p", [SSHRef,Chn,Type,TO,Data]),
case ssh_connection:send(SSHRef, Chn, Type, Data, TO) of
ok ->
Result = do_recv_response(SSHRef, Chn, [], End, TO),
@@ -1052,137 +1060,162 @@ handle_msg({send_and_receive,Chn,Type,Data,End,TO}, State) ->
handle_msg({subsystem,Chn,Subsystem,TO}, State) ->
#state{ssh_ref=SSHRef, target=Target} = State,
- log(heading(subsystem,Target),
- "SSH Ref: ~p, Chn: ~p, Subsys: ~p, Timeout: ~p",
- [SSHRef,Chn,Subsystem,TO]),
+ try_log(heading(subsystem,Target),
+ "SSH Ref: ~p, Chn: ~p, Subsys: ~p, Timeout: ~p",
+ [SSHRef,Chn,Subsystem,TO]),
Result = ssh_connection:subsystem(SSHRef, Chn, Subsystem, TO),
{Result,State};
%% --- SFTP Commands ---
handle_msg({read_file,Srv,File}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:read_file(ref(Srv,SSHRef), File),S};
handle_msg({write_file,Srv,File,Iolist}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:write_file(ref(Srv,SSHRef), File, Iolist),S};
handle_msg({list_dir,Srv,Path}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:list_dir(ref(Srv,SSHRef), Path),S};
handle_msg({open,Srv,File,Mode}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:open(ref(Srv,SSHRef), File, Mode),S};
handle_msg({opendir,Srv,Path}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:opendir(ref(Srv,SSHRef), Path),S};
handle_msg({close,Srv,Handle}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:close(ref(Srv,SSHRef), Handle),S};
handle_msg({read,Srv,Handle,Len}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:read(ref(Srv,SSHRef), Handle, Len),S};
handle_msg({pread,Srv,Handle,Position,Length}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:pread(ref(Srv,SSHRef),Handle,Position,Length),S};
handle_msg({aread,Srv,Handle,Len}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:aread(ref(Srv,SSHRef), Handle, Len),S};
handle_msg({apread,Srv,Handle,Position,Length}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:apread(ref(Srv,SSHRef), Handle, Position, Length),S};
handle_msg({write,Srv,Handle,Data}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:write(ref(Srv,SSHRef), Handle, Data),S};
handle_msg({pwrite,Srv,Handle,Position,Data}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:pwrite(ref(Srv,SSHRef), Handle, Position, Data),S};
handle_msg({awrite,Srv,Handle,Data}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:awrite(ref(Srv,SSHRef), Handle, Data),S};
handle_msg({apwrite,Srv,Handle,Position,Data}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:apwrite(ref(Srv,SSHRef), Handle, Position, Data),S};
handle_msg({position,Srv,Handle,Location}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:position(ref(Srv,SSHRef), Handle, Location),S};
handle_msg({read_file_info,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:read_file_info(ref(Srv,SSHRef), Name),S};
handle_msg({get_file_info,Srv,Handle}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:get_file_info(ref(Srv,SSHRef), Handle),S};
handle_msg({read_link_info,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:read_link_info(ref(Srv,SSHRef), Name),S};
handle_msg({write_file_info,Srv,Name,Info}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:write_file_info(ref(Srv,SSHRef), Name, Info),S};
handle_msg({read_link,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:read_link(ref(Srv,SSHRef), Name),S};
handle_msg({make_symlink,Srv,Name,Target}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:make_symlink(ref(Srv,SSHRef), Name, Target),S};
handle_msg({rename,Srv,OldName,NewName}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:rename(ref(Srv,SSHRef), OldName, NewName),S};
handle_msg({delete,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:delete(ref(Srv,SSHRef), Name),S};
handle_msg({make_dir,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:make_dir(ref(Srv,SSHRef), Name),S};
handle_msg({del_dir,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) ->
- log(heading(sftp,S#state.target),
- "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
+ try_log(heading(sftp,S#state.target),
+ "SSH Ref: ~p, Server: ~p~nCmd: ~p",
+ [SSHRef,ref(Srv,SSHRef),mod(Cmd)]),
{ssh_sftp:del_dir(ref(Srv,SSHRef), Name),S}.
%% @hidden
@@ -1197,12 +1230,12 @@ close(SSHRef) ->
terminate(SSHRef, State) ->
case State#state.conn_type of
ssh ->
- log(heading(disconnect_ssh,State#state.target),
- "SSH Ref: ~p",[SSHRef]),
+ try_log(heading(disconnect_ssh,State#state.target),
+ "SSH Ref: ~p",[SSHRef], 5000),
ssh:close(SSHRef);
sftp ->
- log(heading(disconnect_sftp,State#state.target),
- "SFTP Ref: ~p",[SSHRef]),
+ try_log(heading(disconnect_sftp,State#state.target),
+ "SFTP Ref: ~p",[SSHRef], 5000),
ssh_sftp:stop_channel(SSHRef)
end.
@@ -1217,7 +1250,7 @@ do_recv_response(SSH, Chn, Data, End, Timeout) ->
{ssh_cm, SSH, {open,Chn,RemoteChn,{session}}} ->
debug("RECVD open"),
{ok,{open,Chn,RemoteChn,{session}}};
-
+
{ssh_cm, SSH, {closed,Chn}} ->
ssh_connection:close(SSH, Chn),
debug("CLSD~n~p ~p", [SSH,Chn]),
@@ -1245,38 +1278,38 @@ do_recv_response(SSH, Chn, Data, End, Timeout) ->
{ssh_cm, SSH, {exit_signal,Chn,Signal,Err,_Lang}} ->
debug("RECVD exit_signal~n~p ~p~n~p ~p", [SSH,Chn,Signal,Err]),
do_recv_response(SSH, Chn, Data, End, Timeout);
-%% {ok,{exit_signal,Chn,Signal,Err,_Lang}};
+ %% {ok,{exit_signal,Chn,Signal,Err,_Lang}};
{ssh_cm, SSH, {exit_status,Chn,Status}} ->
debug("RECVD exit_status~n~p ~p~n~p", [SSH,Chn,Status]),
do_recv_response(SSH, Chn, Data, End, Timeout);
-%% {ok,{exit_status,Chn,_Status}};
+ %% {ok,{exit_status,Chn,_Status}};
-%% --- INTERACTIVE MESSAGES - NOT HANDLED ---
-%%
-%% {ssh_cm, SSH, {subsystem,Chn,WantReply,Name}} ->
-%% debug("RECVD SUBS WNTRPLY~n~p ~p~n~p~n~p",
-%% [SSH,Chn,WantReply]),
-%% ssh_connection:reply_request(SSH, WantReply, success, Chn),
-%% do_recv_response(SSH, Chn, Data, End, Timeout);
-
-%% {ssh_cm, SSH, {shell,WantReply}} ->
-%% debug("RECVD SHELL WNTRPLY~n~p ~p~n~p~n~p",
-%% [SSH,Chn,WantReply]),
-%% ssh_connection:reply_request(SSH, WantReply, success, Chn),
-%% do_recv_response(SSH,Chn,Data,End,Timeout);
-
-%% {ssh_cm, SSH, {pty,Chn,WantReply,Pty}} ->
-%% debug("RECVD PTY WNTRPLY~n~p ~p~n~p~n~p",
-%% [SSH,Chn,WantReply,Pty]),
-%% ssh_connection:reply_request(SSH, WantReply, success, Chn),
-%% do_recv_response(SSH, Chn, Data, End, Timeout);
-
-%% {ssh_cm, SSH, WCh={window_change,_Chn,_Width,_Height,_PixWidth,_PixHeight}} ->
-%% debug("RECVD WINCH"),
-%% {ok,WCh};
-
+ %% --- INTERACTIVE MESSAGES - NOT HANDLED ---
+ %%
+ %% {ssh_cm, SSH, {subsystem,Chn,WantReply,Name}} ->
+ %% debug("RECVD SUBS WNTRPLY~n~p ~p~n~p~n~p",
+ %% [SSH,Chn,WantReply]),
+ %% ssh_connection:reply_request(SSH, WantReply, success, Chn),
+ %% do_recv_response(SSH, Chn, Data, End, Timeout);
+
+ %% {ssh_cm, SSH, {shell,WantReply}} ->
+ %% debug("RECVD SHELL WNTRPLY~n~p ~p~n~p~n~p",
+ %% [SSH,Chn,WantReply]),
+ %% ssh_connection:reply_request(SSH, WantReply, success, Chn),
+ %% do_recv_response(SSH,Chn,Data,End,Timeout);
+
+ %% {ssh_cm, SSH, {pty,Chn,WantReply,Pty}} ->
+ %% debug("RECVD PTY WNTRPLY~n~p ~p~n~p~n~p",
+ %% [SSH,Chn,WantReply,Pty]),
+ %% ssh_connection:reply_request(SSH, WantReply, success, Chn),
+ %% do_recv_response(SSH, Chn, Data, End, Timeout);
+
+ %% {ssh_cm, SSH, WCh={window_change,_Chn,_Width,_Height,_PixWidth,_PixHeight}} ->
+ %% debug("RECVD WINCH"),
+ %% {ok,WCh};
+
Other ->
debug("UNEXPECTED MESSAGE~n~p ~p~n~p", [SSH,Chn,Other]),
do_recv_response(SSH, Chn, Data, End, Timeout)
@@ -1307,9 +1340,12 @@ get_handle(SSH) ->
%%%-----------------------------------------------------------------
%%%
call(SSH, Msg) ->
+ call(SSH, Msg, infinity).
+
+call(SSH, Msg, Timeout) ->
case get_handle(SSH) of
{ok,Pid} ->
- ct_gen_conn:call(Pid, Msg);
+ ct_gen_conn:call(Pid, Msg, Timeout);
Error ->
Error
end.
@@ -1318,13 +1354,13 @@ call(SSH, Msg) ->
%%%
ref(sftp, SSHRef) -> SSHRef;
ref(Server, _) -> Server.
-
+
%%%-----------------------------------------------------------------
%%%
mod(Cmd) ->
[Op,_Server|Args] = tuple_to_list(Cmd),
list_to_tuple([Op|Args]).
-
+
%%%-----------------------------------------------------------------
%%%
heading(Function, Ref) ->
@@ -1335,6 +1371,20 @@ heading(Function, Ref) ->
log(Heading, Str, Args) ->
ct_gen_conn:log(Heading, Str, Args).
+%%%-----------------------------------------------------------------
+%%%
+try_log(Heading, Str, Args) ->
+ try_log(Heading, Str, Args, infinity).
+
+try_log(Heading, Str, Args, Timeout) ->
+ case ct_util:is_silenced(ssh, Timeout) of
+ true ->
+ ok;
+ false ->
+ ct_gen_conn:log(Heading, Str, Args);
+ _Error ->
+ ok
+ end.
%%%-----------------------------------------------------------------
%%%
@@ -1342,5 +1392,5 @@ debug(Str) ->
debug(Str, []).
debug(_Str, _Args) ->
-%% io:format("~n--- ct_ssh debug ---~n" ++ _Str ++ "~n", _Args),
+ %% io:format("~n--- ct_ssh debug ---~n" ++ _Str ++ "~n", _Args),
ok.
diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl
index f4a551e3ff..b13c050e32 100644
--- a/lib/common_test/src/ct_telnet.erl
+++ b/lib/common_test/src/ct_telnet.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. 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
@@ -155,6 +155,8 @@ open(KeyOrName,ConnType,TargetMod) ->
%%% <p><code>TargetMod</code> is a module which exports the functions
%%% <code>connect(Ip,Port,KeepAlive,Extra)</code> and <code>get_prompt_regexp()</code>
%%% for the given <code>TargetType</code> (e.g. <code>unix_telnet</code>).</p>
+%%%
+%%% @see ct:require/2
open(KeyOrName,ConnType,TargetMod,Extra) ->
case ct:get_config({KeyOrName,ConnType}) of
undefined ->
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index 4c05f57520..a8b67d0329 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -29,6 +29,8 @@
-include("ct_util.hrl").
+-define(testspec_fields, record_info(fields, testspec)).
+
%%%------------------------------------------------------------------
%%% NOTE:
%%% Multiple testspecs may be used as input with the result that
@@ -46,7 +48,8 @@
%%% Version 1 - extract and return all tests and skips for Node
%%% (incl all_nodes)
%%%-------------------------------------------------------------------
-prepare_tests(TestSpec,Node) when is_record(TestSpec,testspec), is_atom(Node) ->
+prepare_tests(TestSpec,Node) when is_record(TestSpec,testspec),
+ is_atom(Node) ->
case lists:keysearch(Node,1,prepare_tests(TestSpec)) of
{value,{Node,Run,Skip}} ->
{Run,Skip};
@@ -249,22 +252,23 @@ collect_tests_from_file1([Spec|Specs],TestSpec,Relaxed) ->
SpecDir = filename:dirname(filename:absname(Spec)),
case file:consult(Spec) of
{ok,Terms} ->
- TestSpec1 = collect_tests(Terms,
- TestSpec#testspec{spec_dir=SpecDir},
- Relaxed),
- collect_tests_from_file1(Specs,TestSpec1,Relaxed);
+ case collect_tests(Terms,
+ TestSpec#testspec{spec_dir=SpecDir},
+ Relaxed) of
+ TS = #testspec{tests=Tests, logdir=LogDirs} when Specs == [] ->
+ LogDirs1 = lists:delete(".",LogDirs) ++ ["."],
+ TS#testspec{tests=lists:flatten(Tests), logdir=LogDirs1};
+ TS = #testspec{alias = As, nodes = Ns} ->
+ TS1 = TS#testspec{alias = lists:reverse(As),
+ nodes = lists:reverse(Ns)},
+ collect_tests_from_file1(Specs,TS1,Relaxed)
+ end;
{error,Reason} ->
ReasonStr =
lists:flatten(io_lib:format("~s",
[file:format_error(Reason)])),
throw({error,{Spec,ReasonStr}})
- end;
-collect_tests_from_file1([],TS=#testspec{config=Cfgs,event_handler=EvHs,
- include=Incl,tests=Tests},_) ->
- TS#testspec{config=lists:reverse(Cfgs),
- event_handler=lists:reverse(EvHs),
- include=lists:reverse(Incl),
- tests=lists:flatten(Tests)}.
+ end.
collect_tests_from_list(Terms,Relaxed) ->
collect_tests_from_list(Terms,[node()],Relaxed).
@@ -278,30 +282,163 @@ collect_tests_from_list(Terms,Nodes,Relaxed) when is_list(Nodes) ->
E = {error,_} ->
E;
TS ->
- #testspec{config=Cfgs,event_handler=EvHs,include=Incl,tests=Tests} = TS,
- TS#testspec{config=lists:reverse(Cfgs),
- event_handler=lists:reverse(EvHs),
- include=lists:reverse(Incl),
- tests=lists:flatten(Tests)}
+ #testspec{tests=Tests, logdir=LogDirs} = TS,
+ LogDirs1 = lists:delete(".",LogDirs) ++ ["."],
+ TS#testspec{tests=lists:flatten(Tests), logdir=LogDirs1}
end.
collect_tests(Terms,TestSpec,Relaxed) ->
put(relaxed,Relaxed),
- TestSpec1 = get_global(Terms,TestSpec),
- TestSpec2 = get_all_nodes(Terms,TestSpec1),
- {Terms2, TestSpec3} = filter_init_terms(Terms, [], TestSpec2),
+ Terms1 = replace_names(Terms),
+ TestSpec1 = get_global(Terms1,TestSpec),
+ TestSpec2 = get_all_nodes(Terms1,TestSpec1),
+ {Terms2, TestSpec3} = filter_init_terms(Terms1, [], TestSpec2),
add_tests(Terms2,TestSpec3).
-get_global([{merge_tests, Bool} | Ts], Spec) ->
- get_global(Ts,Spec#testspec{ merge_tests = Bool });
+%% replace names (atoms) in the testspec matching those in 'define' terms by
+%% searching recursively through tuples and lists
+replace_names(Terms) ->
+ Defs =
+ lists:flatmap(fun(Def={define,Name,_Replacement}) ->
+ %% check that name follows convention
+ if not is_atom(Name) ->
+ throw({illegal_name_in_testspec,Name});
+ true ->
+ [First|_] = atom_to_list(Name),
+ if ((First == $?) or (First == $$)
+ or (First == $_)
+ or ((First >= $A)
+ and (First =< $Z))) ->
+ [Def];
+ true ->
+ throw({illegal_name_in_testspec,
+ Name})
+ end
+ end;
+ (_) -> []
+ end, Terms),
+ DefProps = replace_names_in_defs(Defs,[]),
+ replace_names(Terms,[],DefProps).
+
+replace_names_in_defs([Def|Left],ModDefs) ->
+ [{define,Name,Replacement}] = replace_names([Def],[],ModDefs),
+ replace_names_in_defs(Left,[{Name,Replacement}|ModDefs]);
+replace_names_in_defs([],ModDefs) ->
+ ModDefs.
+
+replace_names([Term|Ts],Modified,Defs) when is_tuple(Term) ->
+ [TypeTag|Data] = tuple_to_list(Term),
+ Term1 = list_to_tuple([TypeTag|replace_names_in_elems(Data,[],Defs)]),
+ replace_names(Ts,[Term1|Modified],Defs);
+replace_names([Term|Ts],Modified,Defs) when is_atom(Term) ->
+ case proplists:get_value(Term,Defs) of
+ undefined ->
+ replace_names(Ts,[Term|Modified],Defs);
+ Replacement ->
+ replace_names(Ts,[Replacement|Modified],Defs)
+ end;
+replace_names([Term=[Ch|_]|Ts],Modified,Defs) when is_integer(Ch) ->
+ %% Term *could* be a string, attempt to search through it
+ Term1 = replace_names_in_string(Term,Defs),
+ replace_names(Ts,[Term1|Modified],Defs);
+replace_names([Term|Ts],Modified,Defs) ->
+ replace_names(Ts,[Term|Modified],Defs);
+replace_names([],Modified,_Defs) ->
+ lists:reverse(Modified).
+
+replace_names_in_elems([Elem|Es],Modified,Defs) when is_tuple(Elem) ->
+ Elem1 = list_to_tuple(replace_names_in_elems(tuple_to_list(Elem),[],Defs)),
+ replace_names_in_elems(Es,[Elem1|Modified],Defs);
+replace_names_in_elems([Elem|Es],Modified,Defs) when is_atom(Elem) ->
+ case proplists:get_value(Elem,Defs) of
+ undefined ->
+ %% if Term is a node name, check it for replacements as well
+ Elem1 = replace_names_in_node(Elem,Defs),
+ replace_names_in_elems(Es,[Elem1|Modified],Defs);
+ Replacement ->
+ replace_names_in_elems(Es,[Replacement|Modified],Defs)
+ end;
+replace_names_in_elems([Elem=[Ch|_]|Es],Modified,Defs) when is_integer(Ch) ->
+ %% Term *could* be a string, attempt to search through it
+ case replace_names_in_string(Elem,Defs) of
+ Elem ->
+ List = replace_names_in_elems(Elem,[],Defs),
+ replace_names_in_elems(Es,[List|Modified],Defs);
+ Elem1 ->
+ replace_names_in_elems(Es,[Elem1|Modified],Defs)
+ end;
+replace_names_in_elems([Elem|Es],Modified,Defs) when is_list(Elem) ->
+ List = replace_names_in_elems(Elem,[],Defs),
+ replace_names_in_elems(Es,[List|Modified],Defs);
+replace_names_in_elems([Elem|Es],Modified,Defs) ->
+ replace_names_in_elems(Es,[Elem|Modified],Defs);
+replace_names_in_elems([],Modified,_Defs) ->
+ lists:reverse(Modified).
+
+replace_names_in_string(Term,Defs=[{Name,Replacement=[Ch|_]}|Ds])
+ when is_integer(Ch) ->
+ try re:replace(Term,[$'|atom_to_list(Name)]++"'",
+ Replacement,[{return,list}]) of
+ Term -> % no match, proceed
+ replace_names_in_string(Term,Ds);
+ Term1 ->
+ replace_names_in_string(Term1,Defs)
+ catch
+ _:_ -> Term % Term is not a string
+ end;
+replace_names_in_string(Term,[_|Ds]) ->
+ replace_names_in_string(Term,Ds);
+replace_names_in_string(Term,[]) ->
+ Term.
+
+replace_names_in_node(Node,Defs) ->
+ String = atom_to_list(Node),
+ case lists:member($@,String) of
+ true ->
+ list_to_atom(replace_names_in_node1(String,Defs));
+ false ->
+ Node
+ end.
+
+replace_names_in_node1(NodeStr,Defs=[{Name,Replacement}|Ds]) ->
+ ReplStr = case Replacement of
+ [Ch|_] when is_integer(Ch) -> Replacement;
+ _ when is_atom(Replacement) -> atom_to_list(Replacement);
+ _ -> false
+ end,
+ if ReplStr == false ->
+ replace_names_in_node1(NodeStr,Ds);
+ true ->
+ case re:replace(NodeStr,atom_to_list(Name),
+ ReplStr,[{return,list}]) of
+ NodeStr -> % no match, proceed
+ replace_names_in_node1(NodeStr,Ds);
+ NodeStr1 ->
+ replace_names_in_node1(NodeStr1,Defs)
+ end
+ end;
+replace_names_in_node1(NodeStr,[]) ->
+ NodeStr.
+
+
+%% global terms that will be used for analysing all other terms in the spec
+get_global([{merge_tests,Bool} | Ts], Spec) ->
+ get_global(Ts,Spec#testspec{merge_tests=Bool});
+
+%% the 'define' term replaces the 'alias' and 'node' terms, but we need to keep
+%% the latter two for backwards compatibility...
get_global([{alias,Ref,Dir}|Ts],Spec=#testspec{alias=Refs}) ->
get_global(Ts,Spec#testspec{alias=[{Ref,get_absdir(Dir,Spec)}|Refs]});
get_global([{node,Ref,Node}|Ts],Spec=#testspec{nodes=Refs}) ->
- get_global(Ts,Spec#testspec{nodes=[{Ref,Node}|lists:keydelete(Node,2,Refs)]});
-get_global([_|Ts],Spec) -> get_global(Ts,Spec);
-get_global([],Spec) -> Spec.
+ get_global(Ts,Spec#testspec{nodes=[{Ref,Node} |
+ lists:keydelete(Node,2,Refs)]});
-get_absfile(Callback, FullName,#testspec{spec_dir=SpecDir}) ->
+get_global([_|Ts],Spec) ->
+ get_global(Ts,Spec);
+get_global([],Spec=#testspec{nodes=Ns, alias=As}) ->
+ Spec#testspec{nodes=lists:reverse(Ns), alias=lists:reverse(As)}.
+
+get_absfile(Callback,FullName,#testspec{spec_dir=SpecDir}) ->
% we need to temporary switch to new cwd here, because
% otherwise config files cannot be found
{ok, OldWd} = file:get_cwd(),
@@ -329,29 +466,45 @@ get_absfile(FullName,#testspec{spec_dir=SpecDir}) ->
get_absdir(Dir,#testspec{spec_dir=SpecDir}) ->
get_absname(Dir,SpecDir).
-get_absname(TestDir,SpecDir) ->
- AbsName = filename:absname(TestDir,SpecDir),
- TestDirName = filename:basename(AbsName),
- Path = filename:dirname(AbsName),
- TopDir = filename:basename(Path),
- Path1 =
- case TopDir of
- "." ->
- [_|Rev] = lists:reverse(filename:split(Path)),
- filename:join(lists:reverse(Rev));
- ".." ->
- [_,_|Rev] = lists:reverse(filename:split(Path)),
- filename:join(lists:reverse(Rev));
- _ ->
- Path
- end,
- filename:join(Path1,TestDirName).
+get_absname(Dir,SpecDir) ->
+ AbsName = filename:absname(Dir,SpecDir),
+ shorten_path(AbsName,SpecDir).
+
+shorten_path(Path,SpecDir) ->
+ case shorten_split_path(filename:split(Path),[]) of
+ [] ->
+ [Root|_] = filename:split(SpecDir),
+ Root;
+ Short ->
+ filename:join(Short)
+ end.
+
+shorten_split_path([".."|Path],SoFar) ->
+ shorten_split_path(Path,tl(SoFar));
+shorten_split_path(["."|Path],SoFar) ->
+ shorten_split_path(Path,SoFar);
+shorten_split_path([Dir|Path],SoFar) ->
+ shorten_split_path(Path,[Dir|SoFar]);
+shorten_split_path([],SoFar) ->
+ lists:reverse(SoFar).
%% go through all tests and register all nodes found
get_all_nodes([{suites,Nodes,_,_}|Ts],Spec) when is_list(Nodes) ->
get_all_nodes(Ts,save_nodes(Nodes,Spec));
get_all_nodes([{suites,Node,_,_}|Ts],Spec) ->
get_all_nodes(Ts,save_nodes([Node],Spec));
+get_all_nodes([{groups,[Char|_],_,_,_}|Ts],Spec) when is_integer(Char) ->
+ get_all_nodes(Ts,Spec);
+get_all_nodes([{groups,Nodes,_,_,_}|Ts],Spec) when is_list(Nodes) ->
+ get_all_nodes(Ts,save_nodes(Nodes,Spec));
+get_all_nodes([{groups,Nodes,_,_,_,_}|Ts],Spec) when is_list(Nodes) ->
+ get_all_nodes(Ts,save_nodes(Nodes,Spec));
+get_all_nodes([{groups,_,_,_,{cases,_}}|Ts],Spec) ->
+ get_all_nodes(Ts,Spec);
+get_all_nodes([{groups,Node,_,_,_}|Ts],Spec) ->
+ get_all_nodes(Ts,save_nodes([Node],Spec));
+get_all_nodes([{groups,Node,_,_,_,_}|Ts],Spec) ->
+ get_all_nodes(Ts,save_nodes([Node],Spec));
get_all_nodes([{cases,Nodes,_,_,_}|Ts],Spec) when is_list(Nodes) ->
get_all_nodes(Ts,save_nodes(Nodes,Spec));
get_all_nodes([{cases,Node,_,_,_}|Ts],Spec) ->
@@ -360,74 +513,93 @@ get_all_nodes([{skip_suites,Nodes,_,_,_}|Ts],Spec) when is_list(Nodes) ->
get_all_nodes(Ts,save_nodes(Nodes,Spec));
get_all_nodes([{skip_suites,Node,_,_,_}|Ts],Spec) ->
get_all_nodes(Ts,save_nodes([Node],Spec));
+get_all_nodes([{skip_groups,[Char|_],_,_,_,_}|Ts],Spec) when is_integer(Char) ->
+ get_all_nodes(Ts,Spec);
+get_all_nodes([{skip_groups,Nodes,_,_,_,_}|Ts],Spec) when is_list(Nodes) ->
+ get_all_nodes(Ts,save_nodes(Nodes,Spec));
+get_all_nodes([{skip_groups,Node,_,_,_,_}|Ts],Spec) ->
+ get_all_nodes(Ts,save_nodes([Node],Spec));
+get_all_nodes([{skip_groups,Nodes,_,_,_,_,_}|Ts],Spec) when is_list(Nodes) ->
+ get_all_nodes(Ts,save_nodes(Nodes,Spec));
+get_all_nodes([{skip_groups,Node,_,_,_,_,_}|Ts],Spec) ->
+ get_all_nodes(Ts,save_nodes([Node],Spec));
get_all_nodes([{skip_cases,Nodes,_,_,_,_}|Ts],Spec) when is_list(Nodes) ->
get_all_nodes(Ts,save_nodes(Nodes,Spec));
get_all_nodes([{skip_cases,Node,_,_,_,_}|Ts],Spec) ->
get_all_nodes(Ts,save_nodes([Node],Spec));
-get_all_nodes([_|Ts],Spec) ->
+get_all_nodes([_Other|Ts],Spec) ->
get_all_nodes(Ts,Spec);
get_all_nodes([],Spec) ->
Spec.
-filter_init_terms([{init, InitOptions}|Ts], NewTerms, Spec)->
- filter_init_terms([{init, list_nodes(Spec), InitOptions}|Ts], NewTerms, Spec);
-filter_init_terms([{init, NodeRef, InitOptions}|Ts], NewTerms, Spec)
- when is_atom(NodeRef)->
- filter_init_terms([{init, [NodeRef], InitOptions}|Ts], NewTerms, Spec);
-filter_init_terms([{init, NodeRefs, InitOption}|Ts], NewTerms, Spec) when is_tuple(InitOption) ->
- filter_init_terms([{init, NodeRefs, [InitOption]}|Ts], NewTerms, Spec);
-filter_init_terms([{init, [NodeRef|NodeRefs], InitOptions}|Ts], NewTerms, Spec=#testspec{init=InitData})->
- NodeStartOptions = case lists:keyfind(node_start, 1, InitOptions) of
- {node_start, NSOptions}->
- case lists:keyfind(callback_module, 1, NSOptions) of
- {callback_module, _Callback}->
- NSOptions;
- false->
- [{callback_module, ct_slave}|NSOptions]
- end;
- false->
- []
- end,
- EvalTerms = case lists:keyfind(eval, 1, InitOptions) of
- {eval, MFA} when is_tuple(MFA)->
- [MFA];
- {eval, MFAs} when is_list(MFAs)->
- MFAs;
- false->
- []
- end,
+filter_init_terms([{init,InitOptions}|Ts],NewTerms,Spec) ->
+ filter_init_terms([{init,list_nodes(Spec),InitOptions}|Ts],
+ NewTerms,Spec);
+filter_init_terms([{init,all_nodes,InitOptions}|Ts],NewTerms,Spec) ->
+ filter_init_terms([{init,list_nodes(Spec),InitOptions}|Ts],
+ NewTerms,Spec);
+filter_init_terms([{init,NodeRef,InitOptions}|Ts],
+ NewTerms,Spec) when is_atom(NodeRef) ->
+ filter_init_terms([{init,[NodeRef],InitOptions}|Ts],NewTerms,Spec);
+filter_init_terms([{init,NodeRefs,InitOption}|Ts],
+ NewTerms,Spec) when is_tuple(InitOption) ->
+ filter_init_terms([{init,NodeRefs,[InitOption]}|Ts],NewTerms,Spec);
+filter_init_terms([{init,[NodeRef|NodeRefs],InitOptions}|Ts],
+ NewTerms,Spec=#testspec{init=InitData}) ->
+ NodeStartOptions =
+ case lists:keyfind(node_start,1,InitOptions) of
+ {node_start,NSOptions}->
+ case lists:keyfind(callback_module,1,NSOptions) of
+ {callback_module,_Callback}->
+ NSOptions;
+ false->
+ [{callback_module,ct_slave}|NSOptions]
+ end;
+ false->
+ []
+ end,
+ EvalTerms = case lists:keyfind(eval,1,InitOptions) of
+ {eval,MFA} when is_tuple(MFA) ->
+ [MFA];
+ {eval,MFAs} when is_list(MFAs) ->
+ MFAs;
+ false->
+ []
+ end,
Node = ref2node(NodeRef,Spec#testspec.nodes),
- InitData2 = add_option({node_start, NodeStartOptions}, Node, InitData, true),
- InitData3 = add_option({eval, EvalTerms}, Node, InitData2, false),
- filter_init_terms([{init, NodeRefs, InitOptions}|Ts], NewTerms, Spec#testspec{init=InitData3});
-filter_init_terms([{init, [], _}|Ts], NewTerms, Spec)->
- filter_init_terms(Ts, NewTerms, Spec);
-filter_init_terms([Term|Ts], NewTerms, Spec)->
- filter_init_terms(Ts, [Term|NewTerms], Spec);
-filter_init_terms([], NewTerms, Spec)->
- {lists:reverse(NewTerms), Spec}.
-
-add_option({Key, Value}, Node, List, WarnIfExists) when is_list(Value)->
- OldOptions = case lists:keyfind(Node, 1, List) of
- {Node, Options}->
+ InitData2 = add_option({node_start,NodeStartOptions},Node,InitData,true),
+ InitData3 = add_option({eval,EvalTerms},Node,InitData2,false),
+ filter_init_terms([{init,NodeRefs,InitOptions}|Ts],
+ NewTerms,Spec#testspec{init=InitData3});
+filter_init_terms([{init,[],_}|Ts],NewTerms,Spec) ->
+ filter_init_terms(Ts,NewTerms,Spec);
+filter_init_terms([Term|Ts],NewTerms,Spec) ->
+ filter_init_terms(Ts,[Term|NewTerms],Spec);
+filter_init_terms([],NewTerms,Spec) ->
+ {lists:reverse(NewTerms),Spec}.
+
+add_option({Key,Value},Node,List,WarnIfExists) when is_list(Value) ->
+ OldOptions = case lists:keyfind(Node,1,List) of
+ {Node,Options}->
Options;
false->
[]
end,
- NewOption = case lists:keyfind(Key, 1, OldOptions) of
- {Key, OldOption} when WarnIfExists, OldOption/=[]->
- io:format("There is an option ~w=~w already defined for node ~p, skipping new ~w~n",
- [Key, OldOption, Node, Value]),
+ NewOption = case lists:keyfind(Key,1,OldOptions) of
+ {Key,OldOption} when WarnIfExists,OldOption/=[]->
+ io:format("There is an option ~w=~w already "
+ "defined for node ~p, skipping new ~w~n",
+ [Key,OldOption,Node,Value]),
OldOption;
- {Key, OldOption}->
+ {Key,OldOption}->
OldOption ++ Value;
false->
Value
end,
- lists:keystore(Node, 1, List,
- {Node, lists:keystore(Key, 1, OldOptions, {Key, NewOption})});
-add_option({Key, Value}, Node, List, WarnIfExists)->
- add_option({Key, [Value]}, Node, List, WarnIfExists).
+ lists:keystore(Node,1,List,
+ {Node,lists:keystore(Key,1,OldOptions,{Key,NewOption})});
+add_option({Key,Value},Node,List,WarnIfExists) ->
+ add_option({Key,[Value]},Node,List,WarnIfExists).
save_nodes(Nodes,Spec=#testspec{nodes=NodeRefs}) ->
NodeRefs1 =
@@ -446,267 +618,18 @@ save_nodes(Nodes,Spec=#testspec{nodes=NodeRefs}) ->
end
end
end,NodeRefs,Nodes),
- Spec#testspec{nodes=NodeRefs1}.
+ Spec#testspec{nodes=NodeRefs1}.
list_nodes(#testspec{nodes=NodeRefs}) ->
lists:map(fun({_Ref,Node}) -> Node end, NodeRefs).
-
-%% ---------------------------------------------------------
-%% / \
-%% | When adding tests, remember to update valid_terms/0 also! |
-%% \ /
-%% ---------------------------------------------------------
-
-
-%% Associate a "global" logdir with all nodes
-%% except those with specific logdir, e.g:
-%% ["/tmp/logdir",{ct1@finwe,"/tmp/logdir2"}]
-%% means all nodes should write to /tmp/logdir
-%% except ct1@finwe that should use /tmp/logdir2.
-
-%% --- logdir ---
-add_tests([{logdir,all_nodes,Dir}|Ts],Spec) ->
- Dirs = Spec#testspec.logdir,
- Tests = [{logdir,N,get_absdir(Dir,Spec)} ||
- N <- list_nodes(Spec),
- lists:keymember(ref2node(N,Spec#testspec.nodes),
- 1,Dirs) == false],
- add_tests(Tests++Ts,Spec);
-add_tests([{logdir,Nodes,Dir}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,logdir,[Dir],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{logdir,Node,Dir}|Ts],Spec) ->
- Dirs = Spec#testspec.logdir,
- Dirs1 = [{ref2node(Node,Spec#testspec.nodes),get_absdir(Dir,Spec)} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,Dirs)],
- add_tests(Ts,Spec#testspec{logdir=Dirs1});
-add_tests([{logdir,Dir}|Ts],Spec) ->
- add_tests([{logdir,all_nodes,Dir}|Ts],Spec);
-
-%% --- logopts ---
-add_tests([{logopts,all_nodes,Opts}|Ts],Spec) ->
- LogOpts = Spec#testspec.logopts,
- Tests = [{logopts,N,Opts} ||
- N <- list_nodes(Spec),
- lists:keymember(ref2node(N,Spec#testspec.nodes),1,
- LogOpts) == false],
- add_tests(Tests++Ts,Spec);
-add_tests([{logopts,Nodes,Opts}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,logopts,[Opts],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{logopts,Node,Opts}|Ts],Spec) ->
- LogOpts = Spec#testspec.logopts,
- LogOpts1 = [{ref2node(Node,Spec#testspec.nodes),Opts} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),
- 1,LogOpts)],
- add_tests(Ts,Spec#testspec{logopts=LogOpts1});
-add_tests([{logopts,Opts}|Ts],Spec) ->
- add_tests([{logopts,all_nodes,Opts}|Ts],Spec);
-
-%% --- label ---
-add_tests([{label,all_nodes,Lbl}|Ts],Spec) ->
- Labels = Spec#testspec.label,
- Tests = [{label,N,Lbl} || N <- list_nodes(Spec),
- lists:keymember(ref2node(N,Spec#testspec.nodes),
- 1,Labels) == false],
- add_tests(Tests++Ts,Spec);
-add_tests([{label,Nodes,Lbl}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,label,[Lbl],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{label,Node,Lbl}|Ts],Spec) ->
- Labels = Spec#testspec.label,
- Labels1 = [{ref2node(Node,Spec#testspec.nodes),Lbl} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,Labels)],
- add_tests(Ts,Spec#testspec{label=Labels1});
-add_tests([{label,Lbl}|Ts],Spec) ->
- add_tests([{label,all_nodes,Lbl}|Ts],Spec);
-
-%% --- cover ---
-add_tests([{cover,all_nodes,File}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {cover,N,File} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{cover,Nodes,File}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,cover,[File],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{cover,Node,File}|Ts],Spec) ->
- CoverFs = Spec#testspec.cover,
- CoverFs1 = [{ref2node(Node,Spec#testspec.nodes),get_absfile(File,Spec)} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,CoverFs)],
- add_tests(Ts,Spec#testspec{cover=CoverFs1});
-add_tests([{cover,File}|Ts],Spec) ->
- add_tests([{cover,all_nodes,File}|Ts],Spec);
-
-%% --- multiply_timetraps ---
-add_tests([{multiply_timetraps,all_nodes,MT}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {multiply_timetraps,N,MT} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{multiply_timetraps,Nodes,MT}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,multiply_timetraps,[MT],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{multiply_timetraps,Node,MT}|Ts],Spec) ->
- MTs = Spec#testspec.multiply_timetraps,
- MTs1 = [{ref2node(Node,Spec#testspec.nodes),MT} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,MTs)],
- add_tests(Ts,Spec#testspec{multiply_timetraps=MTs1});
-add_tests([{multiply_timetraps,MT}|Ts],Spec) ->
- add_tests([{multiply_timetraps,all_nodes,MT}|Ts],Spec);
-
-%% --- scale_timetraps ---
-add_tests([{scale_timetraps,all_nodes,ST}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {scale_timetraps,N,ST} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{scale_timetraps,Nodes,ST}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,scale_timetraps,[ST],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{scale_timetraps,Node,ST}|Ts],Spec) ->
- STs = Spec#testspec.scale_timetraps,
- STs1 = [{ref2node(Node,Spec#testspec.nodes),ST} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,STs)],
- add_tests(Ts,Spec#testspec{scale_timetraps=STs1});
-add_tests([{scale_timetraps,ST}|Ts],Spec) ->
- add_tests([{scale_timetraps,all_nodes,ST}|Ts],Spec);
-
-%% --- create_priv_dir ---
-add_tests([{create_priv_dir,all_nodes,PD}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {create_priv_dir,N,PD} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{create_priv_dir,Nodes,PD}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,create_priv_dir,[PD],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{create_priv_dir,Node,PD}|Ts],Spec) ->
- PDs = Spec#testspec.create_priv_dir,
- PDs1 = [{ref2node(Node,Spec#testspec.nodes),PD} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,PDs)],
- add_tests(Ts,Spec#testspec{create_priv_dir=PDs1});
-add_tests([{create_priv_dir,PD}|Ts],Spec) ->
- add_tests([{create_priv_dir,all_nodes,PD}|Ts],Spec);
-
-%% --- config ---
-add_tests([{config,all_nodes,Files}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {config,N,Files} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{config,Nodes,Files}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,config,[Files],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{config,Node,[F|Fs]}|Ts],Spec) when is_list(F) ->
- Cfgs = Spec#testspec.config,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- add_tests([{config,Node,Fs}|Ts],
- Spec#testspec{config=[{Node1,get_absfile(F,Spec)}|Cfgs]});
-add_tests([{config,_Node,[]}|Ts],Spec) ->
- add_tests(Ts,Spec);
-add_tests([{config,Node,F}|Ts],Spec) ->
- add_tests([{config,Node,[F]}|Ts],Spec);
-add_tests([{config,Files}|Ts],Spec) ->
- add_tests([{config,all_nodes,Files}|Ts],Spec);
-
-
-%% --- userconfig ---
-add_tests([{userconfig,all_nodes,CBF}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {userconfig,N,CBF} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{userconfig,Nodes,CBF}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,userconfig,[CBF],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{userconfig,Node,[{Callback, Config}|CBF]}|Ts],Spec) ->
- Cfgs = Spec#testspec.userconfig,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- add_tests([{userconfig,Node,CBF}|Ts],
- Spec#testspec{userconfig=[{Node1,{Callback,
- get_absfile(Callback, Config ,Spec)}}|Cfgs]});
-add_tests([{userconfig,_Node,[]}|Ts],Spec) ->
- add_tests(Ts,Spec);
-add_tests([{userconfig,Node,CBF}|Ts],Spec) ->
- add_tests([{userconfig,Node,[CBF]}|Ts],Spec);
-add_tests([{userconfig,CBF}|Ts],Spec) ->
- add_tests([{userconfig,all_nodes,CBF}|Ts],Spec);
-
-%% --- event_handler ---
-add_tests([{event_handler,all_nodes,Hs}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {event_handler,N,Hs,[]} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{event_handler,all_nodes,Hs,Args}|Ts],Spec) when is_list(Args) ->
- Tests = lists:map(fun(N) -> {event_handler,N,Hs,Args} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{event_handler,Hs}|Ts],Spec) ->
- add_tests([{event_handler,all_nodes,Hs,[]}|Ts],Spec);
-add_tests([{event_handler,HsOrNodes,HsOrArgs}|Ts],Spec) ->
- case is_noderef(HsOrNodes,Spec#testspec.nodes) of
- true -> % HsOrNodes == Nodes, HsOrArgs == Hs
- case {HsOrNodes,HsOrArgs} of
- {Nodes,Hs} when is_list(Nodes) ->
- Ts1 = separate(Nodes,event_handler,[Hs,[]],Ts,
- Spec#testspec.nodes),
- add_tests(Ts1,Spec);
- {_Node,[]} ->
- add_tests(Ts,Spec);
- {Node,HOrHs} ->
- EvHs = Spec#testspec.event_handler,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- case HOrHs of
- [H|Hs] when is_atom(H) ->
- add_tests([{event_handler,Node,Hs}|Ts],
- Spec#testspec{event_handler=[{Node1,H,[]}|EvHs]});
- H when is_atom(H) ->
- add_tests(Ts,Spec#testspec{event_handler=[{Node1,H,[]}|EvHs]})
- end
- end;
- false -> % HsOrNodes == Hs, HsOrArgs == Args
- add_tests([{event_handler,all_nodes,HsOrNodes,HsOrArgs}|Ts],Spec)
- end;
-add_tests([{event_handler,Nodes,Hs,Args}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,event_handler,[Hs,Args],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{event_handler,Node,[H|Hs],Args}|Ts],Spec) when is_atom(H) ->
- EvHs = Spec#testspec.event_handler,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- add_tests([{event_handler,Node,Hs,Args}|Ts],
- Spec#testspec{event_handler=[{Node1,H,Args}|EvHs]});
-add_tests([{event_handler,_Node,[],_Args}|Ts],Spec) ->
- add_tests(Ts,Spec);
-add_tests([{event_handler,Node,H,Args}|Ts],Spec) when is_atom(H) ->
- EvHs = Spec#testspec.event_handler,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- add_tests(Ts,Spec#testspec{event_handler=[{Node1,H,Args}|EvHs]});
-
-%% --- ct_hooks --
-add_tests([{ct_hooks, all_nodes, Hooks} | Ts], Spec) ->
- Tests = [{ct_hooks,N,Hooks} || N <- list_nodes(Spec)],
- add_tests(Tests ++ Ts, Spec);
-add_tests([{ct_hooks, Node, [Hook|Hooks]}|Ts], Spec) ->
- SuiteCbs = Spec#testspec.ct_hooks,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- add_tests([{ct_hooks, Node, Hooks} | Ts],
- Spec#testspec{ct_hooks = [{Node1,Hook} | SuiteCbs]});
-add_tests([{ct_hooks, _Node, []}|Ts], Spec) ->
- add_tests(Ts, Spec);
-add_tests([{ct_hooks, Hooks}|Ts], Spec) ->
- add_tests([{ct_hooks, all_nodes, Hooks}|Ts], Spec);
-
-%% -- enable_builtin_hooks --
-add_tests([{enable_builtin_hooks,Bool}|Ts],Spec) ->
- add_tests(Ts, Spec#testspec{ enable_builtin_hooks = Bool });
-
-%% --- include ---
-add_tests([{include,all_nodes,InclDirs}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {include,N,InclDirs} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{include,Nodes,InclDirs}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,include,[InclDirs],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{include,Node,[D|Ds]}|Ts],Spec) when is_list(D) ->
- Dirs = Spec#testspec.include,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- add_tests([{include,Node,Ds}|Ts],
- Spec#testspec{include=[{Node1,get_absdir(D,Spec)}|Dirs]});
-add_tests([{include,_Node,[]}|Ts],Spec) ->
- add_tests(Ts,Spec);
-add_tests([{include,Node,D}|Ts],Spec) ->
- add_tests([{include,Node,[D]}|Ts],Spec);
-add_tests([{include,InclDirs}|Ts],Spec) ->
- add_tests([{include,all_nodes,InclDirs}|Ts],Spec);
+%% -----------------------------------------------------
+%% / \
+%% | When adding test/config terms, remember to update |
+%% | valid_terms/0 also! |
+%% \ /
+%% -----------------------------------------------------
%% --- suites ---
add_tests([{suites,all_nodes,Dir,Ss}|Ts],Spec) ->
@@ -719,7 +642,7 @@ add_tests([{suites,Nodes,Dir,Ss}|Ts],Spec) when is_list(Nodes) ->
add_tests([{suites,Node,Dir,Ss}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = insert_suites(ref2node(Node,Spec#testspec.nodes),
- ref2dir(Dir,Spec#testspec.alias),
+ ref2dir(Dir,Spec),
Ss,Tests, Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
@@ -739,20 +662,22 @@ add_tests([{groups,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec) ->
add_tests([{groups,Nodes,Dir,Suite,Gs}|Ts],Spec) when is_list(Nodes) ->
Ts1 = separate(Nodes,groups,[Dir,Suite,Gs],Ts,Spec#testspec.nodes),
add_tests(Ts1,Spec);
-add_tests([{groups,Nodes,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,groups,[Dir,Suite,Gs,{cases,TCs}],Ts,Spec#testspec.nodes),
+add_tests([{groups,Nodes,Dir,Suite,Gs,{cases,TCs}}|Ts],
+ Spec) when is_list(Nodes) ->
+ Ts1 = separate(Nodes,groups,[Dir,Suite,Gs,{cases,TCs}],Ts,
+ Spec#testspec.nodes),
add_tests(Ts1,Spec);
add_tests([{groups,Node,Dir,Suite,Gs}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = insert_groups(ref2node(Node,Spec#testspec.nodes),
- ref2dir(Dir,Spec#testspec.alias),
+ ref2dir(Dir,Spec),
Suite,Gs,all,Tests,
Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
add_tests([{groups,Node,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = insert_groups(ref2node(Node,Spec#testspec.nodes),
- ref2dir(Dir,Spec#testspec.alias),
+ ref2dir(Dir,Spec),
Suite,Gs,TCs,Tests,
Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
@@ -768,7 +693,7 @@ add_tests([{cases,Nodes,Dir,Suite,Cs}|Ts],Spec) when is_list(Nodes) ->
add_tests([{cases,Node,Dir,Suite,Cs}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = insert_cases(ref2node(Node,Spec#testspec.nodes),
- ref2dir(Dir,Spec#testspec.alias),
+ ref2dir(Dir,Spec),
Suite,Cs,Tests, Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
@@ -783,7 +708,7 @@ add_tests([{skip_suites,Nodes,Dir,Ss,Cmt}|Ts],Spec) when is_list(Nodes) ->
add_tests([{skip_suites,Node,Dir,Ss,Cmt}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = skip_suites(ref2node(Node,Spec#testspec.nodes),
- ref2dir(Dir,Spec#testspec.alias),
+ ref2dir(Dir,Spec),
Ss,Cmt,Tests,
Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
@@ -792,7 +717,8 @@ add_tests([{skip_suites,Node,Dir,Ss,Cmt}|Ts],Spec) ->
add_tests([{skip_groups,all_nodes,Dir,Suite,Gs,Cmt}|Ts],Spec) ->
add_tests([{skip_groups,list_nodes(Spec),Dir,Suite,Gs,Cmt}|Ts],Spec);
add_tests([{skip_groups,all_nodes,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) ->
- add_tests([{skip_groups,list_nodes(Spec),Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec);
+ add_tests([{skip_groups,list_nodes(Spec),Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],
+ Spec);
add_tests([{skip_groups,Dir,Suite,Gs,Cmt}|Ts],Spec) ->
add_tests([{skip_groups,all_nodes,Dir,Suite,Gs,Cmt}|Ts],Spec);
add_tests([{skip_groups,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) ->
@@ -800,20 +726,22 @@ add_tests([{skip_groups,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) ->
add_tests([{skip_groups,Nodes,Dir,Suite,Gs,Cmt}|Ts],Spec) when is_list(Nodes) ->
Ts1 = separate(Nodes,skip_groups,[Dir,Suite,Gs,Cmt],Ts,Spec#testspec.nodes),
add_tests(Ts1,Spec);
-add_tests([{skip_groups,Nodes,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,skip_groups,[Dir,Suite,Gs,{cases,TCs},Cmt],Ts,Spec#testspec.nodes),
+add_tests([{skip_groups,Nodes,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],
+ Spec) when is_list(Nodes) ->
+ Ts1 = separate(Nodes,skip_groups,[Dir,Suite,Gs,{cases,TCs},Cmt],Ts,
+ Spec#testspec.nodes),
add_tests(Ts1,Spec);
add_tests([{skip_groups,Node,Dir,Suite,Gs,Cmt}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = skip_groups(ref2node(Node,Spec#testspec.nodes),
- ref2dir(Dir,Spec#testspec.alias),
+ ref2dir(Dir,Spec),
Suite,Gs,all,Cmt,Tests,
Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
add_tests([{skip_groups,Node,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = skip_groups(ref2node(Node,Spec#testspec.nodes),
- ref2dir(Dir,Spec#testspec.alias),
+ ref2dir(Dir,Spec),
Suite,Gs,TCs,Cmt,Tests,
Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
@@ -829,45 +757,101 @@ add_tests([{skip_cases,Nodes,Dir,Suite,Cs,Cmt}|Ts],Spec) when is_list(Nodes) ->
add_tests([{skip_cases,Node,Dir,Suite,Cs,Cmt}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = skip_cases(ref2node(Node,Spec#testspec.nodes),
- ref2dir(Dir,Spec#testspec.alias),
+ ref2dir(Dir,Spec),
Suite,Cs,Cmt,Tests,Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
+%% --- various configuration terms ---
+add_tests([{config,Nodes,CfgDir,Files}|Ts],Spec) when is_list(Nodes);
+ Nodes == all_nodes ->
+ add_tests([{config,Nodes,{CfgDir,Files}}|Ts],Spec);
+add_tests([{config,Node,CfgDir,FileOrFiles}|Ts],Spec) ->
+ add_tests([{config,Node,{CfgDir,FileOrFiles}}|Ts],Spec);
+add_tests([{config,CfgDir=[Ch|_],Files}|Ts],Spec) when is_integer(Ch) ->
+ add_tests([{config,all_nodes,{CfgDir,Files}}|Ts],Spec);
+
+add_tests([{event_handler,Nodes,Hs,Args}|Ts],Spec) when is_list(Nodes);
+ Nodes == all_nodes ->
+ add_tests([{event_handler,Nodes,{Hs,Args}}|Ts],Spec);
+add_tests([{event_handler,Node,HOrHs,Args}|Ts],Spec) ->
+ add_tests([{event_handler,Node,{HOrHs,Args}}|Ts],Spec);
+
+add_tests([{enable_builtin_hooks,Bool}|Ts],Spec) ->
+ add_tests(Ts, Spec#testspec{enable_builtin_hooks = Bool});
+
+add_tests([{release_shell,Bool}|Ts],Spec) ->
+ add_tests(Ts, Spec#testspec{release_shell = Bool});
+
%% --- handled/errors ---
+add_tests([{define,_,_}|Ts],Spec) -> % handled
+ add_tests(Ts,Spec);
+
add_tests([{alias,_,_}|Ts],Spec) -> % handled
add_tests(Ts,Spec);
add_tests([{node,_,_}|Ts],Spec) -> % handled
add_tests(Ts,Spec);
-add_tests([{merge_tests, _} | Ts], Spec) -> % handled
+add_tests([{merge_tests, _} | Ts], Spec) -> % handled
add_tests(Ts,Spec);
-%% check if it's a CT term that has bad format or if the user seems to
-%% have added something of his/her own, which we'll let pass if relaxed
-%% mode is enabled.
-add_tests([Other|Ts],Spec) when is_tuple(Other) ->
- [Name|_] = tuple_to_list(Other),
- case lists:keymember(Name,1,valid_terms()) of
- true -> % halt
- throw({error,{bad_term_in_spec,Other}});
- false -> % ignore
- case get(relaxed) of
- true ->
- %% warn if name resembles a CT term
- case resembles_ct_term(Name,size(Other)) of
- true ->
- io:format("~nSuspicious term, please check:~n"
- "~p~n", [Other]);
- false ->
- ok
- end,
- add_tests(Ts,Spec);
- false ->
- throw({error,{undefined_term_in_spec,Other}})
- end
+%% --------------------------------------------------
+%% / \
+%% | General add_tests/2 clauses below will work for |
+%% | most test spec configuration terms |
+%% \ /
+%% --------------------------------------------------
+
+%% create one test entry per known node and reinsert
+add_tests([Term={Tag,all_nodes,Data}|Ts],Spec) ->
+ case check_term(Term) of
+ valid ->
+ Tests = [{Tag,Node,Data} || Node <- list_nodes(Spec),
+ should_be_added(Tag,Node,Data,Spec)],
+ add_tests(Tests++Ts,Spec);
+ invalid -> % ignore term
+ add_tests(Ts,Spec)
end;
-
+%% create one test entry per node in Nodes and reinsert
+add_tests([{Tag,[],Data}|Ts],Spec) ->
+ add_tests([{Tag,all_nodes,Data}|Ts],Spec);
+add_tests([{Tag,String=[Ch|_],Data}|Ts],Spec) when is_integer(Ch) ->
+ add_tests([{Tag,all_nodes,{String,Data}}|Ts],Spec);
+add_tests([{Tag,NodesOrOther,Data}|Ts],Spec) when is_list(NodesOrOther) ->
+ case lists:all(fun(Test) -> is_node(Test,Spec#testspec.nodes)
+ end, NodesOrOther) of
+ true ->
+ Ts1 = separate(NodesOrOther,Tag,[Data],Ts,Spec#testspec.nodes),
+ add_tests(Ts1,Spec);
+ false ->
+ add_tests([{Tag,all_nodes,{NodesOrOther,Data}}|Ts],Spec)
+ end;
+%% update data for testspec term of type Tag
+add_tests([Term={Tag,NodeOrOther,Data}|Ts],Spec) ->
+ case is_node(NodeOrOther,Spec#testspec.nodes) of
+ true ->
+ case check_term(Term) of
+ valid ->
+ Node = ref2node(NodeOrOther,Spec#testspec.nodes),
+ NodeIxData =
+ update_recorded(Tag,Node,Spec) ++
+ handle_data(Tag,Node,Data,Spec),
+ add_tests(Ts,mod_field(Spec,Tag,NodeIxData));
+ invalid -> % ignore term
+ add_tests(Ts,Spec)
+ end;
+ false ->
+ add_tests([{Tag,all_nodes,{NodeOrOther,Data}}|Ts],Spec)
+ end;
+%% this test should be added for all known nodes
+add_tests([Term={Tag,Data}|Ts],Spec) ->
+ case check_term(Term) of
+ valid ->
+ add_tests([{Tag,all_nodes,Data}|Ts],Spec);
+ invalid ->
+ add_tests(Ts,Spec)
+ end;
+%% some other data than a tuple
add_tests([Other|Ts],Spec) ->
case get(relaxed) of
true ->
@@ -879,6 +863,118 @@ add_tests([Other|Ts],Spec) ->
add_tests([],Spec) -> % done
Spec.
+%% check if it's a CT term that has bad format or if the user seems to
+%% have added something of his/her own, which we'll let pass if relaxed
+%% mode is enabled.
+check_term(Term) ->
+ Size = size(Term),
+ [Name|_] = tuple_to_list(Term),
+ Valid = valid_terms(),
+ case lists:member({Name,Size},Valid) of
+ true ->
+ valid;
+ false ->
+ case lists:keymember(Name,1,Valid) of
+ true -> % halt
+ throw({error,{bad_term_in_spec,Term}});
+ false -> % ignore
+ case get(relaxed) of
+ true ->
+ %% warn if name resembles a CT term
+ case resembles_ct_term(Name,size(Term)) of
+ true ->
+ io:format("~nSuspicious term, "
+ "please check:~n"
+ "~p~n", [Term]),
+ invalid;
+ false ->
+ invalid
+ end;
+ false ->
+ throw({error,{undefined_term_in_spec,Term}})
+ end
+ end
+ end.
+
+%% specific data handling before saving in testspec record, e.g.
+%% converting relative paths to absolute for directories and files
+%% (introduce a clause *only* if the data value needs processing)
+handle_data(logdir,Node,Dir,Spec) ->
+ [{Node,ref2dir(Dir,Spec)}];
+handle_data(cover,Node,File,Spec) ->
+ [{Node,get_absfile(File,Spec)}];
+handle_data(include,Node,Dirs=[D|_],Spec) when is_list(D) ->
+ [{Node,ref2dir(Dir,Spec)} || Dir <- Dirs];
+handle_data(include,Node,Dir=[Ch|_],Spec) when is_integer(Ch) ->
+ handle_data(include,Node,[Dir],Spec);
+handle_data(config,Node,File=[Ch|_],Spec) when is_integer(Ch) ->
+ handle_data(config,Node,[File],Spec);
+handle_data(config,Node,{CfgDir,File=[Ch|_]},Spec) when is_integer(Ch) ->
+ handle_data(config,Node,{CfgDir,[File]},Spec);
+handle_data(config,Node,Files=[F|_],Spec) when is_list(F) ->
+ [{Node,get_absfile(File,Spec)} || File <- Files];
+handle_data(config,Node,{CfgDir,Files=[F|_]},Spec) when is_list(F) ->
+ [{Node,filename:join(ref2dir(CfgDir,Spec),File)} || File <- Files];
+handle_data(userconfig,Node,CBs,Spec) when is_list(CBs) ->
+ [{Node,{Callback,get_absfile(Callback,Config,Spec)}} ||
+ {Callback,Config} <- CBs];
+handle_data(userconfig,Node,CB,Spec) when is_tuple(CB) ->
+ handle_data(userconfig,Node,[CB],Spec);
+handle_data(event_handler,Node,H,Spec) when is_atom(H) ->
+ handle_data(event_handler,Node,{[H],[]},Spec);
+handle_data(event_handler,Node,{H,Args},Spec) when is_atom(H) ->
+ handle_data(event_handler,Node,{[H],Args},Spec);
+handle_data(event_handler,Node,Hs,_Spec) when is_list(Hs) ->
+ [{Node,EvH,[]} || EvH <- Hs];
+handle_data(event_handler,Node,{Hs,Args},_Spec) when is_list(Hs) ->
+ [{Node,EvH,Args} || EvH <- Hs];
+handle_data(ct_hooks,Node,Hooks,_Spec) when is_list(Hooks) ->
+ [{Node,Hook} || Hook <- Hooks ];
+handle_data(ct_hooks,Node,Hook,_Spec) ->
+ [{Node,Hook}];
+handle_data(stylesheet,Node,CSSFile,Spec) ->
+ [{Node,get_absfile(CSSFile,Spec)}];
+handle_data(verbosity,Node,VLvls,_Spec) when is_integer(VLvls) ->
+ [{Node,[{'$unspecified',VLvls}]}];
+handle_data(verbosity,Node,VLvls,_Spec) when is_list(VLvls) ->
+ VLvls1 = lists:map(fun(VLvl = {_Cat,_Lvl}) -> VLvl;
+ (Lvl) -> {'$unspecified',Lvl} end, VLvls),
+ [{Node,VLvls1}];
+handle_data(silent_connections,Node,all,_Spec) ->
+ [{Node,[all]}];
+handle_data(silent_connections,Node,Conn,_Spec) when is_atom(Conn) ->
+ [{Node,[Conn]}];
+handle_data(silent_connections,Node,Conns,_Spec) ->
+ [{Node,Conns}];
+handle_data(_Tag,Node,Data,_Spec) ->
+ [{Node,Data}].
+
+%% check if duplicates should be saved or not
+should_be_added(Tag,Node,_Data,Spec) ->
+ if
+ %% list terms *without* possible duplicates here
+ Tag == logdir; Tag == logopts;
+ Tag == basic_html; Tag == label;
+ Tag == auto_compile; Tag == stylesheet;
+ Tag == verbosity; Tag == silent_connections ->
+ lists:keymember(ref2node(Node,Spec#testspec.nodes),1,
+ read_field(Spec,Tag)) == false;
+ %% for terms *with* possible duplicates
+ true ->
+ true
+ end.
+
+%% check if previous elements for Node should be deleted
+update_recorded(Tag,Node,Spec) ->
+ if Tag == config; Tag == userconfig; Tag == event_handler;
+ Tag == ct_hooks; Tag == include ->
+ read_field(Spec,Tag);
+ true ->
+ %% delete previous value for Tag
+ lists:keydelete(Node,1,read_field(Spec,Tag))
+ end.
+
+%% create one test term per node
separate(Nodes,Tag,Data,Tests,Refs) ->
Separated = separate(Nodes,Tag,Data,Refs),
Separated ++ Tests.
@@ -886,7 +982,25 @@ separate([N|Ns],Tag,Data,Refs) ->
[list_to_tuple([Tag,ref2node(N,Refs)|Data])|separate(Ns,Tag,Data,Refs)];
separate([],_,_,_) ->
[].
-
+
+%% read the value for FieldName in record Rec#testspec
+read_field(Rec, FieldName) ->
+ catch lists:foldl(fun(F, Pos) when F == FieldName ->
+ throw(element(Pos, Rec));
+ (_,Pos) ->
+ Pos+1
+ end,2,?testspec_fields).
+
+%% modify the value for FieldName in record Rec#testspec
+mod_field(Rec, FieldName, NewVal) ->
+ [_testspec|RecList] = tuple_to_list(Rec),
+ RecList1 =
+ (catch lists:foldl(fun(F, {Prev,[_OldVal|Rest]}) when F == FieldName ->
+ throw(lists:reverse(Prev) ++ [NewVal|Rest]);
+ (_,{Prev,[Field|Rest]}) ->
+ {[Field|Prev],Rest}
+ end,{[],RecList},?testspec_fields)),
+ list_to_tuple([testspec|RecList1]).
%% Representation:
%% {{Node,Dir},[{Suite1,[GrOrCase11,GrOrCase12,...]},
@@ -1094,33 +1208,40 @@ ref2node(all_nodes,_Refs) ->
ref2node(master,_Refs) ->
master;
ref2node(RefOrNode,Refs) ->
- case string:chr(atom_to_list(RefOrNode),$@) of
- 0 -> % a ref
+ case lists:member($@,atom_to_list(RefOrNode)) of
+ false -> % a ref
case lists:keysearch(RefOrNode,1,Refs) of
{value,{RefOrNode,Node}} ->
Node;
false ->
throw({error,{noderef_missing,RefOrNode}})
end;
- _ -> % a node
+ true -> % a node
RefOrNode
end.
-ref2dir(Ref,Refs) when is_atom(Ref) ->
+ref2dir(Ref,Spec) ->
+ ref2dir(Ref,Spec#testspec.alias,Spec).
+
+ref2dir(Ref,Refs,Spec) when is_atom(Ref) ->
case lists:keysearch(Ref,1,Refs) of
{value,{Ref,Dir}} ->
- Dir;
+ get_absdir(Dir,Spec);
false ->
throw({error,{alias_missing,Ref}})
end;
-ref2dir(Dir,_) when is_list(Dir) ->
- Dir.
-
-is_noderef(What,Nodes) when is_atom(What) ->
- is_noderef([What],Nodes);
-is_noderef([master|_],_Nodes) ->
+ref2dir(Dir,_,Spec) when is_list(Dir) ->
+ get_absdir(Dir,Spec);
+ref2dir(What,_,_) ->
+ throw({error,{invalid_directory_name,What}}).
+
+is_node(What,Nodes) when is_atom(What) ->
+ is_node([What],Nodes);
+is_node([master|_],_Nodes) ->
true;
-is_noderef([What|_],Nodes) ->
+is_node(What={N,H},Nodes) when is_atom(N), is_atom(H) ->
+ is_node([What],Nodes);
+is_node([What|_],Nodes) ->
case lists:keymember(What,1,Nodes) or
lists:keymember(What,2,Nodes) of
true ->
@@ -1128,24 +1249,32 @@ is_noderef([What|_],Nodes) ->
false ->
false
end;
-is_noderef([],_) ->
+is_node([],_) ->
false.
valid_terms() ->
[
+ {define,3},
{node,3},
{cover,2},
{cover,3},
{config,2},
{config,3},
+ {config,4},
{userconfig,2},
{userconfig,3},
{alias,3},
- {merge_tests,1},
+ {merge_tests,2},
{logdir,2},
{logdir,3},
{logopts,2},
{logopts,3},
+ {basic_html,2},
+ {basic_html,3},
+ {verbosity,2},
+ {verbosity,3},
+ {silent_connections,2},
+ {silent_connections,3},
{label,2},
{label,3},
{event_handler,2},
@@ -1153,13 +1282,18 @@ valid_terms() ->
{event_handler,4},
{ct_hooks,2},
{ct_hooks,3},
- {enable_builtin_hooks,1},
+ {enable_builtin_hooks,2},
+ {release_shell,2},
{multiply_timetraps,2},
{multiply_timetraps,3},
{scale_timetraps,2},
{scale_timetraps,3},
{include,2},
{include,3},
+ {auto_compile,2},
+ {auto_compile,3},
+ {stylesheet,2},
+ {stylesheet,3},
{suites,3},
{suites,4},
{groups,4},
@@ -1174,7 +1308,8 @@ valid_terms() ->
{skip_groups,7},
{skip_cases,5},
{skip_cases,6},
- {create_priv_dir,2}
+ {create_priv_dir,2},
+ {create_priv_dir,3}
].
%% this function "guesses" if the user has misspelled a term name
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index 9d6ee3c8b9..cf891ed043 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -25,7 +25,8 @@
%%%
-module(ct_util).
--export([start/0,start/1,start/2,stop/1,update_last_run_index/0]).
+-export([start/0,start/1,start/2,start/3,
+ stop/1,update_last_run_index/0]).
-export([register_connection/4,unregister_connection/1,
does_connection_exist/3,get_key_from_name/1]).
@@ -36,14 +37,15 @@
save_suite_data_async/3, save_suite_data_async/2,
read_suite_data/1,
delete_suite_data/0, delete_suite_data/1, match_delete_suite_data/1,
- delete_testdata/0, delete_testdata/1, set_testdata/1, get_testdata/1,
- set_testdata_async/1, update_testdata/2]).
+ delete_testdata/0, delete_testdata/1,
+ set_testdata/1, get_testdata/1, get_testdata/2,
+ set_testdata_async/1, update_testdata/2, update_testdata/3]).
-export([override_silence_all_connections/0, override_silence_connections/1,
get_overridden_silenced_connections/0,
delete_overridden_silenced_connections/0,
- silence_all_connections/0, silence_connections/1, is_silenced/1,
- reset_silent_connections/0]).
+ silence_all_connections/0, silence_connections/1,
+ is_silenced/1, is_silenced/2, reset_silent_connections/0]).
-export([get_mode/0, create_table/3, read_opts/0]).
@@ -64,9 +66,13 @@
-export([get_profile_data/0, get_profile_data/1,
get_profile_data/2, open_url/3]).
+-include("ct.hrl").
-include("ct_event.hrl").
-include("ct_util.hrl").
+-define(default_verbosity, [{default,?MAX_VERBOSITY},
+ {'$unspecified',?MAX_VERBOSITY}]).
+
-record(suite_data, {key,name,value}).
%%%-----------------------------------------------------------------
@@ -85,18 +91,21 @@
%%%
%%% @see ct
start() ->
- start(normal,".").
+ start(normal, ".", ?default_verbosity).
start(LogDir) when is_list(LogDir) ->
- start(normal,LogDir);
+ start(normal, LogDir, ?default_verbosity);
start(Mode) ->
- start(Mode,".").
+ start(Mode, ".", ?default_verbosity).
+
+start(LogDir, Verbosity) when is_list(LogDir) ->
+ start(normal, LogDir, Verbosity).
-start(Mode,LogDir) ->
+start(Mode, LogDir, Verbosity) ->
case whereis(ct_util_server) of
undefined ->
S = self(),
- Pid = spawn_link(fun() -> do_start(S,Mode,LogDir) end),
+ Pid = spawn_link(fun() -> do_start(S, Mode, LogDir, Verbosity) end),
receive
{Pid,started} -> Pid;
{Pid,Error} -> exit(Error);
@@ -113,7 +122,7 @@ start(Mode,LogDir) ->
end
end.
-do_start(Parent,Mode,LogDir) ->
+do_start(Parent, Mode, LogDir, Verbosity) ->
process_flag(trap_exit,true),
register(ct_util_server,self()),
create_table(?conn_table,#conn.handle),
@@ -173,7 +182,7 @@ do_start(Parent,Mode,LogDir) ->
false ->
ok
end,
- {StartTime,TestLogDir} = ct_logs:init(Mode),
+ {StartTime,TestLogDir} = ct_logs:init(Mode, Verbosity),
ct_event:notify(#event{name=test_start,
node=node(),
@@ -193,7 +202,7 @@ do_start(Parent,Mode,LogDir) ->
self() ! {{stop,{self(),{user_error,CTHReason}}},
{Parent,make_ref()}}
end,
- loop(Mode,[],StartDir).
+ loop(Mode, [{{verbosity,Cat},Lvl} || {Cat,Lvl} <- Verbosity], StartDir).
create_table(TableName,KeyPos) ->
create_table(TableName,set,KeyPos).
@@ -243,7 +252,10 @@ delete_testdata(Key) ->
call({delete_testdata, Key}).
update_testdata(Key, Fun) ->
- call({update_testdata, Key, Fun}).
+ update_testdata(Key, Fun, []).
+
+update_testdata(Key, Fun, Opts) ->
+ call({update_testdata, Key, Fun, Opts}).
set_testdata(TestData) ->
call({set_testdata, TestData}).
@@ -254,6 +266,9 @@ set_testdata_async(TestData) ->
get_testdata(Key) ->
call({get_testdata, Key}).
+get_testdata(Key, Timeout) ->
+ call({get_testdata, Key}, Timeout).
+
set_cwd(Dir) ->
call({set_cwd,Dir}).
@@ -321,7 +336,7 @@ loop(Mode,TestData,StartDir) ->
return(From,undefined)
end,
loop(From,TestData,StartDir);
- {{update_testdata,Key,Fun},From} ->
+ {{update_testdata,Key,Fun,Opts},From} ->
TestData1 =
case lists:keysearch(Key,1,TestData) of
{value,{Key,Val}} ->
@@ -329,8 +344,15 @@ loop(Mode,TestData,StartDir) ->
return(From,NewVal),
[{Key,NewVal}|lists:keydelete(Key,1,TestData)];
_ ->
- return(From,undefined),
- TestData
+ case lists:member(create,Opts) of
+ true ->
+ InitVal = Fun(undefined),
+ return(From,InitVal),
+ [{Key,InitVal}|TestData];
+ false ->
+ return(From,undefined),
+ TestData
+ end
end,
loop(From,TestData1,StartDir);
{{set_cwd,Dir},From} ->
@@ -369,14 +391,25 @@ loop(Mode,TestData,StartDir) ->
{'EXIT',_Pid,normal} ->
loop(Mode,TestData,StartDir);
{'EXIT',Pid,Reason} ->
- %% Let process crash in case of error, this shouldn't happen!
- io:format("\n\nct_util_server got EXIT from ~p: ~p\n\n",
- [Pid,Reason]),
- file:set_cwd(StartDir),
- exit(Reason)
+ case ets:lookup(?conn_table,Pid) of
+ [#conn{address=A,callback=CB}] ->
+ %% A connection crashed - remove the connection but don't die
+ ct_logs:tc_log_async(ct_error_notify,
+ "Connection process died: "
+ "Pid: ~p, Address: ~p, Callback: ~p\n"
+ "Reason: ~p\n\n",
+ [Pid,A,CB,Reason]),
+ catch CB:close(Pid),
+ loop(Mode,TestData,StartDir);
+ _ ->
+ %% Let process crash in case of error, this shouldn't happen!
+ io:format("\n\nct_util_server got EXIT from ~p: ~p\n\n",
+ [Pid,Reason]),
+ file:set_cwd(StartDir),
+ exit(Reason)
+ end
end.
-
close_connections([#conn{handle=Handle,callback=CB}|Conns]) ->
CB:close(Handle),
close_connections(Conns);
@@ -508,7 +541,7 @@ close_connections() ->
%%%
%%% @doc
override_silence_all_connections() ->
- Protocols = [telnet,ftp,rpc,snmp],
+ Protocols = [telnet,ftp,rpc,snmp,ssh],
override_silence_connections(Protocols),
Protocols.
@@ -545,7 +578,10 @@ silence_connections(Conns) when is_list(Conns) ->
set_testdata({silent_connections,Conns1}).
is_silenced(Conn) ->
- case get_testdata(silent_connections) of
+ is_silenced(Conn, infinity).
+
+is_silenced(Conn, Timeout) ->
+ case get_testdata(silent_connections, Timeout) of
Conns when is_list(Conns) ->
case lists:keysearch(Conn,1,Conns) of
{value,{Conn,true}} ->
@@ -553,6 +589,8 @@ is_silenced(Conn) ->
_ ->
false
end;
+ Error = {error,_} ->
+ Error;
_ ->
false
end.
@@ -827,19 +865,28 @@ get_profile_data(Profile, Key, StartDir) ->
%%%-----------------------------------------------------------------
%%% Internal functions
call(Msg) ->
- case whereis(ct_util_server) of
- undefined ->
+ call(Msg, infinity).
+
+call(Msg, Timeout) ->
+ case {self(),whereis(ct_util_server)} of
+ {_,undefined} ->
{error,ct_util_server_not_running};
- Pid ->
+ {Pid,Pid} ->
+ %% the caller is ct_util_server, which must
+ %% be a mistake
+ {error,bad_invocation};
+ {Self,Pid} ->
MRef = erlang:monitor(process, Pid),
Ref = make_ref(),
- ct_util_server ! {Msg,{self(),Ref}},
+ ct_util_server ! {Msg,{Self,Ref}},
receive
{Ref, Result} ->
erlang:demonitor(MRef, [flush]),
Result;
{'DOWN',MRef,process,_,Reason} ->
{error,{ct_util_server_down,Reason}}
+ after
+ Timeout -> {error,timeout}
end
end.
diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl
index 6b016e95df..196b5e46d0 100644
--- a/lib/common_test/src/ct_util.hrl
+++ b/lib/common_test/src/ct_util.hrl
@@ -34,13 +34,19 @@
profile=[],
logdir=["."],
logopts=[],
+ basic_html=[],
+ verbosity=[],
+ silent_connections=[],
cover=[],
config=[],
userconfig=[],
event_handler=[],
ct_hooks=[],
enable_builtin_hooks=true,
+ release_shell=false,
include=[],
+ auto_compile=[],
+ stylesheet=[],
multiply_timetraps=[],
scale_timetraps=[],
create_priv_dir=[],
@@ -64,3 +70,11 @@
-define(ct_config_txt, ct_config_plain).
-define(ct_profile_file, ".common_test").
+
+-define(css_default, "ct_default.css").
+-define(sortable_table_name, "SortableTable").
+-define(jquery_script, "jquery-latest.js").
+-define(tablesorter_script, "jquery.tablesorter.min.js").
+
+%% Logging information for error handler
+-record(conn_log, {client, name, address, action, module}).
diff --git a/lib/common_test/src/cth_conn_log.erl b/lib/common_test/src/cth_conn_log.erl
new file mode 100644
index 0000000000..3af89db3a5
--- /dev/null
+++ b/lib/common_test/src/cth_conn_log.erl
@@ -0,0 +1,124 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%----------------------------------------------------------------------
+%% CT hook for logging of connections.
+%%
+%% HookOptions can be hardcoded in the test suite:
+%%
+%% suite() ->
+%% [{ct_hooks, [{cth_conn_log,
+%% [{ct_netconfc:conn_mod(),ct_netconfc:hook_options()}]}]}].
+%%
+%% or specified in a configuration file:
+%%
+%% {ct_conn_log,[{ct_netconfc:conn_mod(),ct_netconfc:hook_options()}]}.
+%%
+%% The conn_mod() is the common test module implementing the protocol,
+%% e.g. ct_netconfc, ct_telnet, etc. This module must log by calling
+%%
+%% error_logger:info_report(ConnLogInfo,Data).
+%% ConnLogInfo = #conn_log{} | {ct_connection,Action,ConnName}
+%% Action = open | close | send | recv | term()
+%% ConnName = atom() - The 'KeyOrName' argument used when opening the connection
+%%
+%% ct_conn_log_h will print to html log or separate file (depending on
+%% log_type() option). conn_mod() must implement and export
+%%
+%% format_data(log_type(), Data).
+%%
+%% If logging to separate file, ct_conn_log_h will also log error
+%% reports which are witten like this:
+%%
+%% error_logger:error_report([{ct_connection,ConnName} | Report]).
+%%
+%%----------------------------------------------------------------------
+-module(cth_conn_log).
+
+-include_lib("common_test/include/ct.hrl").
+
+-export([init/2,
+ pre_init_per_testcase/3,
+ post_end_per_testcase/4]).
+
+-spec init(Id, HookOpts) -> Result when
+ Id :: term(),
+ HookOpts :: ct:hook_options(),
+ Result :: {ok,[{ct_netconfc:conn_mod(),
+ {ct_netconfc:log_type(),[ct_netconfc:key_or_name()]}}]}.
+init(_Id, HookOpts) ->
+ ConfOpts = ct:get_config(ct_conn_log,[]),
+ {ok,merge_log_info(ConfOpts,HookOpts)}.
+
+merge_log_info([{Mod,ConfOpts}|ConfList],HookList) ->
+ {Opts,HookList1} =
+ case lists:keytake(Mod,1,HookList) of
+ false ->
+ {ConfOpts,HookList};
+ {value,{_,HookOpts},HL1} ->
+ {ConfOpts ++ HookOpts, HL1} % ConfOpts overwrites HookOpts!
+ end,
+ [{Mod,get_log_opts(Opts)} | merge_log_info(ConfList,HookList1)];
+merge_log_info([],HookList) ->
+ [{Mod,get_log_opts(Opts)} || {Mod,Opts} <- HookList].
+
+get_log_opts(Opts) ->
+ LogType = proplists:get_value(log_type,Opts,html),
+ Hosts = proplists:get_value(hosts,Opts,[]),
+ {LogType,Hosts}.
+
+
+pre_init_per_testcase(TestCase,Config,CthState) ->
+ Logs =
+ lists:map(
+ fun({ConnMod,{LogType,Hosts}}) ->
+ case LogType of
+ LogType when LogType==raw; LogType==pretty ->
+ Dir = ?config(priv_dir,Config),
+ TCStr = atom_to_list(TestCase),
+ ConnModStr = atom_to_list(ConnMod),
+ DefLogName = TCStr ++ "-" ++ ConnModStr ++ ".txt",
+ DefLog = filename:join(Dir,DefLogName),
+ Ls = [{Host,
+ filename:join(Dir,TCStr ++ "-"++
+ atom_to_list(Host) ++ "-" ++
+ ConnModStr ++
+ ".txt")}
+ || Host <- Hosts]
+ ++[{default,DefLog}],
+ Str =
+ "<table borders=1>"
+ "<b>" ++ ConnModStr ++ " logs:</b>\n" ++
+ [io_lib:format(
+ "<tr><td>~p</td><td><a href=~p>~s</a></td></tr>",
+ [S,L,filename:basename(L)])
+ || {S,L} <- Ls] ++
+ "</table>",
+ io:format(Str,[]),
+ {ConnMod,{LogType,Ls}};
+ _ ->
+ {ConnMod,{LogType,[]}}
+ end
+ end,
+ CthState),
+ error_logger:add_report_handler(ct_conn_log_h,{group_leader(),Logs}),
+ {Config,CthState}.
+
+post_end_per_testcase(_TestCase,_Config,Return,CthState) ->
+ error_logger:delete_report_handler(ct_conn_log_h),
+ {Return,CthState}.
diff --git a/lib/common_test/src/cth_surefire.erl b/lib/common_test/src/cth_surefire.erl
index c42f956b3a..76b0f0b5ea 100644
--- a/lib/common_test/src/cth_surefire.erl
+++ b/lib/common_test/src/cth_surefire.erl
@@ -49,9 +49,12 @@ init(Path, Opts) ->
properties = proplists:get_value(properties,Opts,[]),
timer = now() }.
-pre_init_per_suite(Suite,Config,State) ->
+pre_init_per_suite(Suite,Config,#state{ test_cases = [] } = State) ->
{Config, init_tc(State#state{ curr_suite = Suite, curr_suite_ts = now() },
- Config) }.
+ Config) };
+pre_init_per_suite(Suite,Config,State) ->
+ %% Have to close the previous suite
+ pre_init_per_suite(Suite,Config,close_suite(State)).
post_init_per_suite(_Suite,Config, Result, State) ->
{Result, end_tc(init_per_suite,Config,Result,State)}.
@@ -59,11 +62,7 @@ post_init_per_suite(_Suite,Config, Result, State) ->
pre_end_per_suite(_Suite,Config,State) -> {Config, init_tc(State, Config)}.
post_end_per_suite(_Suite,Config,Result,State) ->
- NewState = end_tc(end_per_suite,Config,Result,State),
- TCs = NewState#state.test_cases,
- Suite = get_suite(NewState, TCs),
- {Result, State#state{ test_cases = [],
- test_suites = [Suite | State#state.test_suites]}}.
+ {Result, end_tc(end_per_suite,Config,Result,State)}.
pre_init_per_group(Group,Config,State) ->
{Config, init_tc(State#state{ curr_group = [Group|State#state.curr_group]},
@@ -83,24 +82,36 @@ pre_init_per_testcase(_TC,Config,State) -> {Config, init_tc(State, Config)}.
post_end_per_testcase(TC,Config,Result,State) ->
{Result, end_tc(TC,Config, Result,State)}.
+on_tc_fail(_TC, _Res, State = #state{test_cases = []}) ->
+ State;
on_tc_fail(_TC, Res, State) ->
TCs = State#state.test_cases,
- TC = hd(State#state.test_cases),
- NewTC = TC#testcase{ failure =
- {fail,lists:flatten(io_lib:format("~p",[Res]))} },
+ TC = hd(TCs),
+ NewTC = TC#testcase{
+ failure =
+ {fail,lists:flatten(io_lib:format("~p",[Res]))} },
State#state{ test_cases = [NewTC | tl(TCs)]}.
+on_tc_skip(Tc,{Type,_Reason} = Res, State) when Type == tc_auto_skip ->
+ do_tc_skip(Res, end_tc(Tc,[],Res,init_tc(State,[])));
+on_tc_skip(_Tc, _Res, State = #state{test_cases = []}) ->
+ State;
on_tc_skip(_Tc, Res, State) ->
+ do_tc_skip(Res, State).
+
+do_tc_skip(Res, State) ->
TCs = State#state.test_cases,
- TC = hd(State#state.test_cases),
+ TC = hd(TCs),
NewTC = TC#testcase{
failure =
{skipped,lists:flatten(io_lib:format("~p",[Res]))} },
State#state{ test_cases = [NewTC | tl(TCs)]}.
+init_tc(State, Config) when is_list(Config) == false ->
+ State#state{ timer = now(), tc_log = "" };
init_tc(State, Config) ->
State#state{ timer = now(),
- tc_log = proplists:get_value(tc_logfile, Config)}.
+ tc_log = proplists:get_value(tc_logfile, Config, [])}.
end_tc(Func, Config, Res, State) when is_atom(Func) ->
end_tc(atom_to_list(Func), Config, Res, State);
@@ -118,26 +129,35 @@ end_tc(Name, _Config, _Res, State = #state{ curr_suite = Suite,
name = Name,
time = TimeTakes,
failure = passed }| State#state.test_cases]}.
-
-get_suite(State, TCs) ->
+close_suite(#state{ test_cases = [] } = State) ->
+ State;
+close_suite(#state{ test_cases = TCs } = State) ->
Total = length(TCs),
Succ = length(lists:filter(fun(#testcase{ failure = F }) ->
F == passed
end,TCs)),
Fail = Total - Succ,
TimeTaken = timer:now_diff(now(),State#state.curr_suite_ts) / 1000000,
- #testsuite{ name = atom_to_list(State#state.curr_suite),
- package = State#state.package,
- time = io_lib:format("~f",[TimeTaken]),
- timestamp = now_to_string(State#state.curr_suite_ts),
- errors = Fail, tests = Total, testcases = lists:reverse(TCs) }.
-
-terminate(State) ->
- {ok,D} = file:open(State#state.filepath,[write]),
+ Suite = #testsuite{ name = atom_to_list(State#state.curr_suite),
+ package = State#state.package,
+ time = io_lib:format("~f",[TimeTaken]),
+ timestamp = now_to_string(State#state.curr_suite_ts),
+ errors = Fail, tests = Total,
+ testcases = lists:reverse(TCs) },
+ State#state{ test_cases = [],
+ test_suites = [Suite | State#state.test_suites]}.
+
+terminate(State = #state{ test_cases = [] }) ->
+ {ok,D} = file:open(State#state.filepath,[write,{encoding,utf8}]),
io:format(D, "<?xml version=\"1.0\" encoding= \"UTF-8\" ?>", []),
io:format(D, to_xml(State), []),
catch file:sync(D),
- catch file:close(D).
+ catch file:close(D);
+terminate(State) ->
+ %% Have to close the last suite
+ terminate(close_suite(State)).
+
+
to_xml(#testcase{ group = Group, classname = CL, log = L, name = N, time = T, timestamp = TS, failure = F}) ->
["<testcase ",
diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile
index 560a0b0d5a..7691920993 100644
--- a/lib/common_test/test/Makefile
+++ b/lib/common_test/test/Makefile
@@ -39,13 +39,21 @@ MODULES= \
ct_sequence_1_SUITE \
ct_repeat_1_SUITE \
ct_testspec_1_SUITE \
+ ct_testspec_2_SUITE \
ct_skip_SUITE \
ct_error_SUITE \
ct_test_server_if_1_SUITE \
ct_config_SUITE \
ct_master_SUITE \
ct_misc_1_SUITE \
- ct_hooks_SUITE
+ ct_hooks_SUITE \
+ ct_netconfc_SUITE \
+ ct_basic_html_SUITE \
+ ct_auto_compile_SUITE \
+ ct_verbosity_SUITE \
+ ct_shell_SUITE \
+ ct_system_error_SUITE \
+ ct_snmp_SUITE
ERL_FILES= $(MODULES:%=%.erl)
diff --git a/lib/common_test/test/common_test.spec b/lib/common_test/test/common_test.spec
index 8755b08117..8bec66d6f2 100644
--- a/lib/common_test/test/common_test.spec
+++ b/lib/common_test/test/common_test.spec
@@ -1 +1 @@
-{suites,"../common_test_test",all}. \ No newline at end of file
+{suites,"../common_test_test",all}.
diff --git a/lib/common_test/test/ct_auto_compile_SUITE.erl b/lib/common_test/test/ct_auto_compile_SUITE.erl
new file mode 100644
index 0000000000..cc546ed30d
--- /dev/null
+++ b/lib/common_test/test/ct_auto_compile_SUITE.erl
@@ -0,0 +1,187 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_auto_compile_SUITE
+%%%
+%%% Description:
+%%%
+%%%
+%%% The suites used for the test are located in the data directory.
+%%%-------------------------------------------------------------------
+-module(ct_auto_compile_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config1 = ct_test_support:init_per_suite(Config),
+ Config1.
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [ac_flag, ac_spec].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+ac_flag(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ file:copy(filename:join(DataDir, "bad_SUITE.erl"),
+ filename:join(PrivDir, "bad_SUITE.erl")),
+ Suite = filename:join(DataDir, "dummy_SUITE"),
+ compile:file(Suite, [{outdir,PrivDir}]),
+ {Opts,ERPid} = setup([{dir,PrivDir},
+ {auto_compile,false},
+ {label,"ac_flag"}],
+ Config),
+
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(ac_flag,
+ reformat(Events, ?eh),
+ PrivDir,
+ Opts),
+
+ TestEvents = events_to_check(ac_flag),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+ac_spec(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ file:copy(filename:join(DataDir, "bad_SUITE.erl"),
+ filename:join(PrivDir, "bad_SUITE.erl")),
+ TestSpec = [{label,ac_spec},
+ {auto_compile,false},
+ {suites,PrivDir,all}],
+ FileName = filename:join(?config(priv_dir, Config),"ac_spec.spec"),
+ {ok,Dev} = file:open(FileName, [write]),
+ [io:format(Dev, "~p.~n", [Term]) || Term <- TestSpec],
+ file:close(Dev),
+
+ {Opts,ERPid} = setup([{spec,FileName}], Config),
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(ac_spec,
+ reformat(Events, ?eh),
+ PrivDir,
+ Opts),
+
+ TestEvents = events_to_check(ac_spec),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+setup(Test, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+ %reformat(Events, _EH) ->
+ % Events.
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
+test_events(ac_flag) ->
+ [
+ {ct_test_support_eh,start_logging,{'DEF','RUNDIR'}},
+ {ct_test_support_eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {ct_test_support_eh,start_info,{1,1,3}},
+ {ct_test_support_eh,tc_start,{dummy_SUITE,init_per_suite}},
+ {ct_test_support_eh,tc_done,{dummy_SUITE,init_per_suite,ok}},
+ {ct_test_support_eh,test_stats,{1,1,{1,0}}},
+ {ct_test_support_eh,tc_start,{dummy_SUITE,end_per_suite}},
+ {ct_test_support_eh,tc_done,{dummy_SUITE,end_per_suite,ok}},
+ {ct_test_support_eh,test_done,{'DEF','STOP_TIME'}},
+ {ct_test_support_eh,stop_logging,[]}
+ ];
+
+test_events(ac_spec) ->
+ [
+ {ct_test_support_eh,start_logging,{'DEF','RUNDIR'}},
+ {ct_test_support_eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {ct_test_support_eh,start_info,{1,1,3}},
+ {ct_test_support_eh,tc_start,{dummy_SUITE,init_per_suite}},
+ {ct_test_support_eh,tc_done,{dummy_SUITE,init_per_suite,ok}},
+ {ct_test_support_eh,test_stats,{1,1,{1,0}}},
+ {ct_test_support_eh,tc_start,{dummy_SUITE,end_per_suite}},
+ {ct_test_support_eh,tc_done,{dummy_SUITE,end_per_suite,ok}},
+ {ct_test_support_eh,test_done,{'DEF','STOP_TIME'}},
+ {ct_test_support_eh,stop_logging,[]}
+ ].
diff --git a/erts/test/autoimport_SUITE_data/dummy.txt b/lib/common_test/test/ct_auto_compile_SUITE_data/bad_SUITE.erl
index 972643e527..b270c28849 100644
--- a/erts/test/autoimport_SUITE_data/dummy.txt
+++ b/lib/common_test/test/ct_auto_compile_SUITE_data/bad_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. 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
@@ -15,5 +15,9 @@
%% under the License.
%%
%% %CopyrightEnd%
-%%
-%% Purpouse: Dummy
+
+-module(bad_SUITE).
+
+-compile(export_all).
+
+bad_bad_suite
diff --git a/lib/common_test/test/ct_auto_compile_SUITE_data/dummy_SUITE.erl b/lib/common_test/test/ct_auto_compile_SUITE_data/dummy_SUITE.erl
new file mode 100644
index 0000000000..0bb2e388c7
--- /dev/null
+++ b/lib/common_test/test/ct_auto_compile_SUITE_data/dummy_SUITE.erl
@@ -0,0 +1,130 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+-module(dummy_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,30}}].
+
+%%--------------------------------------------------------------------
+%% @spec init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec init_per_group(GroupName, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1} | {fail,Reason}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec groups() -> [Group]
+%% Group = {GroupName,Properties,GroupsAndTestCases}
+%% GroupName = atom()
+%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
+%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
+%% TestCase = atom()
+%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
+%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
+%% repeat_until_any_ok | repeat_until_any_fail
+%% N = integer() | forever
+%% @end
+%%--------------------------------------------------------------------
+groups() ->
+ [].
+
+%%--------------------------------------------------------------------
+%% @spec all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [ok,fail,skip].
+
+
+ok(_Config) ->
+ ok.
+
+fail(Config) ->
+ tuple_to_list(Config),
+ ok.
+
+skip(_Config) ->
+ {skip,"should be skipped"}.
diff --git a/lib/common_test/test/ct_basic_html_SUITE.erl b/lib/common_test/test/ct_basic_html_SUITE.erl
new file mode 100644
index 0000000000..a5f2e6197e
--- /dev/null
+++ b/lib/common_test/test/ct_basic_html_SUITE.erl
@@ -0,0 +1,180 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_basic_html_SUITE
+%%%
+%%% Description:
+%%%
+%%%
+%%% The suites used for the test are located in the data directory.
+%%%-------------------------------------------------------------------
+-module(ct_basic_html_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config1 = ct_test_support:init_per_suite(Config),
+ Config1.
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [basic_flag, basic_spec].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+basic_flag(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suites = [filename:join(DataDir, "babbling_SUITE")],
+ {Opts,ERPid} = setup([{suite,Suites},
+ {basic_html,true},
+ {label,"basic_flag"}],
+ Config),
+
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(basic_flag,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(basic_flag),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+basic_spec(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ TestSpec = [{label,basic_spec},
+ {basic_html,true},
+ {suites,DataDir,babbling_SUITE}],
+ FileName = filename:join(?config(priv_dir, Config),"basic_spec.spec"),
+ {ok,Dev} = file:open(FileName, [write]),
+ [io:format(Dev, "~p.~n", [Term]) || Term <- TestSpec],
+ file:close(Dev),
+
+ {Opts,ERPid} = setup([{spec,FileName}], Config),
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(basic_spec,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(basic_spec),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+setup(Test, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+ %reformat(Events, _EH) ->
+ % Events.
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
+test_events(basic_flag) ->
+ [
+ {ct_test_support_eh,start_logging,{'DEF','RUNDIR'}},
+ {ct_test_support_eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {ct_test_support_eh,start_info,{1,1,3}},
+ {ct_test_support_eh,tc_start,{babbling_SUITE,init_per_suite}},
+ {ct_test_support_eh,tc_done,{babbling_SUITE,init_per_suite,ok}},
+ {ct_test_support_eh,test_stats,{1,1,{1,0}}},
+ {ct_test_support_eh,tc_start,{babbling_SUITE,end_per_suite}},
+ {ct_test_support_eh,tc_done,{babbling_SUITE,end_per_suite,ok}},
+ {ct_test_support_eh,test_done,{'DEF','STOP_TIME'}},
+ {ct_test_support_eh,stop_logging,[]}
+ ];
+
+test_events(basic_spec) ->
+ [
+ {ct_test_support_eh,start_logging,{'DEF','RUNDIR'}},
+ {ct_test_support_eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {ct_test_support_eh,start_info,{1,1,3}},
+ {ct_test_support_eh,tc_start,{babbling_SUITE,init_per_suite}},
+ {ct_test_support_eh,tc_done,{babbling_SUITE,init_per_suite,ok}},
+ {ct_test_support_eh,test_stats,{1,1,{1,0}}},
+ {ct_test_support_eh,tc_start,{babbling_SUITE,end_per_suite}},
+ {ct_test_support_eh,tc_done,{babbling_SUITE,end_per_suite,ok}},
+ {ct_test_support_eh,test_done,{'DEF','STOP_TIME'}},
+ {ct_test_support_eh,stop_logging,[]}
+ ].
diff --git a/lib/common_test/test/ct_basic_html_SUITE_data/babbling_SUITE.erl b/lib/common_test/test/ct_basic_html_SUITE_data/babbling_SUITE.erl
new file mode 100644
index 0000000000..7a3ccf30e0
--- /dev/null
+++ b/lib/common_test/test/ct_basic_html_SUITE_data/babbling_SUITE.erl
@@ -0,0 +1,130 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+-module(babbling_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,30}}].
+
+%%--------------------------------------------------------------------
+%% @spec init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec init_per_group(GroupName, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1} | {fail,Reason}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec groups() -> [Group]
+%% Group = {GroupName,Properties,GroupsAndTestCases}
+%% GroupName = atom()
+%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
+%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
+%% TestCase = atom()
+%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
+%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
+%% repeat_until_any_ok | repeat_until_any_fail
+%% N = integer() | forever
+%% @end
+%%--------------------------------------------------------------------
+groups() ->
+ [].
+
+%%--------------------------------------------------------------------
+%% @spec all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [ok,fail,skip].
+
+
+ok(_Config) ->
+ ok.
+
+fail(Config) ->
+ tuple_to_list(Config),
+ ok.
+
+skip(_Config) ->
+ {skip,"should be skipped"}.
diff --git a/lib/common_test/test/ct_config_SUITE.erl b/lib/common_test/test/ct_config_SUITE.erl
index 18218bee47..0b1abae757 100644
--- a/lib/common_test/test/ct_config_SUITE.erl
+++ b/lib/common_test/test/ct_config_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -88,7 +88,8 @@ require(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
run_test(config_static_SUITE,
Config,
- {config, filename:join(DataDir, "config/config.txt")},
+ [{config, filename:join(DataDir, "config/shadow.txt")},
+ {config, filename:join(DataDir, "config/config.txt")}],
["config_static_SUITE"]).
install_config(Config) when is_list(Config) ->
@@ -106,7 +107,8 @@ userconfig_static(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
run_test(config_static_SUITE,
Config,
- {userconfig, {ct_config_xml, filename:join(DataDir, "config/config.xml")}},
+ [{userconfig, {ct_config_xml, filename:join(DataDir, "config/config.xml")}},
+ {config, filename:join(DataDir, "config/shadow.txt")}],
["config_static_SUITE"]).
userconfig_dynamic(Config) when is_list(Config) ->
@@ -121,7 +123,8 @@ testspec_legacy(Config) when is_list(Config) ->
make_spec(DataDir, ConfigDir,
"spec_legacy.spec",
[config_static_SUITE],
- [{config, filename:join(DataDir, "config/config.txt")}]),
+ [{config, filename:join(DataDir, "config/shadow.txt")},
+ {config, filename:join(DataDir, "config/config.txt")}]),
run_test(config_static_SUITE,
Config,
{spec, filename:join(ConfigDir, "spec_legacy.spec")},
@@ -134,7 +137,8 @@ testspec_static(Config) when is_list(Config) ->
make_spec(DataDir, ConfigDir,
"spec_static.spec",
[config_static_SUITE],
- [{userconfig, {ct_config_xml, filename:join(DataDir, "config/config.xml")}}]),
+ [{userconfig, {ct_config_xml, filename:join(DataDir, "config/config.xml")}},
+ {config, filename:join(DataDir, "config/shadow.txt")}]),
run_test(config_static_SUITE,
Config,
{spec, filename:join(ConfigDir, "spec_static.spec")},
@@ -179,13 +183,15 @@ run_test(Name, Config, CTConfig, SuiteNames)->
ExpEvents = events_to_check(Name),
ok = ct_test_support:verify_events(ExpEvents, TestEvents, Config).
-setup_env(Test, Config, CTConfig) ->
+setup_env(Test, Config, CTConfig) when is_list(CTConfig) ->
Opts0 = ct_test_support:get_opts(Config),
Level = ?config(trace_level, Config),
EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
- Opts = Opts0 ++ [Test,{event_handler,{?eh,EvHArgs}}, CTConfig],
+ Opts = Opts0 ++ [Test,{event_handler,{?eh,EvHArgs}} | CTConfig],
ERPid = ct_test_support:start_event_receiver(Config),
- {Opts,ERPid}.
+ {Opts,ERPid};
+setup_env(Test, Config, CTConfig) ->
+ setup_env(Test, Config, [CTConfig]).
reformat_events(Events, EH) ->
ct_test_support:reformat(Events, EH).
@@ -202,40 +208,49 @@ events_to_check(_, 0) ->
events_to_check(Test, N) ->
expected_events(Test) ++ events_to_check(Test, N-1).
+-define(ok(Name,Suite,Stat),{?eh,tc_start,{Suite,Name}},
+ {?eh,tc_done,{Suite,Name,ok}},
+ {?eh,test_stats,Stat}).
+-define(nok(Name,Suite,Reason,Stat),{?eh,tc_start,{Suite,Name}},
+ {?eh,tc_done,{Suite,Name,Reason}},
+ {?eh,test_stats,Stat}).
+
+-define(sok(Name,Stat),?ok(Name,config_static_SUITE,Stat)).
+-define(snok(Name,Reason,Stat),?nok(Name,config_static_SUITE,Reason,Stat)).
+
+-define(dok(Name,Stat),?ok(Name,config_dynamic_SUITE,Stat)).
+-define(dnok(Name,Reason,Stat),?nok(Name,config_dynamic_SUITE,Reason,Stat)).
+
expected_events(config_static_SUITE)->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
- {?eh,start_info,{1,1,8}},
+ {?eh,start_info,{1,1,'_'}},
{?eh,tc_start,{config_static_SUITE,init_per_suite}},
{?eh,tc_done,{config_static_SUITE,init_per_suite,ok}},
- {?eh,tc_start,{config_static_SUITE,test_get_config_simple}},
- {?eh,tc_done,{config_static_SUITE,test_get_config_simple,ok}},
- {?eh,test_stats,{1,0,{0,0}}},
- {?eh,tc_start,{config_static_SUITE,test_get_config_nested}},
- {?eh,tc_done,{config_static_SUITE,test_get_config_nested,ok}},
- {?eh,test_stats,{2,0,{0,0}}},
- {?eh,tc_start,{config_static_SUITE,test_default_suitewide}},
- {?eh,tc_done,{config_static_SUITE,test_default_suitewide,ok}},
- {?eh,test_stats,{3,0,{0,0}}},
- {?eh,tc_start,{config_static_SUITE,test_config_name_already_in_use1}},
- {?eh,tc_done,
- {config_static_SUITE,test_config_name_already_in_use1,{skipped,{config_name_already_in_use,[x1]}}}},
- {?eh,test_stats,{3,0,{1,0}}},
- {?eh,tc_start,{config_static_SUITE,test_default_tclocal}},
- {?eh,tc_done,{config_static_SUITE,test_default_tclocal,ok}},
- {?eh,test_stats,{4,0,{1,0}}},
- {?eh,tc_start,{config_static_SUITE,test_config_name_already_in_use2}},
- {?eh,tc_done,
- {config_static_SUITE,test_config_name_already_in_use2,
- {skipped,{config_name_already_in_use,[alias,x1]}}}},
- {?eh,test_stats,{4,0,{2,0}}},
- {?eh,tc_start,{config_static_SUITE,test_alias_tclocal}},
- {?eh,tc_done,{config_static_SUITE,test_alias_tclocal,ok}},
- {?eh,test_stats,{5,0,{2,0}}},
- {?eh,tc_start,{config_static_SUITE,test_get_config_undefined}},
- {?eh,tc_done,{config_static_SUITE,test_get_config_undefined,ok}},
- {?eh,test_stats,{6,0,{2,0}}},
+ ?sok(test_get_config_simple,{1,0,{0,0}}),
+ ?sok(test_get_config_nested,{2,0,{0,0}}),
+ ?sok(test_get_config_deep_nested,{3,0,{0,0}}),
+ ?sok(test_default_suitewide,{4,0,{0,0}}),
+ ?snok(test_config_name_already_in_use1,
+ {skipped,{config_name_already_in_use,[x1]}},{4,0,{1,0}}),
+ ?sok(test_default_tclocal,{5,0,{1,0}}),
+ ?snok(test_config_name_already_in_use2,
+ {skipped,{config_name_already_in_use,[alias,x1]}},{5,0,{2,0}}),
+ ?sok(test_alias_tclocal,{6,0,{2,0}}),
+ ?sok(test_get_config_undefined,{7,0,{2,0}}),
+ ?sok(test_require_subvals,{8,0,{2,0}}),
+ ?snok(test_require_subvals2,
+ {skipped,{require_failed,
+ {not_available,{gen_cfg,[a,b,c,d]}}}},{8,0,{2,1}}),
+ ?sok(test_require_deep_config,{9,0,{2,1}}),
+ ?sok(test_shadow_all,{10,0,{2,1}}),
+ ?sok(test_element,{11,0,{2,1}}),
+ ?sok(test_shadow_all_element,{12,0,{2,1}}),
+ ?sok(test_internal_deep,{13,0,{2,1}}),
+ ?sok(test_alias_tclocal_nested,{14,0,{2,1}}),
+ ?sok(test_alias_tclocal_nested_backward_compat,{15,0,{2,1}}),
+ ?sok(test_alias_tclocal_nested_backward_compat_subvals,{16,0,{2,1}}),
{?eh,tc_start,{config_static_SUITE,end_per_suite}},
{?eh,tc_done,{config_static_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
@@ -246,29 +261,14 @@ expected_events(config_dynamic_SUITE)->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
- {?eh,start_info,{1,1,5}},
+ {?eh,start_info,{1,1,'_'}},
{?eh,tc_start,{config_dynamic_SUITE,init_per_suite}},
{?eh,tc_done,{config_dynamic_SUITE,init_per_suite,ok}},
- {?eh,tc_start,{config_dynamic_SUITE,test_get_known_variable}},
- {?eh,tc_done,
- {config_dynamic_SUITE,test_get_known_variable,ok}},
- {?eh,test_stats,{1,0,{0,0}}},
- {?eh,tc_start,{config_dynamic_SUITE,test_localtime_update}},
- {?eh,tc_done,{config_dynamic_SUITE,test_localtime_update,ok}},
- {?eh,test_stats,{2,0,{0,0}}},
- {?eh,tc_start,{config_dynamic_SUITE,test_server_pid}},
- {?eh,tc_done,{config_dynamic_SUITE,test_server_pid,ok}},
- {?eh,test_stats,{3,0,{0,0}}},
- {?eh,tc_start,
- {config_dynamic_SUITE,test_disappearable_variable}},
- {?eh,tc_done,
- {config_dynamic_SUITE,test_disappearable_variable,ok}},
- {?eh,test_stats,{4,0,{0,0}}},
- {?eh,tc_start,
- {config_dynamic_SUITE,test_disappearable_variable_alias}},
- {?eh,tc_done,
- {config_dynamic_SUITE,test_disappearable_variable_alias,ok}},
- {?eh,test_stats,{5,0,{0,0}}},
+ ?dok(test_get_known_variable,{1,0,{0,0}}),
+ ?dok(test_localtime_update,{2,0,{0,0}}),
+ ?dok(test_server_pid,{3,0,{0,0}}),
+ ?dok(test_disappearable_variable,{4,0,{0,0}}),
+ ?dok(test_disappearable_variable_alias,{5,0,{0,0}}),
{?eh,tc_start,{config_dynamic_SUITE,end_per_suite}},
{?eh,tc_done,{config_dynamic_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
diff --git a/lib/common_test/test/ct_config_SUITE_data/config/config.txt b/lib/common_test/test/ct_config_SUITE_data/config/config.txt
index fcbffcd7f3..e4bcc5ba6b 100644
--- a/lib/common_test/test/ct_config_SUITE_data/config/config.txt
+++ b/lib/common_test/test/ct_config_SUITE_data/config/config.txt
@@ -2,7 +2,8 @@
{gen_cfg,
[
{a,a_value},
- {b,b_value}
+ {b,b_value},
+ {c,[{d,d_value}]}
]}.
{gen_cfg2,
[
diff --git a/lib/common_test/test/ct_config_SUITE_data/config/config.xml b/lib/common_test/test/ct_config_SUITE_data/config/config.xml
index 0a3e5f2e31..8eeff1482f 100644
--- a/lib/common_test/test/ct_config_SUITE_data/config/config.xml
+++ b/lib/common_test/test/ct_config_SUITE_data/config/config.xml
@@ -3,6 +3,7 @@
<gen_cfg>
<a>a_value</a>
<b>b_value</b>
+ <c><d>d_value</d></c>
</gen_cfg>
<gen_cfg2>
<c>"Hello, world!"</c>
diff --git a/lib/common_test/test/ct_config_SUITE_data/config/shadow.txt b/lib/common_test/test/ct_config_SUITE_data/config/shadow.txt
new file mode 100644
index 0000000000..865bf9255a
--- /dev/null
+++ b/lib/common_test/test/ct_config_SUITE_data/config/shadow.txt
@@ -0,0 +1,12 @@
+{x, suite}.
+{gen_cfg3,
+ [
+ {l,
+ [
+ {m,
+ [
+ {n, "n"},
+ {o, 'o'}
+ ]}
+ ]}
+ ]}.
diff --git a/lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl b/lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl
index 8751a2e8f3..2e1ad651e8 100644
--- a/lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl
+++ b/lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -46,7 +46,7 @@ suite() ->
{require, gen_cfg3},
{require, alias, gen_cfg},
%% x1 default value
- {x1, {x,suite}}
+ {default_config, x1, {x,suite}}
].
init_per_suite(Config) ->
@@ -55,14 +55,24 @@ init_per_suite(Config) ->
end_per_suite(_) ->
ok.
-all() -> [test_get_config_simple, test_get_config_nested, test_default_suitewide,
+all() -> [test_get_config_simple, test_get_config_nested,
+ test_get_config_deep_nested, test_default_suitewide,
test_config_name_already_in_use1, test_default_tclocal,
test_config_name_already_in_use2, test_alias_tclocal,
- test_get_config_undefined].
-
-init_per_testcase(_, Config) ->
+ test_get_config_undefined,
+ test_require_subvals,test_require_subvals2,test_require_deep_config,
+ test_shadow_all,test_element,test_shadow_all_element,
+ test_internal_deep, test_alias_tclocal_nested,
+ test_alias_tclocal_nested_backward_compat,
+ test_alias_tclocal_nested_backward_compat_subvals
+].
+
+init_per_testcase(_,Config) ->
Config.
+end_per_testcase(test_alias_tclocal_nested_backward_compat, _) ->
+ os:putenv("COMMON_TEST_ALIAS_TOP",""),
+ ok;
end_per_testcase(_, _) ->
ok.
@@ -76,6 +86,11 @@ test_get_config_nested(_)->
a_value = ct:get_config({gen_cfg, a}),
ok.
+%% test getting a deep nested value
+test_get_config_deep_nested(_)->
+ d_value = ct:get_config({gen_cfg, c, d}),
+ ok.
+
%% test suite-wide default value
test_default_suitewide(_)->
suite = ct:get_config(x1),
@@ -112,12 +127,73 @@ test_config_name_already_in_use2(_) ->
%% test aliases
test_alias_tclocal() ->
[{require,newalias,gen_cfg}].
-test_alias_tclocal(_) ->
- A = [{a,a_value},{b,b_value}] = ct:get_config(newalias),
+test_alias_tclocal(C) when is_list(C) ->
+ test_alias_tclocal(newalias);
+test_alias_tclocal(Alias) when is_atom(Alias) ->
+ A = [{a,a_value},{b,b_value},{c,[{d,d_value}]}] = ct:get_config(Alias),
A = ct:get_config(gen_cfg),
+ B = b_value = ct:get_config({Alias,b}),
+ B = ct:get_config({gen_cfg,b}),
+ ok.
+
+%% test nested aliases
+test_alias_tclocal_nested() ->
+ [{require,newalias2,{gen_cfg,c}}].
+test_alias_tclocal_nested(_) ->
+ A = [{d,d_value}] = ct:get_config(newalias2),
+ A = ct:get_config({gen_cfg,c}),
+ B = d_value = ct:get_config({newalias2,d}),
+ B = ct:get_config({gen_cfg,c,d}),
ok.
+%% test nested aliases backward compat option
+test_alias_tclocal_nested_backward_compat() ->
+ os:putenv("COMMON_TEST_ALIAS_TOP","true"),
+ [{require,newalias3,{gen_cfg,c}}].
+test_alias_tclocal_nested_backward_compat(_) ->
+ test_alias_tclocal(newalias3).
+
+%% test nested aliases backward compat option
+test_alias_tclocal_nested_backward_compat_subvals() ->
+ [{require,newalias4,{gen_cfg,[c]}}].
+test_alias_tclocal_nested_backward_compat_subvals(_) ->
+ test_alias_tclocal(newalias4).
+
%% test for getting undefined variables
test_get_config_undefined(_) ->
undefined = ct:get_config(y1),
ok.
+
+test_require_subvals() ->
+ [{require, {gen_cfg,[a,b,c]}}].
+test_require_subvals(_) ->
+ ok.
+
+test_require_subvals2() ->
+ [{require, {gen_cfg,[a,b,c,d]}}].
+test_require_subvals2(_) ->
+ ct:fail("Test should've been skipped, you shouldn't see this!"),
+ ok.
+
+test_require_deep_config() ->
+ [{require, {gen_cfg3, m, n}}].
+test_require_deep_config(_) ->
+ ok.
+
+
+test_shadow_all(_) ->
+ ["n","N"] = ct:get_config({gen_cfg3,l, m, n}, [], [all]).
+
+test_element(_) ->
+ {{gen_cfg3,l, m, n},"n"} = ct:get_config({gen_cfg3,l, m, n}, [], [element]).
+
+test_shadow_all_element(_) ->
+ [{{gen_cfg3,l, m, n},"n"},{{gen_cfg3,l, m, n},"N"}] =
+ ct:get_config({gen_cfg3,l, m, n}, [], [all,element]).
+
+%% The tests below are needed to verify that things like ct:telnet can use
+%% nested configs
+test_internal_deep(_) ->
+ "n" = ct:get_config({{gen_cfg3,l,m},n}),
+ a_value = ct:get_config({{gen_cfg},a}),
+ undefined = ct:get_config({{gen_cfg3,l,m},p}).
diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl
index c9ee47e01b..338e76264e 100644
--- a/lib/common_test/test/ct_error_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE.erl
@@ -878,11 +878,11 @@ test_events(timetrap_fun) ->
{failed,{timetrap_timeout,{'$approx',1000}}}}},
{?eh,test_stats,{0,5,{0,0}}},
{?eh,tc_start,{timetrap_5_SUITE,tc1}},
- {?eh,tc_done,{undefined,undefined,{user_timetrap_error,
+ {?eh,tc_done,{timetrap_5_SUITE,tc1,{user_timetrap_error,
{kaboom,'_'}}}},
{?eh,test_stats,{0,6,{0,0}}},
{?eh,tc_start,{timetrap_5_SUITE,tc2}},
- {?eh,tc_done,{undefined,undefined,{user_timetrap_error,
+ {?eh,tc_done,{timetrap_5_SUITE,tc2,{user_timetrap_error,
{kaboom,'_'}}}},
{?eh,test_stats,{0,7,{0,0}}},
{?eh,tc_start,{timetrap_5_SUITE,tc3}},
@@ -937,7 +937,7 @@ test_events(timetrap_fun) ->
{?eh,tc_done,{timetrap_5_SUITE,end_per_suite,ok}},
{?eh,tc_start,{timetrap_6_SUITE,init_per_suite}},
- {?eh,tc_done,{undefined,undefined,{user_timetrap_error,
+ {?eh,tc_done,{timetrap_6_SUITE,init_per_suite,{user_timetrap_error,
{kaboom,'_'}}}},
{?eh,tc_auto_skip,{timetrap_6_SUITE,tc0,
{failed,{timetrap_6_SUITE,init_per_suite,
diff --git a/lib/common_test/test/ct_misc_1_SUITE.erl b/lib/common_test/test/ct_misc_1_SUITE.erl
index cb17af9ab5..9ff4e6a65f 100644
--- a/lib/common_test/test/ct_misc_1_SUITE.erl
+++ b/lib/common_test/test/ct_misc_1_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -106,7 +106,7 @@ beam_me_up(Config) when is_list(Config) ->
{Opts,ERPid} = setup([{suite,Suites},{auto_compile,false}], Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ {_Ok,_Fail,_Skip} = ct_test_support:run(ct, run_test, [Opts], Config),
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(beam_me_up,
diff --git a/lib/common_test/test/ct_netconfc_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE.erl
new file mode 100644
index 0000000000..e6e8d5b09c
--- /dev/null
+++ b/lib/common_test/test/ct_netconfc_SUITE.erl
@@ -0,0 +1,124 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_netconfc_SUITE
+%%%
+%%% Description:
+%%% Test ct_netconfc module
+%%%
+%%%-------------------------------------------------------------------
+-module(ct_netconfc_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config1 = ct_test_support:init_per_suite(Config),
+ Config1.
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ default
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+default(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "netconfc1_SUITE"),
+ CfgFile = filename:join(DataDir, "netconfc1.cfg"),
+ {Opts,ERPid} = setup([{suite,Suite},{config,CfgFile},
+ {label,default}], Config),
+
+ ok = execute(default, Opts, ERPid, Config).
+
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+setup(Test, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+execute(Name, Opts, ERPid, Config) ->
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(Name,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(Name,Config),
+ ct_test_support:verify_events(TestEvents, Events, Config).
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(Test,Config) ->
+ {module,_} = code:load_abs(filename:join(?config(data_dir,Config),
+ netconfc1_SUITE)),
+ TCs = netconfc1_SUITE:all(),
+ code:purge(netconfc1_SUITE),
+ code:delete(netconfc1_SUITE),
+
+ OneTest =
+ [{?eh,start_logging,{'DEF','RUNDIR'}}] ++
+ [{?eh,tc_done,{netconfc1_SUITE,TC,ok}} || TC <- TCs] ++
+ [{?eh,stop_logging,[]}],
+
+ %% 2 tests (ct:run_test + script_start) is default
+ OneTest ++ OneTest.
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg
new file mode 100644
index 0000000000..b431301df6
--- /dev/null
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg
@@ -0,0 +1,6 @@
+%% -*- erlang -*-
+{netconf1,[{ssh,"127.0.0.1"},
+ {port,2060},
+ {user,"xxx"},
+ {password,"xxx"}]}.
+{ct_conn_log,[{ct_netconfc,[{log_type,pretty}]}]}. %overrides args to cth_conn_log
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
new file mode 100644
index 0000000000..d337158bce
--- /dev/null
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
@@ -0,0 +1,1132 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%----------------------------------------------------------------------
+%% File: ct_netconfc_SUITE.erl
+%%
+%% Description:
+%% This file contains the test cases for the ct_netconfc API.
+%%
+%% @author Support
+%% @doc Netconf Client Interface.
+%% @end
+%%----------------------------------------------------------------------
+%%----------------------------------------------------------------------
+-module(netconfc1_SUITE).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/src/ct_netconfc.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+-compile(export_all).
+
+%% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:minutes(1)).
+
+-define(NS,ns).
+-define(LOCALHOST, "127.0.0.1").
+-define(SSH_PORT, 2060).
+
+-define(DEFAULT_SSH_OPTS,[{ssh,?LOCALHOST},
+ {port,?SSH_PORT},
+ {user,"xxx"},
+ {password,"xxx"}]).
+-define(DEFAULT_SSH_OPTS(Dir), ?DEFAULT_SSH_OPTS++[{user_dir,Dir}]).
+
+-define(ok,ok).
+
+suite() ->
+ [{ct_hooks, [{cth_conn_log,
+ [{ct_netconfc,[{log_type,html}, %will be overwritten by config
+ {hosts,[my_named_connection,netconf1]}]
+ }]
+ }]
+ }].
+
+all() ->
+ case os:find_executable("ssh") of
+ false ->
+ {skip, "SSH not installed on host"};
+ _ ->
+ [hello,
+ hello_from_server_first,
+ hello_named,
+ hello_configured,
+ hello_configured_extraopts,
+ hello_required,
+ hello_required_exists,
+ hello_global_pwd,
+ hello_no_session_id,
+ hello_incomp_base_vsn,
+ hello_no_base_cap,
+ hello_no_caps,
+ no_server_hello,
+ no_client_hello,
+ get_session_id,
+ get_capabilities,
+ faulty_user,
+ faulty_passwd,
+ faulty_port,
+ no_host,
+ no_port,
+ invalid_opt,
+ get,
+ get_xpath,
+ get_config,
+ get_config_xpath,
+ edit_config,
+ copy_config,
+ delete_config,
+ lock,
+ unlock,
+ kill_session,
+ get_no_such_client,
+ action,
+ send_any_rpc,
+ send_any,
+ hide_password,
+ not_proper_xml,
+ prefixed_namespace,
+ receive_chunked_data,
+ timeout_receive_chunked_data,
+ close_while_waiting_for_chunked_data,
+ connection_crash,
+ get_event_streams,
+ create_subscription,
+ receive_event
+ ]
+ end.
+
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+init_per_testcase(_Case, Config) ->
+ ets:delete_all_objects(ns_tab),
+ Dog = test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+init_per_suite(Config) ->
+ case catch {crypto:start(), ssh:start()} of
+ {ok, ok} ->
+ {ok, _} = get_id_keys(Config),
+ make_dsa_files(Config),
+ Server = ?NS:start(?config(data_dir,Config)),
+ [{server,Server}|Config];
+ _ ->
+ {skip, "Crypto and/or SSH could not be started!"}
+ end.
+
+end_per_suite(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ ?NS:stop(?config(server,Config)),
+ ssh:stop(),
+ crypto:stop(),
+ remove_id_keys(PrivDir),
+ Config.
+
+hello(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+hello_from_server_first(Config) ->
+ DataDir = ?config(data_dir,Config),
+ ?NS:hello(1),
+ {ok,Client} = ct_netconfc:only_open(?DEFAULT_SSH_OPTS(DataDir)),
+ ct:sleep(500),
+ ?NS:expect(hello),
+ ?ok = ct_netconfc:hello(Client),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+hello_named(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(any_name,DataDir),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+hello_configured() ->
+ [{require, netconf1}].
+hello_configured(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_configured_success(netconf1,DataDir),
+ ?NS:expect_do_reply('close-session',close,ok),
+ {error, {no_such_name,netconf1}} = ct_netconfc:close_session(netconf1),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+hello_configured_extraopts() ->
+ [{require, netconf1}].
+hello_configured_extraopts(Config) ->
+ DataDir = ?config(data_dir,Config),
+ %% Test that the cofiguration overwrites the ExtraOpts parameter
+ %% to ct_netconfc:open/2.
+ {ok,Client} = open_configured_success(netconf1,DataDir,[{password,"faulty"}]),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+hello_required() ->
+ [{require, my_named_connection, netconf1}].
+hello_required(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,_Client} = open_configured_success(my_named_connection,DataDir),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(my_named_connection),
+ ok.
+
+hello_required_exists() ->
+ [{require, my_named_connection, netconf1}].
+hello_required_exists(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,_Client1} = open_configured_success(my_named_connection,DataDir),
+
+ %% Check that same name can not be used twice
+ {error,{connection_exists,_Client1}} =
+ ct_netconfc:open(my_named_connection,[{user_dir,DataDir}]),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(my_named_connection),
+ timer:sleep(500),
+
+ %% Then check that it can be used again after the first is closed
+ {ok,_Client2} = open_configured_success(my_named_connection,DataDir),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(my_named_connection),
+ ok.
+
+hello_global_pwd(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir,[{user,"any-user"},
+ {password,"global-xxx"}]),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+hello_no_session_id(Config) ->
+ DataDir = ?config(data_dir,Config),
+ ?NS:hello(no_session_id),
+ ?NS:expect(no_session_id,hello),
+ {error,{incorrect_hello,no_session_id_found}} = open(DataDir),
+ ok.
+
+hello_incomp_base_vsn(Config) ->
+ DataDir = ?config(data_dir,Config),
+ ?NS:hello(1,{base,"1.1"}),
+ ?NS:expect(hello),
+ {error,{incompatible_base_capability_vsn,"1.1"}} = open(DataDir),
+ ok.
+
+hello_no_base_cap(Config) ->
+ DataDir = ?config(data_dir,Config),
+ ?NS:hello(1,no_base),
+ ?NS:expect(hello),
+ {error,{incorrect_hello,no_base_capability_found}} = open(DataDir),
+ ok.
+
+hello_no_caps(Config) ->
+ DataDir = ?config(data_dir,Config),
+ ?NS:hello(1,no_caps),
+ ?NS:expect(hello),
+ {error,{incorrect_hello,capabilities_not_found}} = open(DataDir),
+ ok.
+
+no_server_hello(Config) ->
+ DataDir = ?config(data_dir,Config),
+ ?NS:expect(undefined,hello),
+ {error,{hello_session_failed,timeout}} = open(DataDir,[{timeout,2000}]),
+ ok.
+
+no_client_hello(Config) ->
+ DataDir = ?config(data_dir,Config),
+ ?NS:hello(1),
+ {ok,Client} = ct_netconfc:only_open(?DEFAULT_SSH_OPTS(DataDir)),
+
+ %% Allow server hello to arrive
+ ct:sleep(500),
+
+ %% Tell server to receive a get request and then die without
+ %% replying since no hello has been received. (is this correct
+ %% behavoiur??)
+ ?NS:expect_do(get,close),
+ {error,closed} = ct_netconfc:get(Client,whatever),
+ ok.
+
+get_session_id(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ 1 = ct_netconfc:get_session_id(Client),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+get_capabilities(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ Caps = ct_netconfc:get_capabilities(Client),
+ BaseCap = ?NETCONF_BASE_CAP ++ ?NETCONF_BASE_CAP_VSN,
+ [BaseCap,"urn:ietf:params:netconf:capability:writable-running:1.0" |_] = Caps,
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+faulty_user(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {error,{ssh,could_not_connect_to_server,
+ "Unable to connect using the available authentication methods"}} =
+ open(DataDir,[{user,"yyy"}]),
+ ok.
+
+faulty_passwd(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {error,{ssh,could_not_connect_to_server,
+ "Unable to connect using the available authentication methods"}} =
+ open(DataDir,[{password,"yyy"}]),
+ ok.
+
+faulty_port(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {error,{ssh,could_not_connect_to_server,econnrefused}} =
+ open(DataDir,[{port,2062}]),
+ ok.
+
+no_host(Config) ->
+ DataDir = ?config(data_dir,Config),
+ Opts = lists:keydelete(ssh,1,?DEFAULT_SSH_OPTS(DataDir)),
+ {error,no_host_address} = ct_netconfc:open(Opts),
+ ok.
+
+no_port(Config) ->
+ DataDir = ?config(data_dir,Config),
+ Opts = lists:keydelete(port,1,?DEFAULT_SSH_OPTS(DataDir)),
+ {error,no_port} = ct_netconfc:open(Opts),
+ ok.
+
+invalid_opt(Config) ->
+ DataDir = ?config(data_dir,Config),
+ Opts1 = ?DEFAULT_SSH_OPTS(DataDir) ++ [{timeout,invalidvalue}],
+ {error,{invalid_option,{timeout,invalidvalue}}} = ct_netconfc:open(Opts1),
+ Opts2 = ?DEFAULT_SSH_OPTS(DataDir) ++ [{some_other_opt,true}],
+ {error,{invalid_option,{some_other_opt,true}}} = ct_netconfc:open(Opts2),
+ ok.
+
+get(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}],
+ ?NS:expect_reply('get',{data,Data}),
+ {ok,Data} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+get_xpath(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}],
+ ?NS:expect_reply({'get',xpath},{data,Data}),
+ {ok,Data} = ct_netconfc:get(Client,{xpath,"/server"}),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+get_config(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}],
+ ?NS:expect_reply('get-config',{data,Data}),
+ {ok,Data} = ct_netconfc:get_config(Client,running,
+ {server,[{xmlns,"myns"}],[]}),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+get_config_xpath(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}],
+ ?NS:expect_reply({'get-config',xpath},{data,Data}),
+ {ok,Data} = ct_netconfc:get_config(Client,running,{xpath,"/server"}),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+edit_config(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ ?NS:expect_reply('edit-config',ok),
+ ?ok = ct_netconfc:edit_config(Client,running,
+ {server,[{xmlns,"myns"}],
+ [{name,["myserver"]}]}),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+copy_config(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ ?NS:expect_reply('copy-config',ok),
+ ?ok = ct_netconfc:copy_config(Client,startup,running),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+delete_config(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ ?NS:expect_reply('delete-config',ok),
+ ?ok = ct_netconfc:delete_config(Client,startup),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+lock(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ ?NS:expect_reply('lock',ok),
+ ?ok = ct_netconfc:lock(Client,running),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+unlock(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ ?NS:expect_reply('unlock',ok),
+ ?ok = ct_netconfc:unlock(Client,running),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+kill_session(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ ?NS:hello(2),
+ ?NS:expect(2,hello),
+ {ok,_OtherClient} = open(DataDir),
+
+ ?NS:expect_do_reply('kill-session',{kill,2},ok),
+ ?ok = ct_netconfc:kill_session(Client,2),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+
+ ok.
+
+get_no_such_client(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ case ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}) of
+ {error,no_such_client} ->
+ ok;
+ {error,closed} ->
+ %% Means that the Client process was not terminated before the call.
+ %% Give it one more go.
+ {error,no_such_client} =
+ ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]})
+ end,
+ ok.
+
+action(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ Data = [{myactionreturn,[{xmlns,"myns"}],["value"]}],
+ ?NS:expect_reply(action,{data,Data}),
+ {ok,Data} = ct_netconfc:action(Client,{myaction,[{xmlns,"myns"}],[]}),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+send_any_rpc(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}],
+ GetConf = {'get-config',
+ [{source,["running"]},
+ {filter,[{type,"subtree"}],
+ [{server,[{xmlns,"myns"}],[]}]}]},
+ ?NS:expect_reply('get-config',{data,Data}),
+ [{data,?NETCONF_NAMESPACE_ATTR,Data}] = ct_netconfc:send_rpc(Client,GetConf),
+
+ EditConf = {'edit-config',
+ [{target,["running"]},
+ {config,[{server,[{xmlns,"myns"}],
+ [{name,["myserver"]}]}]}]},
+ ?NS:expect_reply('edit-config',ok),
+ [{ok,?NETCONF_NAMESPACE_ATTR,[]}] = ct_netconfc:send_rpc(Client,EditConf),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+send_any(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ %% Correct get-config rpc
+ Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}],
+ RpcAttr1 = ?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"1"}],
+ RpcGetConf = {rpc,RpcAttr1,
+ [{'get-config',
+ [{source,["running"]},
+ {filter,[{type,"subtree"}],
+ [{server,[{xmlns,"myns"}],[]}]}]}]},
+ ?NS:expect_reply('get-config',{data,Data}),
+ {'rpc-reply',RpcAttr1,[{data,_,Data}]} = ct_netconfc:send(Client,RpcGetConf),
+
+ %% Correct edit-config rpc
+ RpcAttr2 = ?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"2"}],
+ RpcEditConf = {rpc,RpcAttr2,
+ [{'edit-config',
+ [{target,["running"]},
+ {config,[{server,[{xmlns,"myns"}],
+ [{name,["myserver"]}]}]}]}]},
+ ?NS:expect_reply('edit-config',ok),
+ {'rpc-reply',RpcAttr2,[{ok,_,[]}]} = ct_netconfc:send(Client,RpcEditConf),
+
+ %% Send any data
+ ?NS:expect_reply(any,{ok,[],[]}),
+ {ok,_,[]} = ct_netconfc:send(Client,{any,[],[]}),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+hide_password(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ Password = "my_very_secret_password",
+ Data = [{passwords,[{xmlns,"myns"}],
+ [{password,[{xmlns,"pwdns"}],[Password]},
+ {password,[],[Password]}]}],
+ ?NS:expect_reply('get',{data,Data}),
+ ct:capture_start(), % in case of html logging
+ {ok,Data} = ct_netconfc:get(Client,{passwords,[{xmlns,"myns"}],[]}),
+ ct:capture_stop(),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+
+ Log = filename:join(?config(priv_dir,Config),"hide_password-netconf.txt"),
+
+ Text =
+ case file:read_file(Log) of
+ {ok,Bin} ->
+ Bin;
+ _NoLog ->
+ %% Assume html logging
+ list_to_binary(ct:capture_get())
+ end,
+
+ nomatch = binary:match(Text,list_to_binary(Password)),
+
+ ok.
+
+not_proper_xml(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ NS = list_to_binary(?NETCONF_NAMESPACE),
+ NotProper = <<"<rpc-reply message-id=\"1\" xmlns=\"",
+ NS/binary,"\"><data></rpc-reply>">>,
+ ?NS:expect_reply('get',NotProper),
+ {error,{failed_to_parse_received_data,_}} =
+ ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+prefixed_namespace(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ NS = list_to_binary(?NETCONF_NAMESPACE),
+
+ %% Test that data element can be properly decoded and that
+ %% prefixed namespace attributes (exepct the netconf namespace)
+ %% are forwarded to the content of the data element - i.e. that
+ %% the xmlns:my is forwarded from the rpc-reply element to the
+ %% server element below.
+ Data = <<"<nc:rpc-reply message-id=\"1\" xmlns:nc=\"",
+ NS/binary,"\" xmlns:my=\"myns\"><nc:data><my:server>",
+ "<my:name my:lang=\"en\">myserver</my:name></my:server>"
+ "</nc:data></nc:rpc-reply>">>,
+ ?NS:expect_reply('get',Data),
+ {ok,[{'my:server',[{'xmlns:my',"myns"}],
+ [{'my:name',[{'my:lang',"en"}],["myserver"]}]}]} =
+ ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}),
+
+ Ok = <<"<nc:rpc-reply message-id=\"2\" xmlns:nc=\"",
+ NS/binary,"\"><nc:ok/></nc:rpc-reply>">>,
+ ?NS:expect_reply('edit-config',Ok),
+ ?ok = ct_netconfc:edit_config(Client,running,
+ {server,[{xmlns,"myns"}],
+ [{name,["myserver"]}]}),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+%% Test that the client can parse data which is received in chunks,
+%% i.e. when the complete rpc-reply is not contained in one single ssh
+%% data message.
+receive_chunked_data(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ %% Construct the data to return from netconf server
+ Data = [{servers,[{xmlns,"myns"}],
+ [{server,[],[{name,[],["server0"]}]},
+ {server,[],[{name,[],["server1"]}]},
+ {server,[],[{name,[],["server2"]}]},
+ {server,[],[{name,[],["server3"]}]},
+ {server,[],[{name,[],["server4"]}]},
+ {server,[],[{name,[],["server5"]}]},
+ {server,[],[{name,[],["server6"]}]},
+ {server,[],[{name,[],["server7"]}]},
+ {server,[],[{name,[],["server8"]}]},
+ {server,[],[{name,[],["server9"]}]}]
+ }],
+ Rpc = {'rpc-reply',?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"1"}],
+ [{data,Data}]},
+ Xml = list_to_binary(xmerl:export_simple_element(Rpc,xmerl_xml)),
+ Netconf =
+ <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
+ Xml/binary,"\n",?END_TAG/binary>>,
+
+ %% Split the data in some chunks
+ PartLength = size(Netconf) div 3,
+ <<Part1:PartLength/binary,Part2:PartLength/binary,Part3:PartLength/binary,
+ Part4/binary>> = Netconf,
+
+ %% Spawn a process which will wait a bit for the client to send
+ %% the request (below), then order the server to the chunks of the
+ %% rpc-reply one by one.
+ spawn(fun() -> timer:sleep(500),?NS:hupp(send,Part1),
+ timer:sleep(100),?NS:hupp(send,Part2),
+ timer:sleep(100),?NS:hupp(send,Part3),
+ timer:sleep(100),?NS:hupp(send,Part4)
+ end),
+
+ %% Order server to expect a get - then the process above will make
+ %% sure the rpc-reply is sent.
+ ?NS:expect('get'),
+ {ok,Data} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+%% Same as receive_chunked_data, but timeout waiting for last part.
+timeout_receive_chunked_data(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ %% Construct the data to return from netconf server
+ Data = [{servers,[{xmlns,"myns"}],
+ [{server,[],[{name,[],["server0"]}]},
+ {server,[],[{name,[],["server1"]}]},
+ {server,[],[{name,[],["server2"]}]},
+ {server,[],[{name,[],["server3"]}]},
+ {server,[],[{name,[],["server4"]}]},
+ {server,[],[{name,[],["server5"]}]},
+ {server,[],[{name,[],["server6"]}]},
+ {server,[],[{name,[],["server7"]}]},
+ {server,[],[{name,[],["server8"]}]},
+ {server,[],[{name,[],["server9"]}]}]
+ }],
+ Rpc = {'rpc-reply',?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"1"}],
+ [{data,Data}]},
+ Xml = list_to_binary(xmerl:export_simple_element(Rpc,xmerl_xml)),
+ Netconf =
+ <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
+ Xml/binary,"\n",?END_TAG/binary>>,
+
+ %% Split the data in some chunks
+ PartLength = size(Netconf) div 3,
+ <<Part1:PartLength/binary,Part2:PartLength/binary,_Part3:PartLength/binary,
+ _Part4/binary>> = Netconf,
+
+ %% Spawn a process which will wait a bit for the client to send
+ %% the request (below), then order the server to the chunks of the
+ %% rpc-reply one by one.
+ spawn(fun() -> timer:sleep(500),?NS:hupp(send,Part1),
+ timer:sleep(100),?NS:hupp(send,Part2)
+ end),
+
+ %% Order server to expect a get - then the process above will make
+ %% sure the rpc-reply is sent - but only a part of it - then timeout.
+ ?NS:expect('get'),
+ {error,timeout} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]},2000),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+%% Same as receive_chunked_data, but timeout waiting for last part.
+close_while_waiting_for_chunked_data(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ %% Construct the data to return from netconf server
+ Data = [{servers,[{xmlns,"myns"}],
+ [{server,[],[{name,[],["server0"]}]},
+ {server,[],[{name,[],["server1"]}]},
+ {server,[],[{name,[],["server2"]}]},
+ {server,[],[{name,[],["server3"]}]},
+ {server,[],[{name,[],["server4"]}]},
+ {server,[],[{name,[],["server5"]}]},
+ {server,[],[{name,[],["server6"]}]},
+ {server,[],[{name,[],["server7"]}]},
+ {server,[],[{name,[],["server8"]}]},
+ {server,[],[{name,[],["server9"]}]}]
+ }],
+ Rpc = {'rpc-reply',?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"1"}],
+ [{data,Data}]},
+ Xml = list_to_binary(xmerl:export_simple_element(Rpc,xmerl_xml)),
+ Netconf =
+ <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
+ Xml/binary,"\n",?END_TAG/binary>>,
+
+ %% Split the data in some chunks
+ PartLength = size(Netconf) div 3,
+ <<Part1:PartLength/binary,Part2:PartLength/binary,_Part3:PartLength/binary,
+ _Part4/binary>> = Netconf,
+
+ %% Spawn a process which will wait a bit for the client to send
+ %% the request (below), then order the server to the chunks of the
+ %% rpc-reply one by one.
+ spawn(fun() -> timer:sleep(500),?NS:hupp(send,Part1),
+ timer:sleep(100),?NS:hupp(send,Part2),
+ timer:sleep(100),?NS:hupp(kill)
+ end),
+
+ %% Order server to expect a get - then the process above will make
+ %% sure the rpc-reply is sent - but only a part of it - then close.
+ ?NS:expect('get'),
+ {error,closed} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]},2000),
+ ok.
+
+connection_crash(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+
+ %% Test that if the test survives killing the connection
+ %% process. Earlier this caused ct_util_server to terminate, and
+ %% this aborting the complete test run.
+ spawn(fun() -> timer:sleep(500),exit(Client,kill) end),
+ ?NS:expect(get),
+ {error,{closed,killed}}=ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}),
+ ok.
+
+get_event_streams(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ StreamNames = ["NETCONF","stream1","stream2"],
+ Streams = [{N,[{description,"descr of " ++ N}]} || N <- StreamNames],
+ StreamsXml = [{stream,[{name,[N]}|[{Tag,[Value]} || {Tag,Value} <- Data]]}
+ || {N,Data} <- Streams],
+ ReplyData = [{netconf,?NETMOD_NOTIF_NAMESPACE_ATTR,[{streams,StreamsXml}]}],
+ ?NS:expect_reply('get',{data,ReplyData}),
+ {ok,Streams} = ct_netconfc:get_event_streams(Client,StreamNames),
+
+ ?NS:expect_reply('get',{data,ReplyData}),
+ {ok,Streams} = ct_netconfc:get_event_streams(Client,StreamNames,5000),
+
+ ?NS:expect('get'),
+ {error,timeout} = ct_netconfc:get_event_streams(Client,100),
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+ ok.
+
+create_subscription(Config) ->
+ DataDir = ?config(data_dir,Config),
+
+ %% All defaults
+ {ok,Client1} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream]},ok),
+ ?ok = ct_netconfc:create_subscription(Client1),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client1),
+
+ %% All defaults with timeout
+ {ok,Client1a} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream]},ok),
+ ?ok = ct_netconfc:create_subscription(Client1a,5000),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client1a),
+
+ %% All defaults timing out
+ {ok,Client1b} = open_success(DataDir),
+ ?NS:expect({'create-subscription',[stream]}),
+ {error,timeout} = ct_netconfc:create_subscription(Client1b,100),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client1b),
+
+ %% Stream
+ {ok,Client2} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream]},ok),
+ Stream = "some_stream",
+ ?ok = ct_netconfc:create_subscription(Client2,Stream),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client2),
+
+ %% Filter
+ {ok,Client3} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream,filter]},ok),
+ Filter = {notification,?NETMOD_NOTIF_NAMESPACE_ATTR,
+ [eventTime]},
+ ?ok = ct_netconfc:create_subscription(Client3,Filter),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client3),
+
+ %% Filter with timeout
+ {ok,Client3a} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream,filter]},ok),
+ ?ok = ct_netconfc:create_subscription(Client3a,Filter,5000),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client3a),
+
+ %% Filter timing out
+ {ok,Client3b} = open_success(DataDir),
+ ?NS:expect({'create-subscription',[stream,filter]}),
+ {error,timeout}=ct_netconfc:create_subscription(Client3b,Filter,100),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client3b),
+
+ %% Stream and filter
+ {ok,Client4} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream,filter]},ok),
+ ?ok = ct_netconfc:create_subscription(Client4,Stream,Filter),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client4),
+
+ %% Start/stop time
+ {ok,Client5} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream,startTime,stopTime]},ok),
+ StartTime = xs_datetime({D,{H,M,S}}= calendar:local_time()),
+ StopTime = xs_datetime({D,{H+2,M,S}}),
+ ?ok = ct_netconfc:create_subscription(Client5,StartTime,StopTime),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client5),
+
+ %% Start/stop time with timeout
+ {ok,Client5a} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream,startTime,stopTime]},ok),
+ ?ok = ct_netconfc:create_subscription(Client5a,StartTime,StopTime,5000),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client5a),
+
+ %% Start/stop time timing out
+ {ok,Client5b} = open_success(DataDir),
+ ?NS:expect({'create-subscription',[stream,startTime,stopTime]}),
+ {error,timeout} =
+ ct_netconfc:create_subscription(Client5b,StartTime,StopTime,100),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client5b),
+
+ %% Stream and start/stop time
+ {ok,Client6} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream,startTime,stopTime]},ok),
+ ?ok = ct_netconfc:create_subscription(Client6,Stream,StartTime,StopTime),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client6),
+
+ %% Filter and start/stop time
+ {ok,Client7} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream,filter,startTime,stopTime]},
+ ok),
+ ?ok = ct_netconfc:create_subscription(Client7,Filter,
+ StartTime,StopTime),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client7),
+
+ %% Stream, filter and start/stop time
+ {ok,Client8} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream,filter,startTime,stopTime]},
+ ok),
+ ?ok = ct_netconfc:create_subscription(Client8,Stream,Filter,
+ StartTime,StopTime),
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client8),
+
+ ok.
+
+receive_event(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(DataDir),
+ ?NS:expect_reply({'create-subscription',[stream]},ok),
+ ?ok = ct_netconfc:create_subscription(Client),
+
+ ?NS:hupp(send_event),
+
+ receive
+ %% Matching ?NS:make_msg(event)
+ {notification,?NETCONF_NOTIF_NAMESPACE_ATTR,
+ [{eventTime,[],[_Time]},
+ {event,[{xmlns,"http://my.namespaces.com/event"}],
+ [{severity,_,_},
+ {description,_,_}]}]} ->
+ ok;
+ Other ->
+ ct:fail({got_unexpected_while_waiting_for_event, Other})
+ after 3000 ->
+ ct:fail(timeout_waiting_for_event)
+ end,
+
+ ?NS:expect_do_reply('close-session',close,ok),
+ ?ok = ct_netconfc:close_session(Client),
+
+ ok.
+
+%%%-----------------------------------------------------------------
+
+break(_Config) ->
+ test_server:break("break test case").
+
+br() ->
+ test_server:break("").
+
+%%%-----------------------------------------------------------------
+%% Open a netconf session which is not specified in a config file
+open_success(Dir) ->
+ open_success(Dir,[]).
+
+%% Open a netconf session which is not specified in a config file, and
+%% give som extra options in addition to the test defaults.
+open_success(Dir,ExtraOpts) when is_list(Dir), is_list(ExtraOpts) ->
+ ?NS:hello(1), % tell server to send hello with session id 1
+ ?NS:expect(hello), % tell server to expect a hello message from client
+ open(Dir,ExtraOpts);
+
+%% Open a named netconf session which is not specified in a config file
+open_success(KeyOrName,Dir) when is_atom(KeyOrName), is_list(Dir) ->
+ ?NS:hello(1),
+ ?NS:expect(hello),
+ ct_netconfc:open(KeyOrName,?DEFAULT_SSH_OPTS(Dir)).
+
+open(Dir) ->
+ open(Dir,[]).
+open(Dir,ExtraOpts) ->
+ Opts = lists:ukeymerge(1,lists:keysort(1,ExtraOpts),
+ lists:keysort(1,?DEFAULT_SSH_OPTS(Dir))),
+ ct_netconfc:open(Opts).
+
+%%%-----------------------------------------------------------------
+%%% Open a netconf session which is specified in a config file
+%%% KeyOrName is the config key (server_id()) or name given in a
+%%% require statement (target_name()).
+open_configured_success(KeyOrName,Dir) when is_atom(KeyOrName) ->
+ open_configured_success(KeyOrName,Dir,[]).
+open_configured_success(KeyOrName,Dir,ExtraOpts) when is_atom(KeyOrName) ->
+ ?NS:hello(1),
+ ?NS:expect(hello),
+ ct_netconfc:open(KeyOrName,[{user_dir,Dir}|ExtraOpts]).
+
+%%%-----------------------------------------------------------------
+%%% Convert erlang datetime to the simplest variant of XML dateTime
+xs_datetime({{Y,M,D},{H,Mi,S}}) ->
+ lists:flatten(
+ io_lib:format("~p-~s-~sT~s:~s:~s",[Y,pad(M),pad(D),pad(H),pad(Mi),pad(S)])).
+
+pad(I) when I<10 ->
+ "0"++integer_to_list(I);
+pad(I) ->
+ integer_to_list(I).
+
+
+%%%-----------------------------------------------------------------
+%%% BEGIN SSH key management
+%% copy private keys to given dir from ~/.ssh
+get_id_keys(Config) ->
+ DstDir = ?config(priv_dir, Config),
+ SrcDir = filename:join(os:getenv("HOME"), ".ssh"),
+ RsaOk = copyfile(SrcDir, DstDir, "id_rsa"),
+ DsaOk = copyfile(SrcDir, DstDir, "id_dsa"),
+ case {RsaOk, DsaOk} of
+ {{ok, _}, {ok, _}} -> {ok, both};
+ {{ok, _}, _} -> {ok, rsa};
+ {_, {ok, _}} -> {ok, dsa};
+ {Error, _} -> Error
+ end.
+
+%% Remove later on. Use make_dsa_files instead.
+remove_id_keys(Config) ->
+ Dir = ?config(priv_dir, Config),
+ file:delete(filename:join(Dir, "id_rsa")),
+ file:delete(filename:join(Dir, "id_dsa")).
+
+
+make_dsa_files(Config) ->
+ make_dsa_files(Config, rfc4716_public_key).
+make_dsa_files(Config, Type) ->
+ {DSA, EncodedKey} = gen_dsa(128, 20),
+ PKey = DSA#'DSAPrivateKey'.y,
+ P = DSA#'DSAPrivateKey'.p,
+ Q = DSA#'DSAPrivateKey'.q,
+ G = DSA#'DSAPrivateKey'.g,
+ Dss = #'Dss-Parms'{p=P, q=Q, g=G},
+ {ok, Hostname} = inet:gethostname(),
+ {ok, {A, B, C, D}} = inet:getaddr(Hostname, inet),
+ IP = lists:concat([A, ".", B, ".", C, ".", D]),
+ Attributes = [], % Could be [{comment,"user@" ++ Hostname}],
+ HostNames = [{hostnames,[IP, IP]}],
+ PublicKey = [{{PKey, Dss}, Attributes}],
+ KnownHosts = [{{PKey, Dss}, HostNames}],
+
+ KnownHostsEnc = public_key:ssh_encode(KnownHosts, known_hosts),
+ KnownHosts = public_key:ssh_decode(KnownHostsEnc, known_hosts),
+
+ PublicKeyEnc = public_key:ssh_encode(PublicKey, Type),
+
+ SystemTmpDir = ?config(data_dir, Config),
+ filelib:ensure_dir(SystemTmpDir),
+ file:make_dir(SystemTmpDir),
+
+ DSAFile = filename:join(SystemTmpDir, "ssh_host_dsa_key.pub"),
+ file:delete(DSAFile),
+
+ DSAPrivateFile = filename:join(SystemTmpDir, "ssh_host_dsa_key"),
+ file:delete(DSAPrivateFile),
+
+ KHFile = filename:join(SystemTmpDir, "known_hosts"),
+ file:delete(KHFile),
+
+ PemBin = public_key:pem_encode([EncodedKey]),
+
+ file:write_file(DSAFile, PublicKeyEnc),
+ file:write_file(KHFile, KnownHostsEnc),
+ file:write_file(DSAPrivateFile, PemBin),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Creates a dsa key (OBS: for testing only)
+%% the sizes are in bytes
+%% gen_dsa(::integer()) -> {::atom(), ::binary(), ::opaque()}
+%%--------------------------------------------------------------------
+gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) ->
+ Key = gen_dsa2(LSize, NSize),
+ {Key, encode_key(Key)}.
+
+encode_key(Key = #'RSAPrivateKey'{}) ->
+ {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key),
+ {'RSAPrivateKey', list_to_binary(Der), not_encrypted};
+encode_key(Key = #'DSAPrivateKey'{}) ->
+ {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key),
+ {'DSAPrivateKey', list_to_binary(Der), not_encrypted}.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% DSA key generation (OBS: for testing only)
+%% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm
+%% and the fips_186-3.pdf
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+gen_dsa2(LSize, NSize) ->
+ Q = prime(NSize), %% Choose N-bit prime Q
+ X0 = prime(LSize),
+ P0 = prime((LSize div 2) +1),
+
+ %% Choose L-bit prime modulus P such that p-1 is a multiple of q.
+ case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of
+ error ->
+ gen_dsa2(LSize, NSize);
+ P ->
+ G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q.
+ %% such that This may be done by setting g = h^(p-1)/q mod p, commonly h=2 is used.
+
+ X = prime(20), %% Choose x by some random method, where 0 < x < q.
+ Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p.
+
+ #'DSAPrivateKey'{version=0, p=P, q=Q, g=G, y=Y, x=X}
+ end.
+
+%% See fips_186-3.pdf
+dsa_search(T, P0, Q, Iter) when Iter > 0 ->
+ P = 2*T*Q*P0 + 1,
+ case is_prime(crypto:mpint(P), 50) of
+ true -> P;
+ false -> dsa_search(T+1, P0, Q, Iter-1)
+ end;
+dsa_search(_,_,_,_) ->
+ error.
+
+
+%%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+prime(ByteSize) ->
+ Rand = odd_rand(ByteSize),
+ crypto:erlint(prime_odd(Rand, 0)).
+
+prime_odd(Rand, N) ->
+ case is_prime(Rand, 50) of
+ true ->
+ Rand;
+ false ->
+ NotPrime = crypto:erlint(Rand),
+ prime_odd(crypto:mpint(NotPrime+2), N+1)
+ end.
+
+%% see http://en.wikipedia.org/wiki/Fermat_primality_test
+is_prime(_, 0) -> true;
+is_prime(Candidate, Test) ->
+ CoPrime = odd_rand(<<0,0,0,4, 10000:32>>, Candidate),
+ case crypto:mod_exp(CoPrime, Candidate, Candidate) of
+ CoPrime -> is_prime(Candidate, Test-1);
+ _ -> false
+ end.
+
+odd_rand(Size) ->
+ Min = 1 bsl (Size*8-1),
+ Max = (1 bsl (Size*8))-1,
+ odd_rand(crypto:mpint(Min), crypto:mpint(Max)).
+
+odd_rand(Min,Max) ->
+ Rand = <<Sz:32, _/binary>> = crypto:rand_uniform(Min,Max),
+ BitSkip = (Sz+4)*8-1,
+ case Rand of
+ Odd = <<_:BitSkip, 1:1>> -> Odd;
+ Even = <<_:BitSkip, 0:1>> ->
+ crypto:mpint(crypto:erlint(Even)+1)
+ end.
+
+copyfile(SrcDir, DstDir, Fn) ->
+ file:copy(filename:join(SrcDir, Fn),
+ filename:join(DstDir, Fn)).
+
+%%% END SSH key management
+%%%-----------------------------------------------------------------
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
new file mode 100644
index 0000000000..2427f37f52
--- /dev/null
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
@@ -0,0 +1,555 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%----------------------------------------------------------------------
+%% A netconf server used for testing of netconfc
+-module(ns).
+
+%-compile(export_all).
+-include_lib("common_test/src/ct_netconfc.hrl").
+
+
+%%%-----------------------------------------------------------------
+%%% API
+-export([start/1,
+ stop/1,
+ hello/1,
+ hello/2,
+ expect/1,
+ expect/2,
+ expect_reply/2,
+ expect_reply/3,
+ expect_do/2,
+ expect_do/3,
+ expect_do_reply/3,
+ expect_do_reply/4,
+ hupp/1,
+ hupp/2]).
+
+%%%-----------------------------------------------------------------
+%%% ssh_channel callbacks
+-export([init/1,
+ terminate/2,
+ handle_ssh_msg/2,
+ handle_msg/2]).
+
+%%%-----------------------------------------------------------------
+%% Server specifications
+-define(SERVER_DATA_NAMESPACE, "ClientTest").
+-define(CAPABILITIES,?CAPABILITIES_VSN("1.0")).
+-define(CAPABILITIES_VSN(Vsn),
+ [
+ ?NETCONF_BASE_CAP ++ Vsn,
+ "urn:ietf:params:netconf:capability:writable-running:1.0",
+ "urn:ietf:params:netconf:capability:candidate:1.0",
+ "urn:ietf:params:netconf:capability:confirmed-commit:1.0",
+ "urn:ietf:params:netconf:capability:rollback-on-error:1.0",
+ "urn:ietf:params:netconf:capability:startup:1.0",
+ "urn:ietf:params:netconf:capability:url:1.0",
+ "urn:ietf:params:netconf:capability:xpath:1.0",
+ "urn:ietf:params:netconf:capability:notification:1.0",
+ "urn:ietf:params:netconf:capability:interleave:1.0",
+ ?ACTION_NAMESPACE,
+ ?SERVER_DATA_NAMESPACE
+ ]).
+-define(SSH_PORT, 2060).
+-define(ssh_config(Dir),[{port, ?SSH_PORT},
+ {interface, {127,0,0,1}},
+ {system_dir, Dir},
+ {user_dir, Dir},
+ {user_passwords, [{"xxx","xxx"}]},
+ {password, "global-xxx"}]).
+
+%% Some help for debugging
+%-define(dbg(F,A),io:format(F,A)).
+-define(dbg(F,A),ok).
+-define(dbg_event(Event,Expect),
+ ?dbg("Event: ~p~nExpected: ~p~n",[Event,Expect])).
+
+%% State
+-record(session, {cb,
+ connection,
+ buffer = <<>>,
+ session_id}).
+
+
+%%%-----------------------------------------------------------------
+%%% API
+
+%% Start the netconf server and use the given directory as system_dir
+%% and user_dir
+start(Dir) ->
+ spawn(fun() -> init_server(Dir) end).
+
+%% Stop the netconf server
+stop(Pid) ->
+ Pid ! {stop,self()},
+ receive stopped -> ok end.
+
+%% Set the session id for the hello message.
+%% If this is not called prior to starting the session, no hello
+%% message will be sent.
+%% 'Stuff' indicates some special handling to e.g. provoke error cases
+hello(SessionId) ->
+ hello(SessionId,undefined).
+hello(SessionId,Stuff) ->
+ insert(hello,{SessionId,Stuff}).
+
+%% Tell server to expect the given message without doing any further
+%% actions. To be called directly before sending a request.
+expect(Expect) ->
+ expect_do_reply(Expect,undefined,undefined).
+expect(SessionId,Expect) ->
+ expect_do_reply(SessionId,Expect,undefined,undefined).
+
+%% Tell server to expect the given message and reply with the give
+%% reply. To be called directly before sending a request.
+expect_reply(Expect,Reply) ->
+ expect_do_reply(Expect,undefined,Reply).
+expect_reply(SessionId,Expect,Reply) ->
+ expect_do_reply(SessionId,Expect,undefined,Reply).
+
+%% Tell server to expect the given message and perform an action. To
+%% be called directly before sending a request.
+expect_do(Expect,Do) ->
+ expect_do_reply(Expect,Do,undefined).
+expect_do(SessionId,Expect,Do) ->
+ expect_do_reply(SessionId,Expect,Do,undefined).
+
+%% Tell server to expect the given message, perform an action and
+%% reply with the given reply. To be called directly before sending a
+%% request.
+expect_do_reply(Expect,Do,Reply) ->
+ add_expect(1,{Expect,Do,Reply}).
+expect_do_reply(SessionId,Expect,Do,Reply) ->
+ add_expect(SessionId,{Expect,Do,Reply}).
+
+%% Hupp the server - i.e. tell it to do something -
+%% e.g. hupp(send_event) will cause send_event(State) to be called on
+%% the session channel process.
+hupp(send_event) ->
+ hupp(send,[make_msg(event)]);
+hupp(kill) ->
+ hupp(1,fun hupp_kill/1,[]).
+
+hupp(send,Data) ->
+ hupp(1,fun hupp_send/2,[Data]).
+
+hupp(SessionId,Fun,Args) when is_function(Fun) ->
+ [{_,Pid}] = lookup({channel_process,SessionId}),
+ Pid ! {hupp,Fun,Args}.
+
+%%%-----------------------------------------------------------------
+%%% Main loop of the netconf server
+init_server(Dir) ->
+ register(main_ns_proc,self()),
+ ets:new(ns_tab,[set,named_table,public]),
+ Config = ?ssh_config(Dir),
+ {_,Host} = lists:keyfind(interface, 1, Config),
+ {_,Port} = lists:keyfind(port, 1, Config),
+ Opts = lists:filter(fun({Key,_}) ->
+ lists:member(Key,[system_dir,
+ password,
+ user_passwords,
+ pwdfun])
+ end,
+ Config),
+ {ok, Daemon} =
+ ssh:daemon(Host, Port,
+ [{subsystems,[{"netconf",{?MODULE,[]}}]}
+ |Opts]),
+ loop(Daemon).
+
+loop(Daemon) ->
+ receive
+ {stop,From} ->
+ ssh:stop_daemon(Daemon),
+ From ! stopped;
+ {table_trans,Fun,Args,From} ->
+ %% Simple transaction mechanism for ets table
+ R = apply(Fun,Args),
+ From ! {table_trans_done,R},
+ loop(Daemon)
+ end.
+
+%%----------------------------------------------------------------------
+%% Behaviour callback functions (ssh_channel)
+%%----------------------------------------------------------------------
+init([]) ->
+ {ok, undefined}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+handle_ssh_msg({ssh_cm,CM,{data, Ch, _Type = 0, Data}}, State) ->
+ %% io:format("~p~n",[{self(),Data,CM,Ch,State}]),
+ data_for_channel(CM, Ch, Data, State);
+handle_ssh_msg({ssh_cm,CM,{closed, Ch}}, State) ->
+ %% erlang:display({self(),closed,CM,Ch,State}),
+ stop_channel(CM, Ch, State);
+handle_ssh_msg({ssh_cm,CM,{eof, Ch}}, State) ->
+ %% erlang:display({self(),eof,CM,Ch,State}),
+ data_for_channel(CM,Ch, <<>>, State).
+
+
+handle_msg({'EXIT', _Pid, _Reason}, State) ->
+ {ok, State};
+handle_msg({ssh_channel_up,Ch,CM},undefined) ->
+ %% erlang:display({self(),up,CM,Ch}),
+ ConnRef = {CM,Ch},
+ SessionId = maybe_hello(ConnRef),
+ insert({channel_process,SessionId},self()), % used to hupp the server
+ {ok, #session{connection = ConnRef,
+ session_id = SessionId}};
+handle_msg({hupp,Fun,Args},State) ->
+ {ok,apply(Fun,Args ++ [State])}.
+
+data_for_channel(CM, Ch, Data, State) ->
+ try data(Data, State) of
+ {ok, NewState} ->
+ case erase(stop) of
+ true ->
+ stop_channel(CM, Ch, NewState);
+ _ ->
+ {ok, NewState}
+ end
+ catch
+ Class:Reason ->
+ Stacktrace = erlang:get_stacktrace(),
+ error_logger:error_report([{?MODULE, data_for_channel},
+ {request, Data},
+ {buffer, State#session.buffer},
+ {reason, {Class, Reason}},
+ {stacktrace, Stacktrace}]),
+ stop_channel(CM, Ch, State)
+ end.
+
+data(Data, State = #session{connection = ConnRef,
+ buffer = Buffer,
+ session_id = SessionId}) ->
+ AllData = <<Buffer/binary,Data/binary>>,
+ case find_endtag(AllData) of
+ {ok,Msgs,Rest} ->
+ [check_expected(SessionId,ConnRef,Msg) || Msg <- Msgs],
+ {ok,State#session{buffer=Rest}};
+ need_more ->
+ {ok,State#session{buffer=AllData}}
+ end.
+
+stop_channel(CM, Ch, State) ->
+ ssh:close(CM),
+ {stop, Ch, State}.
+
+
+%%%-----------------------------------------------------------------
+%%% Functions to trigg via hupp/1:
+
+%% Send data spontaneously - e.g. an event
+hupp_send(Data,State = #session{connection = ConnRef}) ->
+ send(ConnRef,Data),
+ State.
+hupp_kill(State = #session{connection = ConnRef}) ->
+ kill(ConnRef),
+ State.
+
+%%%-----------------------------------------------------------------
+%%% Internal functions
+
+
+%%% Send ssh data to the client
+send({CM,Ch},Data) ->
+ ssh_connection:send(CM, Ch, Data).
+
+%%% Kill ssh connection
+kill({CM,_Ch}) ->
+ ssh:close(CM).
+
+add_expect(SessionId,Add) ->
+ table_trans(fun do_add_expect/2,[SessionId,Add]).
+
+table_trans(Fun,Args) ->
+ S = self(),
+ case whereis(main_ns_proc) of
+ S ->
+ apply(Fun,Args);
+ Pid ->
+ Pid ! {table_trans,Fun,Args,self()},
+ receive
+ {table_trans_done,Result} ->
+ Result
+ after 5000 ->
+ exit(table_trans_timeout)
+ end
+ end.
+
+do_add_expect(SessionId,Add) ->
+ case lookup({expect,SessionId}) of
+ [] ->
+ insert({expect,SessionId},[Add]);
+ [{_,First}] ->
+ insert({expect,SessionId},First ++ [Add])
+ end,
+ ok.
+
+do_get_expect(SessionId) ->
+ case lookup({expect,SessionId}) of
+ [{_,[{Expect,Do,Reply}|Rest]}] ->
+ insert({expect,SessionId},Rest),
+ {Expect,Do,Reply};
+ _ ->
+ error
+ end.
+
+insert(Key,Value) ->
+ ets:insert(ns_tab,{Key,Value}).
+lookup(Key) ->
+ ets:lookup(ns_tab,Key).
+
+maybe_hello(ConnRef) ->
+ case lookup(hello) of
+ [{hello,{SessionId,Stuff}}] ->
+ %% erlang:display({SessionId,Stuff}),
+ ets:delete(ns_tab,hello),
+ insert({session,SessionId},ConnRef),
+ reply(ConnRef,{hello,SessionId,Stuff}),
+ SessionId;
+ [] ->
+ undefined
+ end.
+
+find_endtag(Data) ->
+ case binary:split(Data,[?END_TAG],[global]) of
+ [Data] ->
+ need_more;
+ Msgs ->
+ {ok,lists:sublist(Msgs,length(Msgs)-1),lists:last(Msgs)}
+ end.
+
+check_expected(SessionId,ConnRef,Msg) ->
+ %% io:format("~p~n",[{check_expected,SessionId,Msg}]),
+ case table_trans(fun do_get_expect/1,[SessionId]) of
+ {Expect,Do,Reply} ->
+ %% erlang:display({got,io_lib:format("~s",[Msg])}),
+ %% erlang:display({expected,Expect}),
+ match(Msg,Expect),
+ do(ConnRef, Do),
+ reply(ConnRef,Reply);
+ error ->
+ timer:sleep(1000),
+ exit({error,{got_unexpected,SessionId,Msg,ets:tab2list(ns_tab)}})
+ end.
+
+match(Msg,Expect) ->
+ ?dbg("Match: ~p~n",[Msg]),
+ {ok,ok,<<>>} = xmerl_sax_parser:stream(Msg,[{event_fun,fun event/3},
+ {event_state,Expect}]).
+
+event(Event,_Loc,Expect) ->
+ ?dbg_event(Event,Expect),
+ event(Event,Expect).
+
+event(startDocument,Expect) -> match(Expect);
+event({startElement,_,Name,_,Attrs},[{se,Name}|Match]) ->
+ msg_id(Name,Attrs),
+ Match;
+event({startElement,_,Name,_,Attrs},[ignore,{se,Name}|Match]) ->
+ msg_id(Name,Attrs),
+ Match;
+event({startElement,_,Name,_,Attrs},[{se,Name,As}|Match]) ->
+ msg_id(Name,Attrs),
+ match_attrs(Name,As,Attrs),
+ Match;
+event({startElement,_,Name,_,Attrs},[ignore,{se,Name,As}|Match]) ->
+ msg_id(Name,Attrs),
+ match_attrs(Name,As,Attrs),
+ Match;
+event({startPrefixMapping,_,Ns},[{ns,Ns}|Match]) -> Match;
+event({startPrefixMapping,_,Ns},[ignore,{ns,Ns}|Match]) -> Match;
+event({endPrefixMapping,_},Match) -> Match;
+event({endElement,_,Name,_},[{ee,Name}|Match]) -> Match;
+event({endElement,_,Name,_},[ignore,{ee,Name}|Match]) -> Match;
+event(endDocument,Match) when Match==[]; Match==[ignore] -> ok;
+event(_,[ignore|_]=Match) -> Match;
+event(Event,Match) -> throw({nomatch,{Event,Match}}).
+
+msg_id("rpc",Attrs) ->
+ case lists:keyfind("message-id",3,Attrs) of
+ {_,_,_,Str} -> put(msg_id,Str);
+ false -> erase(msg_id)
+ end;
+msg_id(_,_) ->
+ ok.
+
+match_attrs(Name,[{Key,Value}|As],Attrs) ->
+ case lists:keyfind(atom_to_list(Key),3,Attrs) of
+ {_,_,_,Value} -> match_attrs(Name,As,Attrs);
+ false -> throw({missing_attr,Key,Name,Attrs});
+ _ -> throw({faulty_attr_value,Key,Name,Attrs})
+ end;
+match_attrs(_,[],_) ->
+ ok.
+
+do(ConnRef, close) ->
+ ets:match_delete(ns_tab,{{session,'_'},ConnRef}),
+ put(stop,true);
+do(_ConnRef, {kill,SessionId}) ->
+ case lookup({session,SessionId}) of
+ [{_,Owner}] ->
+ ets:delete(ns_tab,{session,SessionId}),
+ kill(Owner);
+ _ ->
+ exit({no_session_to_kill,SessionId})
+ end;
+do(_, undefined) ->
+ ok.
+
+reply(_,undefined) ->
+ ?dbg("no reply~n",[]),
+ ok;
+reply(ConnRef,Reply) ->
+ ?dbg("Reply: ~p~n",[Reply]),
+ send(ConnRef, make_msg(Reply)).
+
+from_simple(Simple) ->
+ list_to_binary(xmerl:export_simple_element(Simple,xmerl_xml)).
+
+xml(Content) ->
+ <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
+ Content/binary,"\n",?END_TAG/binary>>.
+
+rpc_reply(Content) when is_binary(Content) ->
+ MsgId = case erase(msg_id) of
+ undefined -> <<>>;
+ Id -> list_to_binary([" message-id=\"",Id,"\""])
+ end,
+ <<"<rpc-reply xmlns=\"",?NETCONF_NAMESPACE,"\"",MsgId/binary,">\n",
+ Content/binary,"\n</rpc-reply>">>;
+rpc_reply(Content) ->
+ rpc_reply(list_to_binary(Content)).
+
+session_id(no_session_id) ->
+ <<>>;
+session_id(SessionId0) ->
+ SessionId = list_to_binary(integer_to_list(SessionId0)),
+ <<"<session-id>",SessionId/binary,"</session-id>\n">>.
+
+capabilities(undefined) ->
+ CapsXml = list_to_binary([["<capability>",C,"</capability>\n"]
+ || C <- ?CAPABILITIES]),
+ <<"<capabilities>\n",CapsXml/binary,"</capabilities>\n">>;
+capabilities({base,Vsn}) ->
+ CapsXml = list_to_binary([["<capability>",C,"</capability>\n"]
+ || C <- ?CAPABILITIES_VSN(Vsn)]),
+ <<"<capabilities>\n",CapsXml/binary,"</capabilities>\n">>;
+capabilities(no_base) ->
+ [_|Caps] = ?CAPABILITIES,
+ CapsXml = list_to_binary([["<capability>",C,"</capability>\n"] || C <- Caps]),
+ <<"<capabilities>\n",CapsXml/binary,"</capabilities>\n">>;
+capabilities(no_caps) ->
+ <<>>.
+
+%%%-----------------------------------------------------------------
+%%% Match received netconf message from the client. Add a new clause
+%%% for each new message to recognize. The clause argument shall match
+%%% the Expect argument in expect/1, expect_reply/2 or
+%%% expect_do_reply/3.
+%%%
+%%% match(term()) -> [Match].
+%%% Match = ignore | {se,Name} | {se,Name,Attrs} | {ee,Name} | {ns,Namespace}
+%%% Name = string()
+%%% Attrs = [{atom(),string()}]
+%%% Namespace = string()
+%%%
+%%% 'se' means start element, 'ee' means end element - i.e. to match
+%%% an XML element you need one 'se' entry and one 'ee' entry with the
+%%% same name in the match list.
+match(hello) ->
+ [ignore,{se,"hello"},ignore,{ee,"hello"},ignore];
+match('close-session') ->
+ [ignore,{se,"rpc"},{se,"close-session"},
+ {ee,"close-session"},{ee,"rpc"},ignore];
+match('edit-config') ->
+ [ignore,{se,"rpc"},{se,"edit-config"},{se,"target"},ignore,{ee,"target"},
+ {se,"config"},ignore,{ee,"config"},{ee,"edit-config"},{ee,"rpc"},ignore];
+match('get') ->
+ match({get,subtree});
+match({'get',FilterType}) ->
+ [ignore,{se,"rpc"},{se,"get"},{se,"filter",[{type,atom_to_list(FilterType)}]},
+ ignore,{ee,"filter"},{ee,"get"},{ee,"rpc"},ignore];
+match('get-config') ->
+ match({'get-config',subtree});
+match({'get-config',FilterType}) ->
+ [ignore,{se,"rpc"},{se,"get-config"},{se,"source"},ignore,{ee,"source"},
+ {se,"filter",[{type,atom_to_list(FilterType)}]},ignore,{ee,"filter"},
+ {ee,"get-config"},{ee,"rpc"},ignore];
+match('copy-config') ->
+ [ignore,{se,"rpc"},{se,"copy-config"},{se,"target"},ignore,{ee,"target"},
+ {se,"source"},ignore,{ee,"source"},{ee,"copy-config"},{ee,"rpc"},ignore];
+match('delete-config') ->
+ [ignore,{se,"rpc"},{se,"delete-config"},{se,"target"},ignore,{ee,"target"},
+ {ee,"delete-config"},{ee,"rpc"},ignore];
+match('lock') ->
+ [ignore,{se,"rpc"},{se,"lock"},{se,"target"},ignore,{ee,"target"},
+ {ee,"lock"},{ee,"rpc"},ignore];
+match('unlock') ->
+ [ignore,{se,"rpc"},{se,"unlock"},{se,"target"},ignore,{ee,"target"},
+ {ee,"unlock"},{ee,"rpc"},ignore];
+match('kill-session') ->
+ [ignore,{se,"rpc"},{se,"kill-session"},{se,"session-id"},ignore,
+ {ee,"session-id"},{ee,"kill-session"},{ee,"rpc"},ignore];
+match(action) ->
+ [ignore,{se,"rpc"},{ns,?ACTION_NAMESPACE},{se,"action"},{se,"data"},ignore,
+ {ee,"data"},{ee,"action"},{ee,"rpc"},ignore];
+match({'create-subscription',Content}) ->
+ [ignore,{se,"rpc"},{ns,?NETCONF_NOTIF_NAMESPACE},
+ {se,"create-subscription"}] ++
+ lists:flatmap(fun(X) ->
+ [{se,atom_to_list(X)},ignore,{ee,atom_to_list(X)}]
+ end, Content) ++
+ [{ee,"create-subscription"},{ee,"rpc"},ignore];
+match(any) ->
+ [ignore].
+
+
+
+%%%-----------------------------------------------------------------
+%%% Make message to send to the client.
+%%% Add a new clause for each new message that shall be sent. The
+%%% clause shall match the Reply argument in expect_reply/2 or
+%%% expect_do_reply/3.
+make_msg({hello,SessionId,Stuff}) ->
+ SessionIdXml = session_id(SessionId),
+ CapsXml = capabilities(Stuff),
+ xml(<<"<hello xmlns=\"",?NETCONF_NAMESPACE,"\">\n",CapsXml/binary,
+ SessionIdXml/binary,"</hello>">>);
+make_msg(ok) ->
+ xml(rpc_reply("<ok/>"));
+make_msg({data,Data}) ->
+ xml(rpc_reply(from_simple({data,Data})));
+make_msg(event) ->
+ xml(<<"<notification xmlns=\"",?NETCONF_NOTIF_NAMESPACE,"\">"
+ "<eventTime>2012-06-14T14:50:54+02:00</eventTime>"
+ "<event xmlns=\"http://my.namespaces.com/event\">"
+ "<severity>major</severity>"
+ "<description>Something terrible happened</description>"
+ "</event>"
+ "</notification>">>);
+make_msg(Xml) when is_binary(Xml) ->
+ xml(Xml);
+make_msg(Simple) when is_tuple(Simple) ->
+ xml(from_simple(Simple)).
diff --git a/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl b/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl
index 423cb2999b..7704a29768 100644
--- a/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl
+++ b/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl
@@ -1,11 +1,21 @@
-%%%-------------------------------------------------------------------
-%%% @author Peter Andersson <[email protected]>
-%%% @copyright (C) 2012, Peter Andersson
-%%% @doc
-%%%
-%%% @end
-%%% Created : 23 Jan 2012 by Peter Andersson <[email protected]>
-%%%-------------------------------------------------------------------
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
-module(priv_dir_SUITE).
-compile(export_all).
diff --git a/lib/common_test/test/ct_shell_SUITE.erl b/lib/common_test/test/ct_shell_SUITE.erl
new file mode 100644
index 0000000000..4b8c43d800
--- /dev/null
+++ b/lib/common_test/test/ct_shell_SUITE.erl
@@ -0,0 +1,133 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_shell_SUITE
+%%%
+%%% Description:
+%%% Test that the interactive mode starts properly
+%%%
+%%% The suites used for the test are located in the data directory.
+%%%-------------------------------------------------------------------
+-module(ct_shell_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+init_per_suite(Config) ->
+ Config1 = ct_test_support:init_per_suite(Config),
+ Config1.
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [start_interactive].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+start_interactive(Config) ->
+ DataDir = ?config(data_dir, Config),
+ CfgFile = filename:join(DataDir, "cfgdata"),
+
+ {Opts,ERPid} = setup([{interactive_mode,true},{config,CfgFile}],
+ Config),
+ CTNode = proplists:get_value(ct_node, Config),
+ Level = proplists:get_value(trace_level, Config),
+ test_server:format(Level, "Saving start opts on ~p: ~p~n",
+ [CTNode, Opts]),
+ rpc:call(CTNode, application, set_env,
+ [common_test, run_test_start_opts, Opts]),
+ test_server:format(Level, "Calling ct_run:script_start() on ~p~n",
+ [CTNode]),
+
+ interactive_mode = rpc:call(CTNode, ct_run, script_start, []),
+
+ ok = rpc:call(CTNode, ct, require, [key1]),
+ value1 = rpc:call(CTNode, ct, get_config, [key1]),
+ ok = rpc:call(CTNode, ct, require, [x,key2]),
+ value2 = rpc:call(CTNode, ct, get_config, [x]),
+
+ ok = rpc:call(CTNode, ct, stop_interactive, []),
+
+ case rpc:call(CTNode, erlang, whereis, [ct_util_server]) of
+ undefined ->
+ ok;
+ _ ->
+ test_server:format(Level,
+ "ct_util_server not stopped on ~p yet, waiting 5 s...~n",
+ [CTNode]),
+ timer:sleep(5000),
+ undefined = rpc:call(CTNode, erlang, whereis, [ct_util_server])
+ end,
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(start_interactive,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+ TestEvents = test_events(start_interactive),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+setup(Test, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+
+test_events(start_interactive) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ].
diff --git a/lib/common_test/test/ct_shell_SUITE_data/cfgdata b/lib/common_test/test/ct_shell_SUITE_data/cfgdata
new file mode 100644
index 0000000000..23a40ad21a
--- /dev/null
+++ b/lib/common_test/test/ct_shell_SUITE_data/cfgdata
@@ -0,0 +1,2 @@
+{key1,value1}.
+{key2,value2}.
diff --git a/lib/common_test/test/ct_snmp_SUITE.erl b/lib/common_test/test/ct_snmp_SUITE.erl
new file mode 100644
index 0000000000..848752b816
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE.erl
@@ -0,0 +1,141 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_snmp_SUITE
+%%%
+%%% Description:
+%%% Test ct_snmp module
+%%%
+%%%-------------------------------------------------------------------
+-module(ct_snmp_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config1 = ct_test_support:init_per_suite(Config),
+ Config1.
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ default
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+default(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "snmp1_SUITE"),
+ CfgFile = filename:join(DataDir, "snmp.cfg"),
+ {Opts,ERPid} = setup([{suite,Suite},{config,CfgFile},
+ {label,default}], Config),
+
+ ok = execute(default, Opts, ERPid, Config).
+
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+setup(Test, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+execute(Name, Opts, ERPid, Config) ->
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(Name,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(Name,Config),
+ ct_test_support:verify_events(TestEvents, Events, Config).
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(_TestName,Config) ->
+ {module,_} = code:load_abs(filename:join(?config(data_dir,Config),
+ snmp1_SUITE)),
+ TCs = get_tcs(),
+ code:purge(snmp1_SUITE),
+ code:delete(snmp1_SUITE),
+
+ OneTest =
+ [{?eh,start_logging,{'DEF','RUNDIR'}}] ++
+ [{?eh,tc_done,{snmp1_SUITE,TC,ok}} || TC <- TCs] ++
+ [{?eh,stop_logging,[]}],
+
+ %% 2 tests (ct:run_test + script_start) is default
+ OneTest ++ OneTest.
+
+
+get_tcs() ->
+ All = snmp1_SUITE:all(),
+ Groups =
+ try snmp1_SUITE:groups()
+ catch error:undef -> []
+ end,
+ flatten_tcs(All,Groups).
+
+flatten_tcs([H|T],Groups) when is_atom(H) ->
+ [H|flatten_tcs(T,Groups)];
+flatten_tcs([{group,Group}|T],Groups) ->
+ TCs = proplists:get_value(Group,Groups),
+ flatten_tcs(TCs ++ T,Groups);
+flatten_tcs([],_) ->
+ [].
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp.cfg b/lib/common_test/test/ct_snmp_SUITE_data/snmp.cfg
new file mode 100644
index 0000000000..b0ac0e6a96
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp.cfg
@@ -0,0 +1,20 @@
+%% -*- erlang -*-
+{snmp, [{start_agent,true},
+ {users,[{user_name,[snmp1_SUITE,[]]}]},
+ {managed_agents,[{agent_name, [user_name, {127,0,0,1}, 4000,
+ [{engine_id,"ct_snmp-test-engine"},
+ {version,v2}]]}]},
+ {engine_id,"ct_snmp-test-engine"},
+ {agent_vsns,[v2]}
+ ]}.
+{snmp_app,[{manager, [{config, [{verbosity, silence}]},
+ {server,[{verbosity,silence}]},
+ {net_if,[{verbosity,silence}]},
+ {versions,[v2]}
+ ]},
+ {agent, [{config, [{verbosity, silence}]},
+ {net_if,[{verbosity,silence}]},
+ {mib_server,[{verbosity,silence}]},
+ {local_db,[{verbosity,silence}]},
+ {agent_verbosity,silence}
+ ]}]}.
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp1_SUITE.erl b/lib/common_test/test/ct_snmp_SUITE_data/snmp1_SUITE.erl
new file mode 100644
index 0000000000..dcc5c5378b
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp1_SUITE.erl
@@ -0,0 +1,152 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%----------------------------------------------------------------------
+%% File: ct_snmp_SUITE.erl
+%%
+%% Description:
+%% This file contains the test cases for the ct_snmp API.
+%%
+%% @author Support
+%% @doc Test of SNMP support in common_test
+%% @end
+%%----------------------------------------------------------------------
+%%----------------------------------------------------------------------
+-module(snmp1_SUITE).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("snmp/include/STANDARD-MIB.hrl").
+-include_lib("snmp/include/snmp_types.hrl").
+
+-compile(export_all).
+
+%% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:minutes(1)).
+
+%% SNMP user stuff
+-behaviour(snmpm_user).
+-export([handle_error/3,
+ handle_agent/5,
+ handle_pdu/4,
+ handle_trap/3,
+ handle_inform/3,
+ handle_report/3]).
+
+
+suite() ->
+ [{require, snmp_mgr_agent, snmp},
+ {require, snmp_app_cfg, snmp_app}].
+
+all() ->
+ [start_stop,
+ {group,get_set}].
+
+
+groups() ->
+ [{get_set,[get_values,get_next_values,set_values]}].
+
+init_per_group(get_set, Config) ->
+ ok = ct_snmp:start(Config,snmp_mgr_agent,snmp_app_cfg),
+ Config.
+
+end_per_group(get_set, Config) ->
+ ok = ct_snmp:stop(Config),
+ Config.
+
+init_per_testcase(_Case, Config) ->
+ Dog = test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ Config.
+
+break(_Config) ->
+ test_server:break(""),
+ ok.
+
+start_stop(Config) ->
+ ok = ct_snmp:start(Config,snmp_mgr_agent,snmp_app_cfg),
+ timer:sleep(1000),
+ {snmp,_,_} = lists:keyfind(snmp,1,application:which_applications()),
+ [_|_] = filelib:wildcard("*/*.conf",?config(priv_dir,Config)),
+
+ ok = ct_snmp:stop(Config),
+ timer:sleep(1000),
+ false = lists:keyfind(snmp,1,application:which_applications()),
+ [] = filelib:wildcard("*/*.conf",?config(priv_dir,Config)),
+ ok.
+
+get_values(_Config) ->
+ Oids1 = [?sysDescr_instance, ?sysName_instance],
+ {noError,_,V1} = ct_snmp:get_values(agent_name,Oids1,snmp_mgr_agent),
+ [#varbind{oid=?sysDescr_instance,value="Erlang SNMP agent"},
+ #varbind{oid=?sysName_instance,value="ct_test"}] = V1,
+ ok.
+
+get_next_values(_Config) ->
+ Oids2 = [?system],
+ {noError,_,V2} = ct_snmp:get_next_values(agent_name,Oids2,snmp_mgr_agent),
+ [#varbind{oid=?sysDescr_instance,value="Erlang SNMP agent"}] = V2,
+ ok.
+
+set_values(Config) ->
+ Oid3 = ?sysName_instance,
+ NewName = "ct_test changed by " ++ atom_to_list(?MODULE),
+ VarsAndVals = [{Oid3,s,NewName}],
+ {noError,_,_} =
+ ct_snmp:set_values(agent_name,VarsAndVals,snmp_mgr_agent,Config),
+
+ Oids4 = [?sysName_instance],
+ {noError,_,V4} = ct_snmp:get_values(agent_name,Oids4,snmp_mgr_agent),
+ [#varbind{oid=?sysName_instance,value=NewName}] = V4,
+
+ ok.
+
+
+%%%-----------------------------------------------------------------
+%%% SNMP Manager User callback
+handle_error(ReqId, Reason, UserData) ->
+ erlang:display({handle_error,ReqId, Reason, UserData}),
+ ignore.
+
+handle_agent(Addr, Port, Type, SnmpInfo, UserData) ->
+ erlang:display({handle_agent,Addr, Port, Type, SnmpInfo, UserData}),
+ ignore.
+
+handle_pdu(TargetName, ReqId, SnmpPduInfo, UserData) ->
+ erlang:display({handle_pdu,TargetName, ReqId, SnmpPduInfo, UserData}),
+ ignore.
+
+handle_trap(TargetName, SnmpTrapInfo, UserData) ->
+ erlang:display({handle_trap,TargetName, SnmpTrapInfo, UserData}),
+ ignore.
+
+handle_inform(TargetName, SnmpInformInfo, UserData) ->
+ erlang:display({handle_inform,TargetName, SnmpInformInfo, UserData}),
+ ignore.
+
+handle_report(TargetName, SnmpReportInfo, UserData) ->
+ erlang:display({handle_report,TargetName, SnmpReportInfo, UserData}),
+ ignore.
diff --git a/lib/common_test/test/ct_system_error_SUITE.erl b/lib/common_test/test/ct_system_error_SUITE.erl
new file mode 100644
index 0000000000..f00f470c33
--- /dev/null
+++ b/lib/common_test/test/ct_system_error_SUITE.erl
@@ -0,0 +1,132 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_system_error_SUITE
+%%%
+%%% Description:
+%%%
+%%% Test that severe system errors (such as failure to write logs) are
+%%% noticed and handled.
+%%%-------------------------------------------------------------------
+-module(ct_system_error_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config1 = ct_test_support:init_per_suite(Config),
+ Config1.
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ test_server_failing_logs
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+test_server_failing_logs(Config) ->
+ TC = test_server_failing_logs,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "a_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC}], Config),
+ crash_test_server(Config),
+ {error,{cannot_create_log_dir,__}} = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+ ct_test_support:log_events(TC,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(TC),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+crash_test_server(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Root = ?config(priv_dir, Config),
+ [$@|Host] = lists:dropwhile(fun(C) ->
+ C =/= $@
+ end, atom_to_list(node())),
+ Format = filename:join(Root,
+ "ct_run.ct@" ++ Host ++
+ ".~4..0w-~2..0w-~2..0w_"
+ "~2..0w.~2..0w.~2..0w"),
+ [C2,C1|_] = lists:reverse(filename:split(DataDir)),
+ LogDir = C1 ++ "." ++ C2 ++ ".a_SUITE.logs",
+ T = calendar:datetime_to_gregorian_seconds(calendar:local_time()),
+ [begin
+ {{Y,Mon,D},{H,Min,S}} =
+ calendar:gregorian_seconds_to_datetime(T+Offset),
+ Dir0 = io_lib:format(Format, [Y,Mon,D,H,Min,S]),
+ Dir = lists:flatten(Dir0),
+ file:make_dir(Dir),
+ File = filename:join(Dir, LogDir),
+ file:write_file(File, "anything goes\n")
+ end || Offset <- lists:seq(0, 20)],
+ ok.
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+setup(Test, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+
+events_to_check(_Test) ->
+ [{?eh,severe_error,{cannot_create_log_dir,{'_','_'}}}].
diff --git a/lib/common_test/test/ct_system_error_SUITE_data/a_SUITE.erl b/lib/common_test/test/ct_system_error_SUITE_data/a_SUITE.erl
new file mode 100644
index 0000000000..c6e3ddfd5d
--- /dev/null
+++ b/lib/common_test/test/ct_system_error_SUITE_data/a_SUITE.erl
@@ -0,0 +1,122 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(a_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,10}}].
+
+%%--------------------------------------------------------------------
+%% @spec init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec init_per_group(GroupName, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1} | {fail,Reason}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec groups() -> [Group]
+%% Group = {GroupName,Properties,GroupsAndTestCases}
+%% GroupName = atom()
+%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
+%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
+%% TestCase = atom()
+%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
+%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
+%% repeat_until_any_ok | repeat_until_any_fail
+%% N = integer() | forever
+%% @end
+%%--------------------------------------------------------------------
+groups() ->
+ [].
+
+%%--------------------------------------------------------------------
+%% @spec all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [tc1].
+
+tc1(_C) ->
+ ok.
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 02246b5763..80cca4a1cc 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -35,6 +35,8 @@
verify_events/3, reformat/2, log_events/4,
join_abs_dirs/2]).
+-export([ct_test_halt/1]).
+
-include_lib("kernel/include/file.hrl").
%%%-----------------------------------------------------------------
@@ -229,8 +231,9 @@ run(Opts, Config) when is_list(Opts) ->
%% use ct interface
test_server:format(Level, "~n[RUN #1] Calling ct:run_test(~p) on ~p~n",
[Opts, CTNode]),
- Result1 = rpc:call(CTNode, ct, run_test, [Opts]),
-
+ CtRunTestResult = rpc:call(CTNode, ct, run_test, [Opts]),
+ test_server:format(Level, "~n[RUN #1] Got return value ~p~n",
+ [CtRunTestResult]),
case rpc:call(CTNode, erlang, whereis, [ct_util_server]) of
undefined ->
ok;
@@ -242,17 +245,36 @@ run(Opts, Config) when is_list(Opts) ->
undefined = rpc:call(CTNode, erlang, whereis, [ct_util_server])
end,
%% use run_test interface (simulated)
- test_server:format(Level, "Saving start opts on ~p: ~p~n", [CTNode,Opts]),
- rpc:call(CTNode, application, set_env, [common_test, run_test_start_opts, Opts]),
- test_server:format(Level, "[RUN #2] Calling ct_run:script_start() on ~p~n", [CTNode]),
- Result2 = rpc:call(CTNode, ct_run, script_start, []),
- case {Result1,Result2} of
- {ok,ok} ->
+ Opts1 = [{halt_with,{?MODULE,ct_test_halt}} | Opts],
+ test_server:format(Level, "Saving start opts on ~p: ~p~n",
+ [CTNode, Opts1]),
+ rpc:call(CTNode, application, set_env,
+ [common_test, run_test_start_opts, Opts1]),
+ test_server:format(Level, "[RUN #2] Calling ct_run:script_start() on ~p~n",
+ [CTNode]),
+ ExitStatus = rpc:call(CTNode, ct_run, script_start, []),
+ test_server:format(Level, "[RUN #2] Got exit status value ~p~n",
+ [ExitStatus]),
+ case {CtRunTestResult,ExitStatus} of
+ {{_Ok,Failed,{_UserSkipped,_AutoSkipped}},1} when Failed > 0 ->
+ ok;
+ {{_Ok,0,{_UserSkipped,AutoSkipped}},ExitStatus} when AutoSkipped > 0 ->
+ case proplists:get_value(exit_status, Opts1) of
+ ignore_config when ExitStatus == 1 ->
+ {error,{wrong_exit_status,ExitStatus}};
+ _ ->
+ ok
+ end;
+ {{error,_}=Error,ExitStatus} ->
+ if ExitStatus /= 2 ->
+ {error,{wrong_exit_status,ExitStatus}};
+ ExitStatus == 2 ->
+ Error
+ end;
+ {{_Ok,0,{_UserSkipped,_AutoSkipped}},0} ->
ok;
- {E,_} when E =/= ok ->
- E;
- {_,E} when E =/= ok ->
- E
+ Unexpected ->
+ {error,{unexpected_return_value,Unexpected}}
end.
run(M, F, A, Config) ->
@@ -272,6 +294,10 @@ run({M,F,A}, InitCalls, Config) ->
[M, F, A, CTNode]),
rpc:call(CTNode, M, F, A).
+%% this is the last function that ct_run:script_start() calls, so the
+%% return value here is what rpc:call/4 above returns
+ct_test_halt(ExitStatus) ->
+ ExitStatus.
%%%-----------------------------------------------------------------
%%% wait_for_ct_stop/1
diff --git a/lib/common_test/test/ct_testspec_1_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE.erl
index b6dcf63fdf..6a4a4acd80 100644
--- a/lib/common_test/test/ct_testspec_1_SUITE.erl
+++ b/lib/common_test/test/ct_testspec_1_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. 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
@@ -58,7 +58,7 @@ end_per_testcase(TestCase, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
-all() ->
+all() ->
[all_suites, skip_all_suites, suite, skip_suite,
all_testcases, skip_all_testcases, testcase,
skip_testcase, all_groups, skip_all_groups, group,
@@ -67,23 +67,23 @@ all() ->
skip_group_testcase, topgroup, subgroup, skip_subgroup,
subgroup_all_testcases, skip_subgroup_all_testcases,
subgroup_testcase, skip_subgroup_testcase,
- sub_skipped_by_top, testcase_in_multiple_groups,
- order_of_tests_in_multiple_dirs_no_merge_tests,
- order_of_tests_in_multiple_suites_no_merge_tests,
- order_of_suites_in_multiple_dirs_no_merge_tests,
- order_of_groups_in_multiple_dirs_no_merge_tests,
- order_of_groups_in_multiple_suites_no_merge_tests,
- order_of_tests_in_multiple_dirs,
- order_of_tests_in_multiple_suites,
- order_of_suites_in_multiple_dirs,
- order_of_groups_in_multiple_dirs,
- order_of_groups_in_multiple_suites,
- order_of_tests_in_multiple_suites_with_skip_no_merge_tests,
- order_of_tests_in_multiple_suites_with_skip,
+ sub_skipped_by_top, testcase_many_groups,
+ order_of_tests_many_dirs_no_merge_tests,
+ order_of_tests_many_suites_no_merge_tests,
+ order_of_suites_many_dirs_no_merge_tests,
+ order_of_groups_many_dirs_no_merge_tests,
+ order_of_groups_many_suites_no_merge_tests,
+ order_of_tests_many_dirs,
+ order_of_tests_many_suites,
+ order_of_suites_many_dirs,
+ order_of_groups_many_dirs,
+ order_of_groups_many_suites,
+ order_of_tests_many_suites_with_skip_no_merge_tests,
+ order_of_tests_many_suites_with_skip,
all_plus_one_tc_no_merge_tests,
all_plus_one_tc].
-groups() ->
+groups() ->
[].
init_per_group(_GroupName, Config) ->
@@ -373,19 +373,19 @@ sub_skipped_by_top(Config) when is_list(Config) ->
%%%-----------------------------------------------------------------
%%%
-testcase_in_multiple_groups(Config) when is_list(Config) ->
+testcase_many_groups(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir = filename:join(DataDir, "groups_1"),
TestSpec = [{cases,TestDir,groups_12_SUITE,[testcase_1a,testcase_1b]},
{skip_cases,TestDir,groups_12_SUITE,[testcase_1b],"SKIPPED!"}],
- setup_and_execute(testcase_in_multiple_groups, TestSpec, Config).
+ setup_and_execute(testcase_many_groups, TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
+order_of_tests_many_dirs_no_merge_tests(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -395,13 +395,13 @@ order_of_tests_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
{cases,TestDir2,groups_22_SUITE,[testcase_1]},
{cases,TestDir1,groups_12_SUITE,[testcase_1b]}],
- setup_and_execute(order_of_tests_in_multiple_dirs_no_merge_tests,
+ setup_and_execute(order_of_tests_many_dirs_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_suites_no_merge_tests(Config) when is_list(Config) ->
+order_of_tests_many_suites_no_merge_tests(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -410,13 +410,13 @@ order_of_tests_in_multiple_suites_no_merge_tests(Config) when is_list(Config) ->
{cases,TestDir1,groups_11_SUITE,[testcase_1]},
{cases,TestDir1,groups_12_SUITE,[testcase_1b]}],
- setup_and_execute(order_of_tests_in_multiple_suites_no_merge_tests,
+ setup_and_execute(order_of_tests_many_suites_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_suites_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
+order_of_suites_many_dirs_no_merge_tests(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -426,13 +426,13 @@ order_of_suites_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
{suites,TestDir2,groups_22_SUITE},
{suites,TestDir1,groups_11_SUITE}],
- setup_and_execute(order_of_suites_in_multiple_dirs_no_merge_tests,
+ setup_and_execute(order_of_suites_many_dirs_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_groups_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
+order_of_groups_many_dirs_no_merge_tests(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -442,13 +442,13 @@ order_of_groups_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
{groups,TestDir2,groups_22_SUITE,test_group_1a},
{groups,TestDir1,groups_12_SUITE,test_group_1b}],
- setup_and_execute(order_of_groups_in_multiple_dirs_no_merge_tests,
+ setup_and_execute(order_of_groups_many_dirs_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_groups_in_multiple_suites_no_merge_tests(Config)
+order_of_groups_many_suites_no_merge_tests(Config)
when is_list(Config) ->
DataDir = ?config(data_dir, Config),
@@ -458,13 +458,13 @@ order_of_groups_in_multiple_suites_no_merge_tests(Config)
{groups,TestDir1,groups_11_SUITE,test_group_1a},
{groups,TestDir1,groups_12_SUITE,test_group_1b}],
- setup_and_execute(order_of_groups_in_multiple_suites_no_merge_tests,
+ setup_and_execute(order_of_groups_many_suites_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_suites_with_skip_no_merge_tests(Config)
+order_of_tests_many_suites_with_skip_no_merge_tests(Config)
when is_list(Config) ->
DataDir = ?config(data_dir, Config),
@@ -477,14 +477,14 @@ order_of_tests_in_multiple_suites_with_skip_no_merge_tests(Config)
{skip_cases,TestDir1,groups_12_SUITE,[testcase_1b],"Skip it"}],
setup_and_execute(
- order_of_tests_in_multiple_suites_with_skip_no_merge_tests,
+ order_of_tests_many_suites_with_skip_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_dirs(Config) when is_list(Config) ->
+order_of_tests_many_dirs(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -493,13 +493,13 @@ order_of_tests_in_multiple_dirs(Config) when is_list(Config) ->
{cases,TestDir2,groups_22_SUITE,[testcase_1]},
{cases,TestDir1,groups_12_SUITE,[testcase_1b]}],
- setup_and_execute(order_of_tests_in_multiple_dirs,
+ setup_and_execute(order_of_tests_many_dirs,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_suites(Config) when is_list(Config) ->
+order_of_tests_many_suites(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -507,13 +507,13 @@ order_of_tests_in_multiple_suites(Config) when is_list(Config) ->
{cases,TestDir1,groups_11_SUITE,[testcase_1]},
{cases,TestDir1,groups_12_SUITE,[testcase_1b]}],
- setup_and_execute(order_of_tests_in_multiple_suites,
+ setup_and_execute(order_of_tests_many_suites,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_suites_in_multiple_dirs(Config) when is_list(Config) ->
+order_of_suites_many_dirs(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -522,13 +522,13 @@ order_of_suites_in_multiple_dirs(Config) when is_list(Config) ->
{suites,TestDir2,groups_22_SUITE},
{suites,TestDir1,groups_11_SUITE}],
- setup_and_execute(order_of_suites_in_multiple_dirs,
+ setup_and_execute(order_of_suites_many_dirs,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_groups_in_multiple_dirs(Config) when is_list(Config) ->
+order_of_groups_many_dirs(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -537,13 +537,13 @@ order_of_groups_in_multiple_dirs(Config) when is_list(Config) ->
{groups,TestDir2,groups_22_SUITE,test_group_1a},
{groups,TestDir1,groups_12_SUITE,test_group_1b}],
- setup_and_execute(order_of_groups_in_multiple_dirs,
+ setup_and_execute(order_of_groups_many_dirs,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_groups_in_multiple_suites(Config) when is_list(Config) ->
+order_of_groups_many_suites(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -551,13 +551,13 @@ order_of_groups_in_multiple_suites(Config) when is_list(Config) ->
{groups,TestDir1,groups_11_SUITE,test_group_1a},
{groups,TestDir1,groups_12_SUITE,test_group_1b}],
- setup_and_execute(order_of_groups_in_multiple_suites,
+ setup_and_execute(order_of_groups_many_suites,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_suites_with_skip(Config) when is_list(Config) ->
+order_of_tests_many_suites_with_skip(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -567,7 +567,7 @@ order_of_tests_in_multiple_suites_with_skip(Config) when is_list(Config) ->
{cases,TestDir1,groups_11_SUITE,[testcase_2]},
{skip_cases,TestDir1,groups_12_SUITE,[testcase_1b],"Skip it!"}],
- setup_and_execute(order_of_tests_in_multiple_suites_with_skip,
+ setup_and_execute(order_of_tests_many_suites_with_skip,
TestSpec, Config).
%%%-----------------------------------------------------------------
@@ -621,7 +621,9 @@ setup_and_execute(TCName, TestSpec, Config) ->
ok = ct_test_support:run(Opts, Config),
TestSpec1 = [{logdir,proplists:get_value(logdir,Opts)},
{label,proplists:get_value(label,TestTerms)} | TestSpec],
- ok = ct_test_support:run(ct, run_testspec, [TestSpec1], Config),
+ {_Ok,_Failed,{_USkipped,_ASkipped}} =
+ ct_test_support:run(ct, run_testspec, [TestSpec1], Config),
+
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(TCName,
@@ -1202,10 +1204,10 @@ test_events(sub_skipped_by_top) ->
{negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
];
-test_events(testcase_in_multiple_groups) ->
+test_events(testcase_many_groups) ->
[];
-test_events(order_of_tests_in_multiple_dirs_no_merge_tests) ->
+test_events(order_of_tests_many_dirs_no_merge_tests) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,testcase_1a}},
{?eh,tc_done, {groups_12_SUITE,testcase_1a,
@@ -1217,7 +1219,7 @@ test_events(order_of_tests_in_multiple_dirs_no_merge_tests) ->
{failed,{error,{test_case_failed,no_group_data}}}}},
{?eh,stop_logging,[]}
];
-test_events(order_of_tests_in_multiple_suites_no_merge_tests) ->
+test_events(order_of_tests_many_suites_no_merge_tests) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,testcase_1a}},
{?eh,tc_done,{groups_12_SUITE,testcase_1a,'_'}},
@@ -1227,7 +1229,7 @@ test_events(order_of_tests_in_multiple_suites_no_merge_tests) ->
{?eh,tc_done,{groups_12_SUITE,testcase_1b,'_'}},
{?eh,stop_logging,[]}
];
-test_events(order_of_suites_in_multiple_dirs_no_merge_tests) ->
+test_events(order_of_suites_many_dirs_no_merge_tests) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,init_per_suite}},
{?eh,tc_done,{groups_12_SUITE,init_per_suite,'_'}},
@@ -1242,7 +1244,7 @@ test_events(order_of_suites_in_multiple_dirs_no_merge_tests) ->
{?eh,tc_start,{groups_11_SUITE,end_per_suite}},
{?eh,tc_done,{groups_11_SUITE,end_per_suite,'_'}},
{?eh,stop_logging,[]}];
-test_events(order_of_groups_in_multiple_dirs_no_merge_tests) ->
+test_events(order_of_groups_many_dirs_no_merge_tests) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1a,'_'}}},
@@ -1255,7 +1257,7 @@ test_events(order_of_groups_in_multiple_dirs_no_merge_tests) ->
{?eh,tc_done, {groups_12_SUITE,{end_per_group,test_group_1b,'_'},'_'}},
{?eh,stop_logging,[]}];
-test_events(order_of_groups_in_multiple_suites_no_merge_tests) ->
+test_events(order_of_groups_many_suites_no_merge_tests) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1a,'_'}}},
@@ -1268,7 +1270,7 @@ test_events(order_of_groups_in_multiple_suites_no_merge_tests) ->
{?eh,tc_done, {groups_12_SUITE,{end_per_group,test_group_1b,'_'},'_'}},
{?eh,stop_logging,[]}];
-test_events(order_of_tests_in_multiple_suites_with_skip_no_merge_tests) ->
+test_events(order_of_tests_many_suites_with_skip_no_merge_tests) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,testcase_1a}},
{?eh,tc_done,{groups_12_SUITE,testcase_1a,'_'}},
@@ -1280,7 +1282,7 @@ test_events(order_of_tests_in_multiple_suites_with_skip_no_merge_tests) ->
{?eh,stop_logging,[]}
];
-test_events(order_of_tests_in_multiple_dirs) ->
+test_events(order_of_tests_many_dirs) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,testcase_1a}},
{?eh,tc_done,
@@ -1294,7 +1296,7 @@ test_events(order_of_tests_in_multiple_dirs) ->
{?eh,tc_done,{groups_22_SUITE,testcase_1,ok}},
{?eh,stop_logging,[]}
];
-test_events(order_of_tests_in_multiple_suites) ->
+test_events(order_of_tests_many_suites) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,testcase_1a}},
{?eh,tc_done,{groups_12_SUITE,testcase_1a,'_'}},
@@ -1306,7 +1308,7 @@ test_events(order_of_tests_in_multiple_suites) ->
{?eh,tc_done,{groups_11_SUITE,testcase_1,ok}},
{?eh,stop_logging,[]}
];
-test_events(order_of_suites_in_multiple_dirs) ->
+test_events(order_of_suites_many_dirs) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,init_per_suite}},
{?eh,tc_done,{groups_12_SUITE,init_per_suite,'_'}},
@@ -1323,7 +1325,7 @@ test_events(order_of_suites_in_multiple_dirs) ->
{?eh,tc_start,{groups_22_SUITE,end_per_suite}},
{?eh,tc_done,{groups_22_SUITE,end_per_suite,'_'}},
{?eh,stop_logging,[]}];
-test_events(order_of_groups_in_multiple_dirs) ->
+test_events(order_of_groups_many_dirs) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1a,'_'}}},
@@ -1336,7 +1338,7 @@ test_events(order_of_groups_in_multiple_dirs) ->
{?eh,tc_done, {groups_22_SUITE,{end_per_group,test_group_1a,'_'},'_'}},
{?eh,stop_logging,[]}];
-test_events(order_of_groups_in_multiple_suites) ->
+test_events(order_of_groups_many_suites) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1a,'_'}}},
@@ -1350,7 +1352,7 @@ test_events(order_of_groups_in_multiple_suites) ->
{?eh,stop_logging,[]}];
-test_events(order_of_tests_in_multiple_suites_with_skip) ->
+test_events(order_of_tests_many_suites_with_skip) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,testcase_1a}},
{?eh,tc_done,{groups_12_SUITE,testcase_1a,'_'}},
diff --git a/lib/common_test/test/ct_testspec_2_SUITE.erl b/lib/common_test/test/ct_testspec_2_SUITE.erl
new file mode 100644
index 0000000000..9d2dc84ad3
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_2_SUITE.erl
@@ -0,0 +1,759 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_testspec_2_SUITE
+%%%
+%%% Description:
+%%% Test test specifications
+%%%
+%%% The suites used for the test are located in the data directory.
+%%%-------------------------------------------------------------------
+-module(ct_testspec_2_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/src/ct_util.hrl").
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+%% suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [basic_compatible_no_nodes,
+ basic_compatible_nodes,
+ unknown_terms,
+ no_merging,
+ multiple_specs,
+ misc_config_terms,
+ define_names_1].
+
+
+%%--------------------------------------------------------------------
+%% VALID TEST SPEC TERMS (R15B02):
+%%
+%% {node,3}
+%% {cover,2}
+%% {cover,3}
+%% {config,2}
+%% {config,3}
+%% {config,4}
+%% {userconfig,2}
+%% {userconfig,3}
+%% {alias,3}
+%% {merge_tests,2}
+%% {logdir,2}
+%% {logdir,3}
+%% {logopts,2}
+%% {logopts,3}
+%% {basic_html,2}
+%% {basic_html,3}
+%% {verbosity,2}
+%% {verbosity,3}
+%% {silent_connections,2}
+%% {silent_connections,3}
+%% {label,2}
+%% {label,3}
+%% {event_handler,2}
+%% {event_handler,3}
+%% {event_handler,4}
+%% {ct_hooks,2}
+%% {ct_hooks,3}
+%% {enable_builtin_hooks,2}
+%% {release_shell,2}
+%% {multiply_timetraps,2}
+%% {multiply_timetraps,3}
+%% {scale_timetraps,2}
+%% {scale_timetraps,3}
+%% {include,2}
+%% {include,3}
+%% {auto_compile,2}
+%% {auto_compile,3}
+%% {stylesheet,2}
+%% {stylesheet,3}
+%% {suites,3}
+%% {suites,4}
+%% {groups,4}
+%% {groups,5}
+%% {groups,6}
+%% {cases,4}
+%% {cases,5}
+%% {skip_suites,4}
+%% {skip_suites,5}
+%% {skip_groups,5}
+%% {skip_groups,6}
+%% {skip_groups,7}
+%% {skip_cases,5}
+%% {skip_cases,6}
+%% {create_priv_dir,2}
+%%
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+basic_compatible_no_nodes(_Config) ->
+
+ AliasDir1 = "../tests/to1",
+ AliasDir2 = "../tests/to2",
+ CfgDir1 = "../cfgs/to1/x.cfg",
+ CfgDir2 = ["../cfgs/to2/x.cfg","../cfgs/to2/y.cfg"],
+ LogDir = "../logs",
+ IncludeDir1 = "../../include",
+ IncludeDir2 = ["../tests/to1/include","../tests/to2/include"],
+
+ Spec =
+ [
+ {label,"basic_compatible_no_nodes"},
+ {alias,to1,AliasDir1},
+ {alias,to2,AliasDir2},
+ {config,CfgDir1},
+ {config,CfgDir2},
+ {userconfig,{?MODULE,"cfg_str1"}},
+ {userconfig,{?MODULE,"cfg_str2"}},
+ {logdir,LogDir},
+ {logopts,[no_nl]},
+ {event_handler,evh1,[1]},
+ {event_handler,[evh2,evh3],[[2,3]]},
+ {ct_hooks,[{cth_mod1,[]}]},
+ {ct_hooks,[{cth_mod2,[]}]},
+ {multiply_timetraps,2},
+ {include,IncludeDir1},
+ {include,IncludeDir2},
+ {suites,to1,[x_SUITE]},
+ {groups,to1,y_SUITE,[g1,g2]},
+ {cases,to1,y_SUITE,[tc1,tc2]},
+ {skip_suites,to1,z_SUITE,"skipped"},
+ {suites,to2,[x_SUITE,y_SUITE]},
+ {skip_groups,to2,x_SUITE,[g1,g2],"skipped"},
+ {skip_cases,to2,y_SUITE,[tc1,tc2],"skipped"}
+ ],
+
+ {ok,SpecDir} = file:get_cwd(),
+
+ ListResult = ct_testspec:collect_tests_from_list(Spec, false),
+ ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]),
+ SpecFile = ct_test_support:write_testspec(Spec,SpecDir,
+ "basic_compatible_no_nodes.spec"),
+ FileResult = ct_testspec:collect_tests_from_file([SpecFile], false),
+ ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]),
+
+ Node = node(),
+ LogDirV = get_absdir(filename:join(SpecDir,"../logs")),
+ Alias1V = get_absdir(filename:join(SpecDir,AliasDir1)),
+ Alias2V = get_absdir(filename:join(SpecDir,AliasDir2)),
+ CFGs = [{Node,get_absdir(filename:join(SpecDir,CfgDir))} ||
+ CfgDir <- [CfgDir1 | CfgDir2]],
+ Incls = [{Node,get_absdir(filename:join(SpecDir,IncludeDir))} ||
+ IncludeDir <- [IncludeDir1 | IncludeDir2]],
+
+ Verify = #testspec{spec_dir = SpecDir,
+ nodes = [{undefined,Node}],
+ init = [],
+ label = [{Node,"basic_compatible_no_nodes"}],
+ logdir = [{Node,LogDirV},"."],
+ logopts = [{Node,[no_nl]}],
+ basic_html = [],
+ cover = [],
+ config = CFGs,
+ userconfig = [{Node,{?MODULE,"cfg_str1"}},
+ {Node,{?MODULE,"cfg_str2"}}],
+ event_handler = [{Node,evh1,[1]},
+ {Node,evh2,[[2,3]]},
+ {Node,evh3,[[2,3]]}],
+ ct_hooks = [{Node,{cth_mod1,[]}},
+ {Node,{cth_mod2,[]}}],
+ enable_builtin_hooks = true,
+ release_shell = false,
+ include = Incls,
+ auto_compile = [],
+ stylesheet = [],
+ multiply_timetraps = [{Node,2}],
+ scale_timetraps = [],
+ create_priv_dir = [],
+ alias = [{to1,Alias1V},{to2,Alias2V}],
+ tests = [{{Node,Alias1V},
+ [{x_SUITE,[all]},
+ {y_SUITE,[{g1,all},{g2,all},tc1,tc2]},
+ {z_SUITE,[{all,{skip,"skipped"}}]}]},
+ {{Node,Alias2V},
+ [{x_SUITE,[all,
+ {{g1,all},{skip,"skipped"}},
+ {{g2,all},{skip,"skipped"}}]},
+ {y_SUITE,[all,
+ {tc1,{skip,"skipped"}},
+ {tc2,{skip,"skipped"}}]}]}],
+ merge_tests = true},
+
+ verify_result(Verify,ListResult,FileResult).
+
+%%%-----------------------------------------------------------------
+%%%
+basic_compatible_nodes(_Config) ->
+
+ Node1 = node1@host1,
+ Node2 = node2@host2,
+ TODir1 = "../tests/to1",
+ TODir2 = "../tests/to2",
+ CfgDir1 = "../cfgs/to1/x.cfg",
+ CfgDir2 = ["../cfgs/to2/x.cfg","../cfgs/to2/y.cfg"],
+ LogDir = "../logs",
+ MasterLogDir = "../master_logs",
+ IncludeDir1 = "../../include",
+ IncludeDir2 = ["../tests/to1/include","../tests/to2/include"],
+
+ Spec =
+ [
+ {node,n1,Node1},
+ {node,n2,Node2},
+ {init,[n1],[{node_start,[{callback_module,cbm}]}]},
+ {init,n2,[{node_start,[]}]},
+ {init,all_nodes,{eval,{mod,func,[]}}},
+ {label,"basic_compatible_nodes"},
+ {label,n1,basic_compatible_nodes_1},
+ {config,n1,CfgDir1},
+ {config,n2,CfgDir2},
+ {userconfig,{?MODULE,"cfg_str1"}},
+ {userconfig,{?MODULE,"cfg_str2"}},
+ {logdir,all_nodes,LogDir},
+ {logdir,master,MasterLogDir},
+ {logopts,node2@host2,[no_nl]},
+ {event_handler,master,evh1,[1]},
+ {event_handler,[n1,n2],[evh2,evh3],[[2,3]]},
+ {ct_hooks,all_nodes,[{cth_mod1,[]}]},
+ {ct_hooks,[{cth_mod2,[]}]},
+ {multiply_timetraps,node1@host1,2},
+ {include,n1,IncludeDir1},
+ {include,[n1,n2],IncludeDir2},
+ {suites,n1,TODir1,[x_SUITE]},
+ {groups,n1,TODir1,y_SUITE,[g1,g2]},
+ {cases,n1,TODir1,y_SUITE,[tc1,tc2]},
+ {skip_suites,n1,TODir1,z_SUITE,"skipped"},
+ {suites,n2,TODir2,[x_SUITE,y_SUITE]},
+ {skip_groups,n2,TODir2,x_SUITE,[g1,g2],"skipped"},
+ {skip_cases,n2,TODir2,y_SUITE,[tc1,tc2],"skipped"}
+ ],
+
+ {ok,SpecDir} = file:get_cwd(),
+
+ ListResult = ct_testspec:collect_tests_from_list(Spec, false),
+ ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]),
+ SpecFile = ct_test_support:write_testspec(Spec,SpecDir,
+ "basic_compatible_nodes.spec"),
+ FileResult = ct_testspec:collect_tests_from_file([SpecFile], false),
+ ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]),
+
+ Node = node(),
+ LogDirV = get_absdir(filename:join(SpecDir,"../logs")),
+ MasterLogDirV = get_absdir(filename:join(SpecDir,"../master_logs")),
+ TO1V = get_absdir(filename:join(SpecDir,TODir1)),
+ TO2V = get_absdir(filename:join(SpecDir,TODir2)),
+ CFGs = [{Node1,get_absdir(filename:join(SpecDir,CfgDir1))} |
+ [{Node2,get_absdir(filename:join(SpecDir,CfgDir))} || CfgDir <- CfgDir2]],
+ Incls = [{Node1,get_absdir(filename:join(SpecDir,IncludeDir1))} |
+ [{Node1,get_absdir(filename:join(SpecDir,IncludeDir))} ||
+ IncludeDir <- IncludeDir2] ++
+ [{Node2,get_absdir(filename:join(SpecDir,IncludeDir))} ||
+ IncludeDir <- IncludeDir2]],
+
+ Verify = #testspec{spec_dir = SpecDir,
+ nodes = [{undefined,Node},{n1,Node1},{n2,Node2}],
+ init = [{Node1,[{node_start,[{callback_module,cbm}]},
+ {eval,[{mod,func,[]}]}]},
+ {Node2,[{node_start,[{callback_module,ct_slave}]},
+ {eval,[{mod,func,[]}]}]},
+ {Node,[{node_start,[]},
+ {eval,[{mod,func,[]}]}]}],
+ label = [{Node,"basic_compatible_nodes"},
+ {Node2,"basic_compatible_nodes"},
+ {Node1,basic_compatible_nodes_1}],
+ logdir = [{Node,LogDirV},{Node1,LogDirV},{Node2,LogDirV},
+ {master,MasterLogDirV},"."],
+ logopts = [{Node2,[no_nl]}],
+ basic_html = [],
+ cover = [],
+ config = CFGs,
+ userconfig = [{Node,{?MODULE,"cfg_str1"}},
+ {Node1,{?MODULE,"cfg_str1"}},
+ {Node2,{?MODULE,"cfg_str1"}},
+ {Node,{?MODULE,"cfg_str2"}},
+ {Node1,{?MODULE,"cfg_str2"}},
+ {Node2,{?MODULE,"cfg_str2"}}],
+ event_handler = [{master,evh1,[1]},
+ {Node1,evh2,[[2,3]]},
+ {Node1,evh3,[[2,3]]},
+ {Node2,evh2,[[2,3]]},
+ {Node2,evh3,[[2,3]]}],
+ ct_hooks = [{Node,{cth_mod1,[]}},
+ {Node1,{cth_mod1,[]}},
+ {Node2,{cth_mod1,[]}},
+ {Node,{cth_mod2,[]}},
+ {Node1,{cth_mod2,[]}},
+ {Node2,{cth_mod2,[]}}],
+ enable_builtin_hooks = true,
+ release_shell = false,
+ include = Incls,
+ auto_compile = [],
+ stylesheet = [],
+ multiply_timetraps = [{Node1,2}],
+ scale_timetraps = [],
+ create_priv_dir = [],
+ tests = [{{Node1,TO1V},
+ [{x_SUITE,[all]},
+ {y_SUITE,[{g1,all},{g2,all},tc1,tc2]},
+ {z_SUITE,[{all,{skip,"skipped"}}]}]},
+ {{Node2,TO2V},
+ [{x_SUITE,[all,
+ {{g1,all},{skip,"skipped"}},
+ {{g2,all},{skip,"skipped"}}]},
+ {y_SUITE,[all,
+ {tc1,{skip,"skipped"}},
+ {tc2,{skip,"skipped"}}]}]}],
+ merge_tests = true},
+
+ verify_result(Verify,ListResult,FileResult).
+
+%%%-----------------------------------------------------------------
+%%%
+unknown_terms(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+
+ Spec1 = [{suites,PrivDir,all},
+ {userdata,"I've got news for you"}],
+ {error,{undefined_term_in_spec,{userdata,_}}} =
+ (catch ct_testspec:collect_tests_from_list(Spec1, false)),
+ true = is_record(ct_testspec:collect_tests_from_list(Spec1, true),
+ testspec),
+
+ Spec2 = [{logdir,{logdir,PrivDir}}],
+ {error,{invalid_directory_name,_}} =
+ (catch ct_testspec:collect_tests_from_list(Spec2, false)),
+
+ Spec3 = [{suite,PrivDir,all}],
+ {error,{undefined_term_in_spec,{suite,_,_}}} =
+ (catch ct_testspec:collect_tests_from_list(Spec3, false)),
+ true = is_record(ct_testspec:collect_tests_from_list(Spec3, true), testspec),
+
+ Spec4 = [{suites,PrivDir,all},
+ {skip_suites,PrivDir,x_SUITE}],
+ {error,{bad_term_in_spec,{skip_suites,_,_}}} =
+ (catch ct_testspec:collect_tests_from_list(Spec4, false)),
+ {error,{bad_term_in_spec,{skip_suites,_,_}}} =
+ (catch ct_testspec:collect_tests_from_list(Spec4, true)),
+
+ Spec5 = [{configs,all_nodes,PrivDir}],
+ {error,{undefined_term_in_spec,{configs,_,_}}} =
+ (catch ct_testspec:collect_tests_from_list(Spec5, false)),
+ true = is_record(ct_testspec:collect_tests_from_list(Spec5, true), testspec),
+
+ ok.
+
+%%%-----------------------------------------------------------------
+%%%
+no_merging(_Config) ->
+ Node1 = node1@host1,
+ Node2 = node2@host2,
+ TODir1 = "../tests/to1",
+ TODir2 = "../tests/to2",
+ Spec =
+ [
+ {merge_tests,false},
+ {node,n1,Node1},
+ {node,n2,Node2},
+ {suites,n1,TODir1,[x_SUITE]},
+ {groups,n1,TODir1,y_SUITE,[g1,g2]},
+ {cases,n1,TODir1,y_SUITE,[tc1,tc2]},
+ {skip_suites,n1,TODir1,z_SUITE,"skipped"},
+ {suites,n2,TODir2,[x_SUITE,y_SUITE]},
+ {skip_groups,n2,TODir2,x_SUITE,[g1,g2],"skipped"},
+ {skip_cases,n2,TODir2,y_SUITE,[tc1,tc2],"skipped"}
+ ],
+
+ {ok,SpecDir} = file:get_cwd(),
+
+ ListResult = ct_testspec:collect_tests_from_list(Spec, false),
+ ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]),
+ SpecFile = ct_test_support:write_testspec(Spec,SpecDir,
+ "no_merging.spec"),
+ FileResult = ct_testspec:collect_tests_from_file([SpecFile], false),
+ ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]),
+
+ Node = node(),
+ TO1V = get_absdir(filename:join(SpecDir,TODir1)),
+ TO2V = get_absdir(filename:join(SpecDir,TODir2)),
+
+ Verify = #testspec{merge_tests = false,
+ spec_dir = SpecDir,
+ nodes = [{undefined,Node},{n1,Node1},{n2,Node2}],
+ tests = [{{Node1,TO1V},
+ [{x_SUITE,[all]}]},
+ {{Node1,TO1V},
+ [{y_SUITE,[{g1,all},{g2,all}]}]},
+ {{Node1,TO1V},
+ [{y_SUITE,[tc1,tc2]}]},
+ {{Node1,TO1V},
+ [{z_SUITE,[{all,{skip,"skipped"}}]}]},
+ {{Node2,TO2V},
+ [{x_SUITE,[all]}]},
+ {{Node2,TO2V},
+ [{y_SUITE,[all]}]},
+ {{Node2,TO2V},
+ [{x_SUITE,[{{g1,all},{skip,"skipped"}},
+ {{g2,all},{skip,"skipped"}}]}]},
+ {{Node2,TO2V},
+ [{y_SUITE,[{tc1,{skip,"skipped"}},
+ {tc2,{skip,"skipped"}}]}]}]},
+
+ verify_result(Verify,ListResult,FileResult).
+
+%%%-----------------------------------------------------------------
+%%%
+multiple_specs(_Config) ->
+ Node1 = node1@host1,
+ Node2 = node2@host2,
+ TODir1 = "../tests/to1",
+ TODir2 = "../tests/to2",
+ CfgDir1 = "../cfgs/to1/x.cfg",
+ CfgDir2 = ["../cfgs/to2/x.cfg","../cfgs/to2/y.cfg"],
+ LogDir = "../logs",
+ Spec1 =
+ [
+ {node,n1,Node1},
+ {node,n2,Node2},
+ {alias,to1,TODir1},
+ {alias,to2,TODir2},
+ {label,"multiple_specs1"},
+ {config,n1,CfgDir1},
+ {config,n2,CfgDir2},
+ {logdir,all_nodes,LogDir}
+ ],
+ Spec2 =
+ [
+ {merge_tests,false},
+ {label,"multiple_specs2"},
+ {suites,n1,TODir1,[x_SUITE]},
+ {groups,n1,TODir1,y_SUITE,[g1,g2]},
+ {cases,n1,TODir1,y_SUITE,[tc1,tc2]},
+ {skip_suites,n1,TODir1,z_SUITE,"skipped"},
+ {suites,n2,TODir2,[x_SUITE,y_SUITE]},
+ {skip_groups,n2,TODir2,x_SUITE,[g1,g2],"skipped"},
+ {skip_cases,n2,TODir2,y_SUITE,[tc1,tc2],"skipped"}
+ ],
+
+ {ok,SpecDir} = file:get_cwd(),
+ SpecFile1 = ct_test_support:write_testspec(Spec1,SpecDir,
+ "multiple_specs.1.spec"),
+ SpecFile2 = ct_test_support:write_testspec(Spec2,SpecDir,
+ "multiple_specs.2.spec"),
+ FileResult = ct_testspec:collect_tests_from_file([SpecFile1,SpecFile2],
+ false),
+ ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]),
+
+ Node = node(),
+ TO1V = get_absdir(filename:join(SpecDir,TODir1)),
+ TO2V = get_absdir(filename:join(SpecDir,TODir2)),
+ CFGs = [{Node1,get_absdir(filename:join(SpecDir,CfgDir1))} |
+ [{Node2,get_absdir(filename:join(SpecDir,CfgDir))} || CfgDir <- CfgDir2]],
+ LogDirV = get_absdir(filename:join(SpecDir,"../logs")),
+
+ Verify = #testspec{merge_tests = false,
+ spec_dir = SpecDir,
+ nodes = [{undefined,Node},{n1,Node1},{n2,Node2}],
+ alias = [{to1,TO1V},{to2,TO2V}],
+ label = [{Node,"multiple_specs1"},
+ {Node1,"multiple_specs1"},
+ {Node2,"multiple_specs1"}],
+ logdir = [{Node,LogDirV},{Node1,LogDirV},{Node2,LogDirV},"."],
+ config = CFGs,
+ tests = [{{Node1,TO1V},
+ [{x_SUITE,[all]}]},
+ {{Node1,TO1V},
+ [{y_SUITE,[{g1,all},{g2,all}]}]},
+ {{Node1,TO1V},
+ [{y_SUITE,[tc1,tc2]}]},
+ {{Node1,TO1V},
+ [{z_SUITE,[{all,{skip,"skipped"}}]}]},
+ {{Node2,TO2V},
+ [{x_SUITE,[all]}]},
+ {{Node2,TO2V},
+ [{y_SUITE,[all]}]},
+ {{Node2,TO2V},
+ [{x_SUITE,[{{g1,all},{skip,"skipped"}},
+ {{g2,all},{skip,"skipped"}}]}]},
+ {{Node2,TO2V},
+ [{y_SUITE,[{tc1,{skip,"skipped"}},
+ {tc2,{skip,"skipped"}}]}]}]},
+
+ verify_result(Verify,FileResult,FileResult).
+
+%%%-----------------------------------------------------------------
+%%%
+misc_config_terms(_Config) ->
+ CfgDir = "../cfgs/to1",
+
+ Spec =
+ [{node,x,n1@h1},{node,y,n2@h2},
+
+ {config,CfgDir,"a.cfg"},
+ {config,n1@h1,CfgDir,"b.cfg"},
+ {config,all_nodes,CfgDir,"c.cfg"},
+ {config,all_nodes,filename:join(CfgDir,"d.cfg")},
+
+ {basic_html,true},
+ {basic_html,n1@h1,false},
+ {basic_html,n2@h2,true},
+
+ {silent_connections,n1@h1,all},
+ {silent_connections,n2@h2,[ssh]},
+
+ {enable_builtin_hooks,false},
+
+ {release_shell,true},
+
+ {auto_compile,false},
+ {auto_compile,n1@h1,true},
+ {auto_compile,n2@h2,false},
+
+ {stylesheet,"../css"},
+ {stylesheet,n1@h1,"./n1/css"},
+ {stylesheet,n2@h2,"./n2/css"},
+
+ {create_priv_dir,[auto_per_tc]},
+ {create_priv_dir,n1@h1,[manual_per_tc]},
+ {create_priv_dir,n2@h2,[auto_per_run]}
+ ],
+
+ {ok,SpecDir} = file:get_cwd(),
+
+ ListResult = ct_testspec:collect_tests_from_list(Spec, false),
+ ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]),
+ SpecFile = ct_test_support:write_testspec(Spec,SpecDir,
+ "misc_config_terms.spec"),
+ FileResult = ct_testspec:collect_tests_from_file([SpecFile], false),
+ ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]),
+
+ Node = node(),
+ CfgA = get_absdir(filename:join(filename:join(SpecDir,CfgDir), "a.cfg")),
+ CfgB = get_absdir(filename:join(filename:join(SpecDir,CfgDir), "b.cfg")),
+ CfgC = get_absdir(filename:join(filename:join(SpecDir,CfgDir), "c.cfg")),
+ CfgD = get_absdir(filename:join(filename:join(SpecDir,CfgDir), "d.cfg")),
+ CSS = get_absdir(filename:join(SpecDir,"../css")),
+ CSS1 = get_absdir(filename:join(SpecDir,"./n1/css")),
+ CSS2 = get_absdir(filename:join(SpecDir,"./n2/css")),
+
+ Verify = #testspec{spec_dir = SpecDir,
+ nodes = [{undefined,Node},{x,n1@h1},{y,n2@h2}],
+ basic_html = [{Node,true},{n1@h1,false},{n2@h2,true}],
+ silent_connections = [{n1@h1,[all]},{n2@h2,[ssh]}],
+ config = [{Node,CfgA},
+ {n1@h1,CfgA},
+ {n2@h2,CfgA},
+ {n1@h1,CfgB},
+ {Node,CfgC},
+ {n1@h1,CfgC},
+ {n2@h2,CfgC},
+ {Node,CfgD},
+ {n1@h1,CfgD},
+ {n2@h2,CfgD}],
+ enable_builtin_hooks = false,
+ release_shell = true,
+ auto_compile = [{Node,false},
+ {n1@h1,true},
+ {n2@h2,false}],
+ stylesheet = [{Node,CSS},
+ {n1@h1,CSS1},
+ {n2@h2,CSS2}],
+ create_priv_dir = [{Node,[auto_per_tc]},
+ {n1@h1,[manual_per_tc]},
+ {n2@h2,[auto_per_run]}]
+ },
+
+ verify_result(Verify,ListResult,FileResult).
+
+%%%-----------------------------------------------------------------
+%%%
+define_names_1(_Config) ->
+ Spec =
+ [
+ {define,'HOST','eniac'},
+ {define,'NODE1',testnode1},
+ {define,'NODE2',testnode2},
+ {define,'NODES',['NODE1@HOST',
+ 'NODE2@HOST']},
+ {define,'TOPDIR',".."},
+ {define,'TO1',"to1"},
+ {define,'TO2',"to2"},
+ {define,'LOGDIR',"'TOPDIR'/logdir"},
+ {define,'LOGDIR1',"'TOPDIR'/logdir1"},
+ {define,'LOGDIR2',"'TOPDIR'/logdir2"},
+ {define,'CFGDIR',"'TOPDIR'/cfgs"},
+ {define,'CFGFILES',["cfgX","cfgY"]},
+ {define,'TESTDIR',"'TOPDIR'/test"},
+ {define,'TO1DIR',"'TESTDIR'/'TO1'"},
+ {define,'TO2DIR',"'TESTDIR'/'TO2'"},
+ {define,'EXSUITE',ex_SUITE},
+ {define,'EXGRS',[g1,g2]},
+
+ {logdir,'LOGDIR'},
+ {logdir,'NODE1@HOST','LOGDIR1'},
+ {logdir,'NODE2@HOST','LOGDIR2'},
+
+ {config,["a.cfg","b.cfg"]},
+ {config,'NODES',"./'CFGDIR'/c.cfg"},
+ {config,'CFGDIR',["d.cfg","e.cfg"]},
+ {config,'NODE2@HOST','CFGDIR','CFGFILES'},
+
+ {suites,'NODE1@HOST','TO1DIR',all},
+ {suites,'NODES','TO2DIR',all},
+
+ {groups,'TO1DIR','EXSUITE','EXGRS'}
+ ],
+
+ {ok,SpecDir} = file:get_cwd(),
+
+ ListResult = ct_testspec:collect_tests_from_list(Spec, false),
+ ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]),
+ SpecFile = ct_test_support:write_testspec(Spec,SpecDir,
+ "define_names_1.spec"),
+ FileResult = ct_testspec:collect_tests_from_file([SpecFile], false),
+ ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]),
+
+ N = node(),
+ N1 = testnode1@eniac,
+ N2 = testnode2@eniac,
+ Join = fun(Dir) -> shorten_path(filename:join(SpecDir,Dir),SpecDir) end,
+
+ Verify = #testspec{spec_dir = SpecDir,
+ nodes = [{undefined,N2},
+ {undefined,N1},
+ {undefined,N}],
+ config = [{N2,Join("a.cfg")},{N2,Join("b.cfg")},
+ {N1,Join("a.cfg")},{N1,Join("b.cfg")},
+ {N,Join("a.cfg")},{N,Join("b.cfg")},
+ {N1,Join("../cfgs/c.cfg")},{N2,Join("../cfgs/c.cfg")},
+ {N2,Join("../cfgs/d.cfg")},{N2,Join("../cfgs/e.cfg")},
+ {N1,Join("../cfgs/d.cfg")},{N1,Join("../cfgs/e.cfg")},
+ {N,Join("../cfgs/d.cfg")},{N,Join("../cfgs/e.cfg")},
+ {N2,Join("../cfgs/cfgX")},{N2,Join("../cfgs/cfgY")}],
+ logdir = [{N,Join("../logdir")},
+ {N1,Join("../logdir1")},
+ {N2,Join("../logdir2")},
+ "."],
+ tests = [{{N1,Join("../test/to1")},[{all,[all]}]},
+ {{N1,Join("../test/to2")},[{all,[all]}]},
+ {{N2,Join("../test/to2")},[{all,[all]}]},
+ {{N2,Join("../test/to1")},
+ [{ex_SUITE,[{g1,all},{g2,all}]}]},
+ {{N,Join("../test/to1")},
+ [{ex_SUITE,[{g1,all},{g2,all}]}]}]
+ },
+ verify_result(Verify,ListResult,FileResult).
+
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+verify_result(Verify,ListResult,FileResult) ->
+ {_,TSLTuples} = rec2proplist(ListResult),
+ {_,TSFTuples} = rec2proplist(FileResult),
+ {_,VTuples} = rec2proplist(Verify),
+ VResult =
+ (catch lists:foldl(fun({Tag,Val},{[{Tag,Val}|TSL],[{Tag,Val}|TSF]}) ->
+ {TSL,TSF};
+ ({Tag,Val},{[{_Tag,TSLVal}|_TSL],[{Tag,Val}|_TSF]}) ->
+ exit({ts_list_mismatch,Tag,Val,TSLVal});
+ ({Tag,Val},{[{Tag,Val}|_TSL],[{_Tag,TSFVal}|_TSF]}) ->
+ exit({ts_file_mismatch,Tag,Val,TSFVal});
+ ({Tag,Val},{_,[{_Tag,TSFVal}|_TSF]}) ->
+ exit({ts_mismatch,Tag,Val,TSFVal})
+ end, {TSLTuples,TSFTuples}, VTuples)),
+ case VResult of
+ {'EXIT',Reason} ->
+ ct:fail(Reason);
+ _ ->
+ ok
+ end,
+ ok.
+
+
+check_parameter(S="cfg_str1") ->
+ {ok,{config,S}};
+check_parameter(S="cfg_str2") ->
+ {ok,{config,S}}.
+read_config(S) ->
+ {ok,[{cfg,S}]}.
+
+rec2proplist(E={error,_What}) ->
+ exit({invalid_testspec_record,E});
+rec2proplist(Rec) ->
+ [RecName|RecList] = tuple_to_list(Rec),
+ FieldNames =
+ if RecName == testspec ->
+ record_info(fields, testspec);
+ true ->
+ undefined
+ end,
+ {RecName,combine_names_and_vals(FieldNames,RecList)}.
+
+combine_names_and_vals([FN|FNs], [V|Vs]) ->
+ [{FN,V} | combine_names_and_vals(FNs, Vs)];
+combine_names_and_vals([], []) ->
+ [];
+combine_names_and_vals(_, _) ->
+ [].
+
+get_absdir(Dir) ->
+ shorten_path(filename:absname(Dir),Dir).
+
+shorten_path(Path,SpecDir) ->
+ case shorten_split_path(filename:split(Path),[]) of
+ [] ->
+ [Root|_] = filename:split(SpecDir),
+ Root;
+ Short ->
+ filename:join(Short)
+ end.
+
+shorten_split_path([".."|Path],SoFar) ->
+ shorten_split_path(Path,tl(SoFar));
+shorten_split_path(["."|Path],SoFar) ->
+ shorten_split_path(Path,SoFar);
+shorten_split_path([Dir|Path],SoFar) ->
+ shorten_split_path(Path,[Dir|SoFar]);
+shorten_split_path([],SoFar) ->
+ lists:reverse(SoFar).
diff --git a/lib/common_test/test/ct_verbosity_SUITE.erl b/lib/common_test/test/ct_verbosity_SUITE.erl
new file mode 100644
index 0000000000..349319de94
--- /dev/null
+++ b/lib/common_test/test/ct_verbosity_SUITE.erl
@@ -0,0 +1,244 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_verbosity_SUITE
+%%%
+%%% Description:
+%%% Test that verbosity levels vs the importance parameter works as
+%%% expected.
+%%%
+%%%-------------------------------------------------------------------
+-module(ct_verbosity_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config1 = ct_test_support:init_per_suite(Config),
+ Config1.
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ no_levels,
+ general_level_low,
+ general_level_std,
+ general_level_hi,
+ change_default,
+ combine_categories,
+ testspec_only,
+ merge_with_testspec
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+no_levels(Config) ->
+ TC = no_levels,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "io_test_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC}], Config),
+ ok = execute(TC, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+general_level_low(Config) ->
+ TC = general_level_low,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "io_test_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC},
+ {verbosity,0}], Config),
+ ok = execute(TC, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+general_level_std(Config) ->
+ TC = general_level_std,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "io_test_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC},
+ {verbosity,50}], Config),
+ ok = execute(TC, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+general_level_hi(Config) ->
+ TC = general_level_high,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "io_test_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC},
+ {verbosity,100}], Config),
+ ok = execute(TC, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+change_default(Config) ->
+ TC = change_default,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "io_test_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC},
+ {verbosity,[{default,49}]}], Config),
+ ok = execute(TC, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+combine_categories(Config) ->
+ TC = combine_categories,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "io_test_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC},
+ {verbosity,[{error,?HI_VERBOSITY},
+ {default,?LOW_VERBOSITY}]}], Config),
+ ok = execute(TC, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+testspec_only(Config) ->
+ TC = testspec_only,
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ TestSpec = [{verbosity,[{default,1},{error,75},100]},
+ {suites,DataDir,[io_test_SUITE]},
+ {label,TC}],
+
+ TestSpecName = ct_test_support:write_testspec(TestSpec, PrivDir,
+ "verbosity_1_spec"),
+ {Opts,ERPid} = setup([{spec,TestSpecName}], Config),
+
+ ok = execute(TC, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+merge_with_testspec(Config) ->
+ TC = merge_with_testspec,
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ TestSpec = [{verbosity,[{default,100},{error,100}]},
+ {suites,DataDir,[io_test_SUITE]},
+ {label,TC}],
+
+ TestSpecName = ct_test_support:write_testspec(TestSpec, PrivDir,
+ "verbosity_2_spec"),
+
+ %% below should override verbosity categories in testspec
+ {Opts,ERPid} = setup([{spec,TestSpecName},
+ {verbosity,[{default,0},0]}],
+ Config),
+
+ ok = execute(TC, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+setup(Test, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+execute(Name, Opts, ERPid, Config) ->
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(Name,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(Name),
+ ct_test_support:verify_events(TestEvents, Events, Config).
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
+
+test_events(_) ->
+ [
+ {?eh,tc_done,{io_test_SUITE,tc1,ok}},
+ {?eh,tc_done,{io_test_SUITE,tc2,ok}},
+ {?eh,tc_done,{io_test_SUITE,tc3,ok}},
+
+ {parallel,
+ [
+ {?eh,tc_start,{io_test_SUITE,tc1}},
+ {?eh,tc_start,{io_test_SUITE,tc2}},
+ {?eh,tc_start,{io_test_SUITE,tc3}},
+ {?eh,tc_done,{io_test_SUITE,tc1,ok}},
+ {?eh,tc_done,{io_test_SUITE,tc2,ok}},
+ {?eh,tc_done,{io_test_SUITE,tc3,ok}},
+ {parallel,
+ [
+ {?eh,tc_start,{io_test_SUITE,tc1}},
+ {?eh,tc_start,{io_test_SUITE,tc2}},
+ {?eh,tc_start,{io_test_SUITE,tc3}},
+ {?eh,tc_done,{io_test_SUITE,tc1,ok}},
+ {?eh,tc_done,{io_test_SUITE,tc2,ok}},
+ {?eh,tc_done,{io_test_SUITE,tc3,ok}},
+ {?eh,test_stats,{9,0,{0,0}}}
+ ]}
+ ]},
+
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ].
+
diff --git a/lib/common_test/test/ct_verbosity_SUITE_data/io_test_SUITE.erl b/lib/common_test/test/ct_verbosity_SUITE_data/io_test_SUITE.erl
new file mode 100644
index 0000000000..946e1c1989
--- /dev/null
+++ b/lib/common_test/test/ct_verbosity_SUITE_data/io_test_SUITE.erl
@@ -0,0 +1,156 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(io_test_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,10}}].
+
+%%--------------------------------------------------------------------
+%% @spec init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec init_per_group(GroupName, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1} | {fail,Reason}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec groups() -> [Group]
+%% Group = {GroupName,Properties,GroupsAndTestCases}
+%% GroupName = atom()
+%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
+%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
+%% TestCase = atom()
+%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
+%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
+%% repeat_until_any_ok | repeat_until_any_fail
+%% N = integer() | forever
+%% @end
+%%--------------------------------------------------------------------
+groups() ->
+ [{g1, [parallel], [tc1,tc2,tc3,{group,g2}]},
+ {g2, [parallel], [tc1,tc2,tc3]}].
+
+%%--------------------------------------------------------------------
+%% @spec all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [tc1,tc2,tc3,{group,g1}].
+
+tc1(_C) ->
+ io:format("This is an io:format(~p)~n", [[]]),
+ ct:log("ct:log(default)", []),
+ ct:log(?STD_IMPORTANCE, "ct:log(default,~p)", [?STD_IMPORTANCE]),
+ ct:log(error, "ct:log(error)", []),
+ ct:log(error, ?STD_IMPORTANCE, "ct:log(error,~p)", [?STD_IMPORTANCE]),
+ ct:log(1, "ct:log(default,~p)", [1]),
+ ct:log(error, 1, "ct:log(error,~p)", [1]),
+ ct:log(99, "ct:log(default,~p)", [99]),
+ ct:log(error, 99, "ct:log(error,~p)", [99]),
+ ok.
+
+tc2(_C) ->
+ io:format("This is an io:format(~p)~n", [[]]),
+ ct:pal("ct:pal(default)", []),
+ ct:pal(?STD_IMPORTANCE, "ct:pal(default,~p)", [?STD_IMPORTANCE]),
+ ct:pal(error, "ct:pal(error)", []),
+ ct:pal(error, ?STD_IMPORTANCE, "ct:pal(error,~p)", [?STD_IMPORTANCE]),
+ ct:pal(1, "ct:pal(default,~p)", [1]),
+ ct:pal(error, 1, "ct:pal(error,~p)", [1]),
+ ct:pal(99, "ct:pal(default,~p)", [99]),
+ ct:pal(error, 99, "ct:pal(error,~p)", [99]),
+ ok.
+
+tc3(_C) ->
+ io:format("This is an io:format(~p)~n", [[]]),
+ ct:print("ct:print(default)", []),
+ ct:print(?STD_IMPORTANCE, "ct:print(default,~p)", [?STD_IMPORTANCE]),
+ ct:print(error, "ct:print(error)", []),
+ ct:print(error, ?STD_IMPORTANCE, "ct:print(error,~p)", [?STD_IMPORTANCE]),
+ ct:print(1, "ct:print(default,~p)", [1]),
+ ct:print(error, 1, "ct:print(error,~p)", [1]),
+ ct:print(99, "ct:print(default,~p)", [99]),
+ ct:print(error, 99, "ct:print(error,~p)", [99]),
+ ok.
diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk
index b94f7f7593..5c9fdfc47e 100644
--- a/lib/common_test/vsn.mk
+++ b/lib/common_test/vsn.mk
@@ -1 +1 @@
-COMMON_TEST_VSN = 1.6.1
+COMMON_TEST_VSN = 1.6.2.1
diff --git a/lib/compiler/doc/src/Makefile b/lib/compiler/doc/src/Makefile
index 308e9c4d02..13a73a5d31 100644
--- a/lib/compiler/doc/src/Makefile
+++ b/lib/compiler/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index b577e5ca4f..27d750f929 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -294,6 +294,12 @@ module.beam: module.erl \
describing what it is doing.</p>
</item>
+ <tag><c>{source,FileName}</c></tag>
+ <item>
+ <p>Sets the value of the source, as returned by
+ <c>module_info(compile)</c>.</p>
+ </item>
+
<tag><c>{outdir,Dir}</c></tag>
<item>
<p>Sets a new directory for the object code. The current
@@ -310,7 +316,7 @@ module.beam: module.erl \
<item>
<p>Add <c>Dir</c> to the list of directories to be searched
when including a file. When encountering an
- <c>-include</c> or <c>-include_dir</c> directive,
+ <c>-include</c> or <c>-include_lib</c> directive,
the compiler searches for header files in the following
directories:</p>
<list type="ordered">
diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml
index e2a921a6f2..6d51074d4a 100644
--- a/lib/compiler/doc/src/notes.xml
+++ b/lib/compiler/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,82 @@
<p>This document describes the changes made to the Compiler
application.</p>
+<section><title>Compiler 4.8.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Modules with very many functions would compile very
+ slowly.</p>
+ <p>
+ Own Id: OTP-10123</p>
+ </item>
+ <item>
+ <p>
+ <c>compile:forms/2</c> will now use a
+ {source,SourceFilePath} to set the source returned by
+ <c>module_info(compile)</c> (Thanks to Jos� Valim)</p>
+ <p>
+ Own Id: OTP-10150</p>
+ </item>
+ <item>
+ <p>
+ A process which had enabled trap_exit would receive EXIT
+ messages after calling the compiler. (Thanks to Jeremy
+ Heater.)</p>
+ <p>
+ Own Id: OTP-10171</p>
+ </item>
+ <item>
+ <p>
+ Fix messages ordering with column numbers</p>
+ <p>
+ Own Id: OTP-10183</p>
+ </item>
+ <item>
+ <p>
+ sys_pre_expand: Fix BASE never being set</p>
+ <p>
+ Commit a612e99fb5aaa934fe5a8591db0f083d7fa0b20a turned
+ module attributes from 2-tuples to 3-tuples but forgot to
+ update get_base/1, breaking BASE for parametric modules.</p>
+ <p>
+ Own Id: OTP-10184</p>
+ </item>
+ <item>
+ <p>
+ The compiler will now issue a warning if literal tuple
+ funs are used. For example, {erlang,is_tuple}(X) will now
+ generate a warning.</p>
+ <p>
+ Own Id: OTP-10185</p>
+ </item>
+ <item>
+ <p>
+ The compiler will now warn for illegal sizes for segments
+ in binary construction. For example,
+ &lt;&lt;X:(2.5)&gt;&gt; will now cause the compiler to
+ issue a warning.</p>
+ <p>
+ Own Id: OTP-10197</p>
+ </item>
+ <item>
+ <p>
+ Fix the erlc -MP flag</p>
+ <p>
+ Because of a copy-and-paste error in erlc.c, the -MP flag
+ had the same effect as -MG. As a workaround, you had to
+ pass +makedep_phony to enable the MP option. This patch
+ makes -MP work as intended.</p>
+ <p>
+ Own Id: OTP-10211</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Compiler 4.8.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile
index 56c45d369c..958d3501c7 100644
--- a/lib/compiler/src/Makefile
+++ b/lib/compiler/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl
index 6f0ffb5b25..7392f99fb6 100644
--- a/lib/compiler/src/beam_type.erl
+++ b/lib/compiler/src/beam_type.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. 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
@@ -29,10 +29,17 @@ module({Mod,Exp,Attr,Fs0,Lc}, _Opts) ->
{ok,{Mod,Exp,Attr,Fs,Lc}}.
function({function,Name,Arity,CLabel,Asm0}) ->
- Asm1 = beam_utils:live_opt(Asm0),
- Asm2 = opt(Asm1, [], tdb_new()),
- Asm = beam_utils:delete_live_annos(Asm2),
- {function,Name,Arity,CLabel,Asm}.
+ try
+ Asm1 = beam_utils:live_opt(Asm0),
+ Asm2 = opt(Asm1, [], tdb_new()),
+ Asm = beam_utils:delete_live_annos(Asm2),
+ {function,Name,Arity,CLabel,Asm}
+ catch
+ Class:Error ->
+ Stack = erlang:get_stacktrace(),
+ io:fwrite("Function: ~w/~w\n", [Name,Arity]),
+ erlang:raise(Class, Error, Stack)
+ end.
%% opt([Instruction], Accumulator, TypeDb) -> {[Instruction'],TypeDb'}
%% Keep track of type information; try to simplify.
diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index abcd93f280..194f089ba1 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -741,6 +741,9 @@ live_opt([{badmatch,Src}=I|Is], _, D, Acc) ->
live_opt([{case_end,Src}=I|Is], _, D, Acc) ->
Regs = x_live([Src], 0),
live_opt(Is, Regs, D, [I|Acc]);
+live_opt([{try_case_end,Src}=I|Is], _, D, Acc) ->
+ Regs = x_live([Src], 0),
+ live_opt(Is, Regs, D, [I|Acc]);
live_opt([if_end=I|Is], _, D, Acc) ->
Regs = 0,
live_opt(Is, Regs, D, [I|Acc]);
@@ -802,8 +805,6 @@ live_opt([{deallocate,_}=I|Is], Regs, D, Acc) ->
live_opt(Is, Regs, D, [I|Acc]);
live_opt([{kill,_}=I|Is], Regs, D, Acc) ->
live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{try_case_end,_}=I|Is], Regs, D, Acc) ->
- live_opt(Is, Regs, D, [I|Acc]);
live_opt([{try_end,_}=I|Is], Regs, D, Acc) ->
live_opt(Is, Regs, D, [I|Acc]);
live_opt([{loop_rec_end,_}=I|Is], Regs, D, Acc) ->
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index a52e7bb761..29758b8fb4 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. 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
@@ -783,15 +783,27 @@ valfun_4({bs_utf16_size,{f,Fail},A,Dst}, Vst) ->
valfun_4({bs_bits_to_bytes,{f,Fail},Src,Dst}, Vst) ->
assert_term(Src, Vst),
set_type_reg({integer,[]}, Dst, branch_state(Fail, Vst));
-valfun_4({bs_init2,{f,Fail},_,Heap,Live,_,Dst}, Vst0) ->
+valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
+ if
+ is_integer(Sz) ->
+ ok;
+ true ->
+ assert_term(Sz, Vst0)
+ end,
Vst1 = heap_alloc(Heap, Vst0),
Vst2 = branch_state(Fail, Vst1),
Vst3 = prune_x_regs(Live, Vst2),
Vst = bs_zero_bits(Vst3),
set_type_reg(binary, Dst, Vst);
-valfun_4({bs_init_bits,{f,Fail},_,Heap,Live,_,Dst}, Vst0) ->
+valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
+ if
+ is_integer(Sz) ->
+ ok;
+ true ->
+ assert_term(Sz, Vst0)
+ end,
Vst1 = heap_alloc(Heap, Vst0),
Vst2 = branch_state(Fail, Vst1),
Vst3 = prune_x_regs(Live, Vst2),
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index 9b505ad15c..0a368df5d6 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -146,10 +146,17 @@ env_default_opts() ->
do_compile(Input, Opts0) ->
Opts = expand_opts(Opts0),
- Self = self(),
- Serv = spawn_link(fun() -> internal(Self, Input, Opts) end),
+ {Pid,Ref} =
+ spawn_monitor(fun() ->
+ exit(try
+ internal(Input, Opts)
+ catch
+ error:Reason ->
+ {error,Reason}
+ end)
+ end),
receive
- {Serv,Rep} -> Rep
+ {'DOWN',Ref,process,Pid,Rep} -> Rep
end.
expand_opts(Opts0) ->
@@ -242,15 +249,12 @@ format_error({module_name,Mod,Filename}) ->
errors=[],
warnings=[]}).
-internal(Master, Input, Opts) ->
- Master ! {self(), try internal(Input, Opts)
- catch error:Reason -> {error, Reason}
- end}.
-
-internal({forms,Forms}, Opts) ->
- {_,Ps} = passes(forms, Opts),
- internal_comp(Ps, "", "", #compile{code=Forms,options=Opts,
- mod_options=Opts});
+internal({forms,Forms}, Opts0) ->
+ {_,Ps} = passes(forms, Opts0),
+ Source = proplists:get_value(source, Opts0, ""),
+ Opts1 = proplists:delete(source, Opts0),
+ Compile = #compile{code=Forms,options=Opts1,mod_options=Opts1},
+ internal_comp(Ps, Source, "", Compile);
internal({file,File}, Opts) ->
{Ext,Ps} = passes(file, Opts),
Compile = #compile{options=Opts,mod_options=Opts},
diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl
index 68bc83433e..e55fb2a037 100644
--- a/lib/compiler/src/sys_pre_expand.erl
+++ b/lib/compiler/src/sys_pre_expand.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -119,7 +119,7 @@ expand_pmod(Fs0, St0) ->
get_base(As) ->
case lists:keyfind(extends, 1, As) of
- {extends,[Base]} when is_atom(Base) ->
+ {extends,_,[Base]} when is_atom(Base) ->
Base;
_ ->
[]
@@ -454,9 +454,6 @@ expr({call,Line,{remote,Lr,M,F},As0}, St0) ->
M1 = expand_package(M, St0),
{[M2,F1|As1],St1} = expr_list([M1,F|As0], St0),
{{call,Line,{remote,Lr,M2,F1},As1},St1};
-expr({call,Line,{tuple,_,[{atom,_,_}=M,{atom,_,_}=F]},As}, St) ->
- %% Rewrite {Mod,Function}(Args...) to Mod:Function(Args...).
- expr({call,Line,{remote,Line,M,F},As}, St);
expr({call,Line,F,As0}, St0) ->
{[Fun1|As1],St1} = expr_list([F|As0], St0),
{{call,Line,Fun1,As1},St1};
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 242196c593..01042cc56f 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -823,6 +823,13 @@ bitstr({bin_element,_,E0,Size0,[Type,{unit,Unit}|Flags]}, St0) ->
{_,_} ->
throw(bad_binary)
end,
+ case Size1 of
+ #c_var{} -> ok;
+ #c_literal{val=Sz} when is_integer(Sz), Sz >= 0 -> ok;
+ #c_literal{val=undefined} -> ok;
+ #c_literal{val=all} -> ok;
+ _ -> throw(bad_binary)
+ end,
{#c_bitstr{val=E1,size=Size1,
unit=#c_literal{val=Unit},
type=#c_literal{val=Type},
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index c4e7b45aac..b184987625 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -278,11 +278,12 @@ expr(#c_binary{anno=A,segments=Cv}, Sub, St0) ->
{#k_binary{anno=A,segs=Kv},Ep,St1}
catch
throw:bad_element_size ->
+ St1 = add_warning(get_line(A), bad_segment_size, A, St0),
Erl = #c_literal{val=erlang},
Name = #c_literal{val=error},
Args = [#c_literal{val=badarg}],
Error = #c_call{anno=A,module=Erl,name=Name,args=Args},
- expr(Error, Sub, St0)
+ expr(Error, Sub, St1)
end;
expr(#c_fun{anno=A,vars=Cvs,body=Cb}, Sub0, #kern{ff=OldFF,func=Func}=St0) ->
FA = case OldFF of
@@ -1827,7 +1828,9 @@ format_error({nomatch_shadow,Line}) ->
format_error(nomatch_shadow) ->
"this clause cannot match because a previous clause always matches";
format_error(bad_call) ->
- "invalid module and/or function name; this call will always fail".
+ "invalid module and/or function name; this call will always fail";
+format_error(bad_segment_size) ->
+ "binary construction will fail because of a type mismatch".
add_warning(none, Term, Anno, #kern{ws=Ws}=St) ->
File = get_file(Anno),
diff --git a/lib/compiler/test/bs_construct_SUITE.erl b/lib/compiler/test/bs_construct_SUITE.erl
index 31c7890f26..9ab76449c7 100644
--- a/lib/compiler/test/bs_construct_SUITE.erl
+++ b/lib/compiler/test/bs_construct_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. 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
@@ -468,6 +468,10 @@ opt(Config) when is_list(Config) ->
?line {'EXIT',_} = (catch <<<<23,56,0,2>>:64/float>>),
?line {'EXIT',_} = (catch <<<<23,56,0,2:7>>/binary>>),
+ %% Test constant propagation - there should be a warning.
+ BadSz = 2.5,
+ {'EXIT',_} = (catch <<<<N,56,0,2>>:BadSz/binary>>),
+
case id(false) of
true -> ?line opt_dont_call_me();
false -> ok
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index 512fa0e4ac..2cd75944f4 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -25,7 +25,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
app_test/1,
- file_1/1, module_mismatch/1, big_file/1, outdir/1,
+ file_1/1, forms_2/1, module_mismatch/1, big_file/1, outdir/1,
binary/1, makedep/1, cond_and_ifdef/1, listings/1, listings_big/1,
other_output/1, package_forms/1, encrypted_abstr/1,
bad_record_use1/1, bad_record_use2/1, strict_record/1,
@@ -42,7 +42,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [app_test, file_1, module_mismatch, big_file, outdir,
+ [app_test, file_1, forms_2, module_mismatch, big_file, outdir,
binary, makedep, cond_and_ifdef, listings, listings_big,
other_output, package_forms, encrypted_abstr,
{group, bad_record_use}, strict_record,
@@ -76,6 +76,9 @@ app_test(Config) when is_list(Config) ->
file_1(Config) when is_list(Config) ->
?line Dog = test_server:timetrap(test_server:minutes(5)),
+
+ process_flag(trap_exit, true),
+
?line {Simple, Target} = files(Config, "file_1"),
?line {ok, Cwd} = file:get_cwd(),
?line ok = file:set_cwd(filename:dirname(Target)),
@@ -102,9 +105,37 @@ file_1(Config) when is_list(Config) ->
%% Cleanup.
?line ok = file:delete(Target),
?line ok = file:del_dir(filename:dirname(Target)),
+
+ %% There should not be any messages in the messages.
+ receive
+ Any ->
+ ?t:fail({unexpected,Any})
+ after 10 ->
+ ok
+ end,
+
?line test_server:timetrap_cancel(Dog),
ok.
+forms_2(Config) when is_list(Config) ->
+ Src = "/foo/bar",
+ AbsSrc = filename:absname(Src),
+ {ok,simple,Binary} = compile:forms([{attribute,1,module,simple}],
+ [binary,{source,Src}]),
+ code:load_binary(simple, Src, Binary),
+ Info = simple:module_info(compile),
+
+ %% Test that the proper source is returned.
+ AbsSrc = proplists:get_value(source, Info),
+
+ %% Ensure that the options are not polluted with 'source'.
+ [] = proplists:get_value(options, Info),
+
+ %% Cleanup.
+ true = code:delete(simple),
+ false = code:purge(simple),
+ ok.
+
module_mismatch(Config) when is_list(Config) ->
?line DataDir = ?config(data_dir, Config),
?line File = filename:join(DataDir, "wrong_module_name.erl"),
@@ -211,6 +242,12 @@ makedep(Config) when is_list(Config) ->
[makedep,{makedep_output,Target}|IncludeOptions]),
?line {ok,Mf6} = file:read_file(Target),
?line BasicMf2 = makedep_canonicalize_result(Mf6, DataDir),
+ %% Rule with creating phony target.
+ ?line PhonyMfName = SimpleRootname ++ "-phony.mk",
+ ?line {ok,PhonyMf} = file:read_file(PhonyMfName),
+ ?line {ok,_,Mf7} = compile:file(Simple,
+ [binary,makedep,makedep_phony|IncludeOptions]),
+ ?line PhonyMf = makedep_canonicalize_result(Mf7, DataDir),
?line ok = file:delete(Target),
?line ok = file:del_dir(filename:dirname(Target)),
diff --git a/lib/compiler/test/compile_SUITE_data/simple-phony.mk b/lib/compiler/test/compile_SUITE_data/simple-phony.mk
new file mode 100644
index 0000000000..c84bcedd2a
--- /dev/null
+++ b/lib/compiler/test/compile_SUITE_data/simple-phony.mk
@@ -0,0 +1,3 @@
+simple.beam: $(srcdir)/simple.erl $(srcdir)/include/simple.hrl
+
+$(srcdir)/include/simple.hrl:
diff --git a/lib/compiler/test/error_SUITE.erl b/lib/compiler/test/error_SUITE.erl
index eb5e50818e..fb51e013ce 100644
--- a/lib/compiler/test/error_SUITE.erl
+++ b/lib/compiler/test/error_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2012. 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/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S b/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S
index c0bf04ed8f..cffb792920 100644
--- a/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S
+++ b/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S
@@ -19,10 +19,10 @@
{get_tuple_element,{x,0},1,{x,2}}.
{get_tuple_element,{x,0},2,{x,3}}.
{get_tuple_element,{x,0},3,{x,4}}.
- {gc_bif,'+',{f,0},5,[{x,1},{x,2}],{x,0}}.
- {gc_bif,'+',{f,0},5,[{x,0},{x,3}],{x,0}}.
- {gc_bif,'+',{f,0},5,[{x,0},{x,4}],{x,0}}.
- {gc_bif,'+',{f,0},5,[{x,0},{x,5}],{x,0}}.
+ {gc_bif,'+',{f,0},6,[{x,1},{x,2}],{x,0}}.
+ {gc_bif,'+',{f,0},6,[{x,0},{x,3}],{x,0}}.
+ {gc_bif,'+',{f,0},6,[{x,0},{x,4}],{x,0}}.
+ {gc_bif,'+',{f,0},6,[{x,0},{x,5}],{x,0}}.
return.
{label,3}.
{badmatch,{x,0}}.
diff --git a/lib/compiler/test/misc_SUITE.erl b/lib/compiler/test/misc_SUITE.erl
index 5e13a93c52..0376c7ef3e 100644
--- a/lib/compiler/test/misc_SUITE.erl
+++ b/lib/compiler/test/misc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. 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
@@ -190,6 +190,15 @@ silly_coverage(Config) when is_list(Config) ->
{label,2}|non_proper_list]}],99},
?line expect_error(fun() -> beam_block:module(BlockInput, []) end),
+ %% beam_type
+ TypeInput = {?MODULE,[{foo,0}],[],
+ [{function,foo,0,2,
+ [{label,1},
+ {line,loc},
+ {func_info,{atom,?MODULE},{atom,foo},0},
+ {label,2}|non_proper_list]}],99},
+ expect_error(fun() -> beam_type:module(TypeInput, []) end),
+
%% beam_except
ExceptInput = {?MODULE,[{foo,0}],[],
[{function,foo,0,2,
diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk
index c9c28cf9a2..b77c82309f 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 4.8.1
+COMPILER_VSN = 4.8.2
diff --git a/lib/cosEvent/doc/src/Makefile b/lib/cosEvent/doc/src/Makefile
index 4a0a7429e9..1b867852e4 100644
--- a/lib/cosEvent/doc/src/Makefile
+++ b/lib/cosEvent/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/cosEvent/src/Makefile b/lib/cosEvent/src/Makefile
index 1825beacc5..7787fad786 100644
--- a/lib/cosEvent/src/Makefile
+++ b/lib/cosEvent/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/cosEvent/test/Makefile b/lib/cosEvent/test/Makefile
index d5350d8293..c5afcdb7e4 100644
--- a/lib/cosEvent/test/Makefile
+++ b/lib/cosEvent/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/cosEventDomain/doc/src/Makefile b/lib/cosEventDomain/doc/src/Makefile
index 4e54b0fb55..f820ee5dc0 100644
--- a/lib/cosEventDomain/doc/src/Makefile
+++ b/lib/cosEventDomain/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. 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/lib/cosEventDomain/src/Makefile b/lib/cosEventDomain/src/Makefile
index 41a31b332f..213d433c72 100644
--- a/lib/cosEventDomain/src/Makefile
+++ b/lib/cosEventDomain/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. 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/lib/cosEventDomain/test/Makefile b/lib/cosEventDomain/test/Makefile
index 9349067eb5..5b59cca401 100644
--- a/lib/cosEventDomain/test/Makefile
+++ b/lib/cosEventDomain/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. 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/lib/cosFileTransfer/doc/src/Makefile b/lib/cosFileTransfer/doc/src/Makefile
index 706dec00ce..d1c6616f94 100644
--- a/lib/cosFileTransfer/doc/src/Makefile
+++ b/lib/cosFileTransfer/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/cosFileTransfer/src/Makefile b/lib/cosFileTransfer/src/Makefile
index 1bb765db73..d552349ede 100644
--- a/lib/cosFileTransfer/src/Makefile
+++ b/lib/cosFileTransfer/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/cosFileTransfer/test/Makefile b/lib/cosFileTransfer/test/Makefile
index 177ad7616b..35f6cc1ac2 100644
--- a/lib/cosFileTransfer/test/Makefile
+++ b/lib/cosFileTransfer/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/cosFileTransfer/test/fileTransfer_SUITE.erl b/lib/cosFileTransfer/test/fileTransfer_SUITE.erl
index 79a234bd28..18a591a7af 100644
--- a/lib/cosFileTransfer/test/fileTransfer_SUITE.erl
+++ b/lib/cosFileTransfer/test/fileTransfer_SUITE.erl
@@ -292,13 +292,8 @@ fts_ftp_file_api(Config) ->
fts_ftp_file_ssl_api(doc) -> ["CosFileTransfer FTP FileTransferSession API tests.", ""];
fts_ftp_file_ssl_api(suite) -> [];
fts_ftp_file_ssl_api(Config) ->
- case os:type() of
- vxworks ->
- {skipped, "No SSL-support for VxWorks."};
- _ ->
- ?line {ok, Node} = create_node("ftp_file_api_ssl", {4005, 1}, ssl),
- file_helper(Config, 'FTP', ?TEST_DIR, Node, 4005, "ftp_file_api_ssl", ssl)
- end.
+ ?line {ok, Node} = create_node("ftp_file_api_ssl", {4005, 1}, ssl),
+ file_helper(Config, 'FTP', ?TEST_DIR, Node, 4005, "ftp_file_api_ssl", ssl).
fts_native_file_api(doc) -> ["CosFileTransfer NATIVE FileTransferSession API tests.", ""];
fts_native_file_api(suite) -> [];
@@ -311,15 +306,10 @@ fts_native_file_api(Config) ->
fts_native_file_ssl_api(doc) -> ["CosFileTransfer NATIVE FileTransferSession API tests.", ""];
fts_native_file_ssl_api(suite) -> [];
fts_native_file_ssl_api(Config) ->
- case os:type() of
- vxworks ->
- {skipped, "No SSL-support for VxWorks."};
- _ ->
- ?line {ok, Node} = create_node("native_file_ssl_api", {4007, 1}, ssl),
- {ok, Pwd} = file:get_cwd(),
- file_helper(Config,{'NATIVE', 'cosFileTransferNATIVE_file'},filename:split(Pwd),
- Node, 4007, "native_file_ssl_api", ssl)
- end.
+ ?line {ok, Node} = create_node("native_file_ssl_api", {4007, 1}, ssl),
+ {ok, Pwd} = file:get_cwd(),
+ file_helper(Config,{'NATIVE', 'cosFileTransferNATIVE_file'},filename:split(Pwd),
+ Node, 4007, "native_file_ssl_api", ssl).
@@ -817,23 +807,13 @@ create_node(Name, Port, Retries, Type, Args, Options) ->
end.
starter(Host, Name, Args) ->
- case os:type() of
- vxworks ->
- test_server:start_node(Name, slave, [{args,Args}]);
- _ ->
- slave:start(Host, Name, Args)
- end.
+ slave:start(Host, Name, Args).
slave_sup() ->
process_flag(trap_exit, true),
receive
{'EXIT', _, _} ->
- case os:type() of
- vxworks ->
- erlang:halt();
- _ ->
- ignore
- end
+ ignore
end.
@@ -850,12 +830,7 @@ destroy_node(Node, Type) ->
stopper(Node, Type) ->
catch stop_orber_remote(Node, Type),
- case os:type() of
- vxworks ->
- test_server:stop_node(Node);
- _ ->
- slave:stop(Node)
- end.
+ slave:stop(Node).
-endif.
%%------------------------------------------------------------
diff --git a/lib/cosNotification/doc/src/Makefile b/lib/cosNotification/doc/src/Makefile
index d970818928..800d633909 100644
--- a/lib/cosNotification/doc/src/Makefile
+++ b/lib/cosNotification/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/cosNotification/src/Makefile b/lib/cosNotification/src/Makefile
index 7b8b73aa11..decf598bbf 100644
--- a/lib/cosNotification/src/Makefile
+++ b/lib/cosNotification/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/cosNotification/test/Makefile b/lib/cosNotification/test/Makefile
index 17b60a8e9f..8c18ae7956 100644
--- a/lib/cosNotification/test/Makefile
+++ b/lib/cosNotification/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/cosProperty/doc/src/Makefile b/lib/cosProperty/doc/src/Makefile
index 9bfacc27bd..1624479808 100644
--- a/lib/cosProperty/doc/src/Makefile
+++ b/lib/cosProperty/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/cosProperty/src/Makefile b/lib/cosProperty/src/Makefile
index cb2c56743c..54d1b6021c 100644
--- a/lib/cosProperty/src/Makefile
+++ b/lib/cosProperty/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/cosProperty/test/Makefile b/lib/cosProperty/test/Makefile
index d85b8a655a..ba634c77cf 100644
--- a/lib/cosProperty/test/Makefile
+++ b/lib/cosProperty/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/cosTime/doc/src/Makefile b/lib/cosTime/doc/src/Makefile
index f89c022c4a..2ed55a3bfa 100644
--- a/lib/cosTime/doc/src/Makefile
+++ b/lib/cosTime/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/cosTime/src/Makefile b/lib/cosTime/src/Makefile
index 35f16edcef..e8536a3f0a 100644
--- a/lib/cosTime/src/Makefile
+++ b/lib/cosTime/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/cosTime/test/Makefile b/lib/cosTime/test/Makefile
index ab4d140537..89d5a4a38e 100644
--- a/lib/cosTime/test/Makefile
+++ b/lib/cosTime/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/cosTransactions/doc/src/Makefile b/lib/cosTransactions/doc/src/Makefile
index 4edad9829b..7247c0dec7 100644
--- a/lib/cosTransactions/doc/src/Makefile
+++ b/lib/cosTransactions/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/cosTransactions/examples/Makefile b/lib/cosTransactions/examples/Makefile
index 0408cad75d..348f506e2e 100644
--- a/lib/cosTransactions/examples/Makefile
+++ b/lib/cosTransactions/examples/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/cosTransactions/src/Makefile b/lib/cosTransactions/src/Makefile
index 0922a21641..1d935c4d1d 100644
--- a/lib/cosTransactions/src/Makefile
+++ b/lib/cosTransactions/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/cosTransactions/test/Makefile b/lib/cosTransactions/test/Makefile
index 80f4b6c624..53ff93ba16 100644
--- a/lib/cosTransactions/test/Makefile
+++ b/lib/cosTransactions/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in
index cfc9a447e0..ffd556ca1a 100644
--- a/lib/crypto/c_src/Makefile.in
+++ b/lib/crypto/c_src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 4be593e208..91ab244620 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2012. 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
@@ -53,6 +53,10 @@
#include <openssl/evp.h>
#include <openssl/hmac.h>
+#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_SHA224) && defined(NID_sha224)\
+ && !defined(OPENSSL_NO_SHA256) /* disabled like this in my sha.h (?) */
+# define HAVE_SHA224
+#endif
#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_SHA256) && defined(NID_sha256)
# define HAVE_SHA256
#endif
@@ -135,10 +139,18 @@ static ERL_NIF_TERM sha(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha224_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha224_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha224_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha224_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha256_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha256_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha256_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha256_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha384_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha384_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha384_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha384_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha512_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha512_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha512_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
@@ -149,6 +161,10 @@ static ERL_NIF_TERM md4_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
static ERL_NIF_TERM md4_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM md5_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM sha_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha224_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha256_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha384_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM sha512_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM hmac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM hmac_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
@@ -201,12 +217,33 @@ static void dyn_destroy_function(struct CRYPTO_dynlock_value *ptr,
#endif /* OPENSSL_THREADS */
/* helpers */
+static void init_digest_types(ErlNifEnv* env);
static void hmac_md5(unsigned char *key, int klen,
unsigned char *dbuf, int dlen,
unsigned char *hmacbuf);
static void hmac_sha1(unsigned char *key, int klen,
unsigned char *dbuf, int dlen,
unsigned char *hmacbuf);
+#ifdef HAVE_SHA224
+static void hmac_sha224(unsigned char *key, int klen,
+ unsigned char *dbuf, int dlen,
+ unsigned char *hmacbuf);
+#endif
+#ifdef HAVE_SHA256
+static void hmac_sha256(unsigned char *key, int klen,
+ unsigned char *dbuf, int dlen,
+ unsigned char *hmacbuf);
+#endif
+#ifdef HAVE_SHA384
+static void hmac_sha384(unsigned char *key, int klen,
+ unsigned char *dbuf, int dlen,
+ unsigned char *hmacbuf);
+#endif
+#ifdef HAVE_SHA512
+static void hmac_sha512(unsigned char *key, int klen,
+ unsigned char *dbuf, int dlen,
+ unsigned char *hmacbuf);
+#endif
static int library_refc = 0; /* number of users of this dynamic library */
@@ -220,10 +257,18 @@ static ErlNifFunc nif_funcs[] = {
{"sha_init", 0, sha_init},
{"sha_update", 2, sha_update},
{"sha_final", 1, sha_final},
+ {"sha224_nif", 1, sha224_nif},
+ {"sha224_init_nif", 0, sha224_init_nif},
+ {"sha224_update_nif", 2, sha224_update_nif},
+ {"sha224_final_nif", 1, sha224_final_nif},
{"sha256_nif", 1, sha256_nif},
{"sha256_init_nif", 0, sha256_init_nif},
{"sha256_update_nif", 2, sha256_update_nif},
{"sha256_final_nif", 1, sha256_final_nif},
+ {"sha384_nif", 1, sha384_nif},
+ {"sha384_init_nif", 0, sha384_init_nif},
+ {"sha384_update_nif", 2, sha384_update_nif},
+ {"sha384_final_nif", 1, sha384_final_nif},
{"sha512_nif", 1, sha512_nif},
{"sha512_init_nif", 0, sha512_init_nif},
{"sha512_update_nif", 2, sha512_update_nif},
@@ -234,6 +279,10 @@ static ErlNifFunc nif_funcs[] = {
{"md4_final", 1, md4_final},
{"md5_mac_n", 3, md5_mac_n},
{"sha_mac_n", 3, sha_mac_n},
+ {"sha224_mac_nif", 3, sha224_mac_nif},
+ {"sha256_mac_nif", 3, sha256_mac_nif},
+ {"sha384_mac_nif", 3, sha384_mac_nif},
+ {"sha512_mac_nif", 3, sha512_mac_nif},
{"hmac_init", 2, hmac_init},
{"hmac_update", 2, hmac_update},
{"hmac_final", 1, hmac_final},
@@ -287,10 +336,12 @@ ERL_NIF_INIT(crypto,nif_funcs,load,reload,upgrade,unload)
#define SHA_CTX_LEN (sizeof(SHA_CTX))
#define SHA_LEN 20
#define SHA_LEN_96 12
+#define SHA224_LEN (224/8)
#define SHA256_LEN (256/8)
#define SHA384_LEN (384/8)
#define SHA512_LEN (512/8)
#define HMAC_INT_LEN 64
+#define HMAC_INT2_LEN 128
#define HMAC_IPAD 0x36
#define HMAC_OPAD 0x5c
@@ -300,6 +351,7 @@ static ErlNifRWLock** lock_vec = NULL; /* Static locks used by openssl */
static ERL_NIF_TERM atom_true;
static ERL_NIF_TERM atom_false;
static ERL_NIF_TERM atom_sha;
+static ERL_NIF_TERM atom_sha224;
static ERL_NIF_TERM atom_sha256;
static ERL_NIF_TERM atom_sha384;
static ERL_NIF_TERM atom_sha512;
@@ -320,6 +372,7 @@ static ERL_NIF_TERM atom_check_failed;
static ERL_NIF_TERM atom_unknown;
static ERL_NIF_TERM atom_none;
static ERL_NIF_TERM atom_notsup;
+static ERL_NIF_TERM atom_digest;
static int is_ok_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info)
@@ -374,6 +427,7 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
atom_true = enif_make_atom(env,"true");
atom_false = enif_make_atom(env,"false");
atom_sha = enif_make_atom(env,"sha");
+ atom_sha224 = enif_make_atom(env,"sha224");
atom_sha256 = enif_make_atom(env,"sha256");
atom_sha384 = enif_make_atom(env,"sha384");
atom_sha512 = enif_make_atom(env,"sha512");
@@ -393,6 +447,9 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
atom_unknown = enif_make_atom(env,"unknown");
atom_none = enif_make_atom(env,"none");
atom_notsup = enif_make_atom(env,"notsup");
+ atom_digest = enif_make_atom(env,"digest");
+
+ init_digest_types(env);
*priv_data = NULL;
library_refc++;
@@ -557,6 +614,67 @@ static ERL_NIF_TERM sha_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
return ret;
}
+static ERL_NIF_TERM sha224_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Data) */
+#ifdef HAVE_SHA224
+ ErlNifBinary ibin;
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) {
+ return enif_make_badarg(env);
+ }
+ SHA224((unsigned char *) ibin.data, ibin.size,
+ enif_make_new_binary(env,SHA224_LEN, &ret));
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+static ERL_NIF_TERM sha224_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* () */
+#ifdef HAVE_SHA224
+ ERL_NIF_TERM ret;
+ SHA224_Init((SHA256_CTX *) enif_make_new_binary(env, sizeof(SHA256_CTX), &ret));
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+static ERL_NIF_TERM sha224_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Context, Data) */
+#ifdef HAVE_SHA224
+ SHA256_CTX* new_ctx;
+ ErlNifBinary ctx_bin, data_bin;
+ ERL_NIF_TERM ret;
+ if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA256_CTX)
+ || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) {
+ return enif_make_badarg(env);
+ }
+ new_ctx = (SHA256_CTX*) enif_make_new_binary(env,sizeof(SHA256_CTX), &ret);
+ memcpy(new_ctx, ctx_bin.data, sizeof(SHA256_CTX));
+ SHA224_Update(new_ctx, data_bin.data, data_bin.size);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+static ERL_NIF_TERM sha224_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Context) */
+#ifdef HAVE_SHA224
+ ErlNifBinary ctx_bin;
+ SHA256_CTX ctx_clone;
+ ERL_NIF_TERM ret;
+ if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA256_CTX)) {
+ return enif_make_badarg(env);
+ }
+ memcpy(&ctx_clone, ctx_bin.data, sizeof(SHA256_CTX)); /* writable */
+ SHA224_Final(enif_make_new_binary(env, SHA224_LEN, &ret), &ctx_clone);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+
static ERL_NIF_TERM sha256_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Data) */
#ifdef HAVE_SHA256
@@ -618,6 +736,67 @@ static ERL_NIF_TERM sha256_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TER
#endif
}
+static ERL_NIF_TERM sha384_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Data) */
+#ifdef HAVE_SHA384
+ ErlNifBinary ibin;
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) {
+ return enif_make_badarg(env);
+ }
+ SHA384((unsigned char *) ibin.data, ibin.size,
+ enif_make_new_binary(env,SHA384_LEN, &ret));
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+static ERL_NIF_TERM sha384_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* () */
+#ifdef HAVE_SHA384
+ ERL_NIF_TERM ret;
+ SHA384_Init((SHA512_CTX *) enif_make_new_binary(env, sizeof(SHA512_CTX), &ret));
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+static ERL_NIF_TERM sha384_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Context, Data) */
+#ifdef HAVE_SHA384
+ SHA512_CTX* new_ctx;
+ ErlNifBinary ctx_bin, data_bin;
+ ERL_NIF_TERM ret;
+ if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA512_CTX)
+ || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) {
+ return enif_make_badarg(env);
+ }
+ new_ctx = (SHA512_CTX*) enif_make_new_binary(env,sizeof(SHA512_CTX), &ret);
+ memcpy(new_ctx, ctx_bin.data, sizeof(SHA512_CTX));
+ SHA384_Update(new_ctx, data_bin.data, data_bin.size);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+static ERL_NIF_TERM sha384_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Context) */
+#ifdef HAVE_SHA384
+ ErlNifBinary ctx_bin;
+ SHA512_CTX ctx_clone;
+ ERL_NIF_TERM ret;
+ if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA512_CTX)) {
+ return enif_make_badarg(env);
+ }
+ memcpy(&ctx_clone, ctx_bin.data, sizeof(SHA512_CTX)); /* writable */
+ SHA384_Final(enif_make_new_binary(env, SHA384_LEN, &ret), &ctx_clone);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+
static ERL_NIF_TERM sha512_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Data) */
#ifdef HAVE_SHA512
@@ -760,6 +939,95 @@ static ERL_NIF_TERM sha_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
return ret;
}
+static ERL_NIF_TERM sha224_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Key, Data, MacSize) */
+#ifdef HAVE_SHA224
+ unsigned char hmacbuf[SHA224_DIGEST_LENGTH];
+ ErlNifBinary key, data;
+ unsigned mac_sz;
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
+ || !enif_inspect_iolist_as_binary(env, argv[1], &data)
+ || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA224_DIGEST_LENGTH) {
+ return enif_make_badarg(env);
+ }
+ hmac_sha224(key.data, key.size, data.data, data.size, hmacbuf);
+ memcpy(enif_make_new_binary(env, mac_sz, &ret),
+ hmacbuf, mac_sz);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM sha256_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Key, Data, MacSize) */
+#ifdef HAVE_SHA256
+ unsigned char hmacbuf[SHA256_DIGEST_LENGTH];
+ ErlNifBinary key, data;
+ unsigned mac_sz;
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
+ || !enif_inspect_iolist_as_binary(env, argv[1], &data)
+ || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA256_DIGEST_LENGTH) {
+ return enif_make_badarg(env);
+ }
+ hmac_sha256(key.data, key.size, data.data, data.size, hmacbuf);
+ memcpy(enif_make_new_binary(env, mac_sz, &ret),
+ hmacbuf, mac_sz);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM sha384_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Key, Data, MacSize) */
+#ifdef HAVE_SHA384
+ unsigned char hmacbuf[SHA384_DIGEST_LENGTH];
+ ErlNifBinary key, data;
+ unsigned mac_sz;
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
+ || !enif_inspect_iolist_as_binary(env, argv[1], &data)
+ || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA384_DIGEST_LENGTH) {
+ return enif_make_badarg(env);
+ }
+ hmac_sha384(key.data, key.size, data.data, data.size, hmacbuf);
+ memcpy(enif_make_new_binary(env, mac_sz, &ret),
+ hmacbuf, mac_sz);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+
+
+static ERL_NIF_TERM sha512_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Key, Data, MacSize) */
+#ifdef HAVE_SHA512
+ unsigned char hmacbuf[SHA512_DIGEST_LENGTH];
+ ErlNifBinary key, data;
+ unsigned mac_sz;
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
+ || !enif_inspect_iolist_as_binary(env, argv[1], &data)
+ || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA512_DIGEST_LENGTH) {
+ return enif_make_badarg(env);
+ }
+ hmac_sha512(key.data, key.size, data.data, data.size, hmacbuf);
+ memcpy(enif_make_new_binary(env, mac_sz, &ret),
+ hmacbuf, mac_sz);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+
static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Type, Key) */
ErlNifBinary key;
@@ -768,6 +1036,18 @@ static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
const EVP_MD *md;
if (argv[0] == atom_sha) md = EVP_sha1();
+#ifdef HAVE_SHA224
+ else if (argv[0] == atom_sha224) md = EVP_sha224();
+#endif
+#ifdef HAVE_SHA256
+ else if (argv[0] == atom_sha256) md = EVP_sha256();
+#endif
+#ifdef HAVE_SHA384
+ else if (argv[0] == atom_sha384) md = EVP_sha384();
+#endif
+#ifdef HAVE_SHA512
+ else if (argv[0] == atom_sha512) md = EVP_sha512();
+#endif
else if (argv[0] == atom_md5) md = EVP_md5();
else if (argv[0] == atom_ripemd160) md = EVP_ripemd160();
else goto badarg;
@@ -954,8 +1234,7 @@ static ERL_NIF_TERM aes_cfb_128_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TE
if (!enif_inspect_iolist_as_binary(env, argv[0], &key) || key.size != 16
|| !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 16
- || !enif_inspect_iolist_as_binary(env, argv[2], &text)
- || text.size % 16 != 0) {
+ || !enif_inspect_iolist_as_binary(env, argv[2], &text)) {
return enif_make_badarg(env);
}
@@ -1208,14 +1487,43 @@ static int inspect_mpint(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifBinary* bin)
}
static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (DigestType,Data,Signature,Key=[P, Q, G, Y]) */
+{/* (DigestType|none, Data|{digest,Digest}, Signature,Key=[P, Q, G, Y]) */
ErlNifBinary data_bin, sign_bin;
BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL, *dsa_y = NULL;
unsigned char hmacbuf[SHA_DIGEST_LENGTH];
+ unsigned char* digest;
ERL_NIF_TERM head, tail;
+ const ERL_NIF_TERM* tpl_terms;
+ int tpl_arity;
DSA *dsa;
int i;
+ if (argv[0] == atom_sha) {
+ if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) {
+ if (tpl_arity != 2 || tpl_terms[0] != atom_digest
+ || !enif_inspect_binary(env, tpl_terms[1], &data_bin)
+ || data_bin.size != SHA_DIGEST_LENGTH) {
+
+ return enif_make_badarg(env);
+ }
+ digest = data_bin.data;
+ }
+ else {
+ if (!inspect_mpint(env, argv[1], &data_bin)) {
+ return enif_make_badarg(env);
+ }
+ SHA1(data_bin.data+4, data_bin.size-4, hmacbuf);
+ digest = hmacbuf;
+ }
+ }
+ else if (argv[0] == atom_none && enif_inspect_binary(env, argv[1], &data_bin)
+ && data_bin.size == SHA_DIGEST_LENGTH) {
+ digest = data_bin.data;
+ }
+ else {
+ return enif_make_badarg(env);
+ }
+
if (!inspect_mpint(env, argv[2], &sign_bin)
|| !enif_get_list_cell(env, argv[3], &head, &tail)
|| !get_bn_from_mpint(env, head, &dsa_p)
@@ -1226,23 +1534,13 @@ static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
|| !enif_get_list_cell(env, tail, &head, &tail)
|| !get_bn_from_mpint(env, head, &dsa_y)
|| !enif_is_empty_list(env,tail)) {
- badarg:
+
if (dsa_p) BN_free(dsa_p);
if (dsa_q) BN_free(dsa_q);
if (dsa_g) BN_free(dsa_g);
if (dsa_y) BN_free(dsa_y);
return enif_make_badarg(env);
}
- if (argv[0] == atom_sha && inspect_mpint(env, argv[1], &data_bin)) {
- SHA1(data_bin.data+4, data_bin.size-4, hmacbuf);
- }
- else if (argv[0] == atom_none && enif_inspect_binary(env, argv[1], &data_bin)
- && data_bin.size == SHA_DIGEST_LENGTH) {
- memcpy(hmacbuf, data_bin.data, SHA_DIGEST_LENGTH);
- }
- else {
- goto badarg;
- }
dsa = DSA_new();
dsa->p = dsa_p;
@@ -1250,23 +1548,134 @@ static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
dsa->g = dsa_g;
dsa->priv_key = NULL;
dsa->pub_key = dsa_y;
- i = DSA_verify(0, hmacbuf, SHA_DIGEST_LENGTH,
+ i = DSA_verify(0, digest, SHA_DIGEST_LENGTH,
sign_bin.data+4, sign_bin.size-4, dsa);
DSA_free(dsa);
return(i > 0) ? atom_true : atom_false;
}
+
+static void md5_digest(unsigned char* in, unsigned int in_len, unsigned char* out)
+{
+ MD5(in, in_len, out);
+}
+static void sha1_digest(unsigned char* in, unsigned int in_len, unsigned char* out)
+{
+ SHA1(in, in_len, out);
+}
+#ifdef HAVE_SHA224
+static void sha224_digest(unsigned char* in, unsigned int in_len, unsigned char* out)
+{
+ SHA224(in, in_len, out);
+}
+#endif
+#ifdef HAVE_SHA256
+static void sha256_digest(unsigned char* in, unsigned int in_len, unsigned char* out)
+{
+ SHA256(in, in_len, out);
+}
+#endif
+#ifdef HAVE_SHA384
+static void sha384_digest(unsigned char* in, unsigned int in_len, unsigned char* out)
+{
+ SHA384(in, in_len, out);
+}
+#endif
+#ifdef HAVE_SHA512
+static void sha512_digest(unsigned char* in, unsigned int in_len, unsigned char* out)
+{
+ SHA512(in, in_len, out);
+}
+#endif
+
+struct digest_type_t {
+ const char* type_str;
+ unsigned len; /* 0 if notsup */
+ int NID_type;
+ void (*funcp)(unsigned char* in, unsigned int in_len, unsigned char* out);
+ ERL_NIF_TERM type_atom;
+};
+
+struct digest_type_t digest_types[] =
+{
+ {"md5", MD5_DIGEST_LENGTH, NID_md5, md5_digest},
+ {"sha", SHA_DIGEST_LENGTH, NID_sha1, sha1_digest},
+ {"sha224",
+#ifdef HAVE_SHA224
+ SHA224_LEN, NID_sha224, sha224_digest
+#else
+ 0
+#endif
+ },
+ {"sha256",
+#ifdef HAVE_SHA256
+ SHA256_LEN, NID_sha256, sha256_digest
+#else
+ 0
+#endif
+ },
+ {"sha384",
+#ifdef HAVE_SHA384
+ SHA384_LEN, NID_sha384, sha384_digest
+#else
+ 0
+#endif
+ },
+ {"sha512",
+#ifdef HAVE_SHA512
+ SHA512_LEN, NID_sha512, sha512_digest
+#else
+ 0
+#endif
+ },
+ {NULL}
+};
+
+static void init_digest_types(ErlNifEnv* env)
+{
+ struct digest_type_t* p = digest_types;
+
+ for (p = digest_types; p->type_str; p++) {
+ p->type_atom = enif_make_atom(env, p->type_str);
+ }
+
+}
+
+static struct digest_type_t* get_digest_type(ERL_NIF_TERM type)
+{
+ struct digest_type_t* p = NULL;
+ for (p = digest_types; p->type_str; p++) {
+ if (type == p->type_atom) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
static ERL_NIF_TERM rsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Type, Data, Signature, Key=[E,N]) */
+{/* (Type, Data|{digest,Digest}, Signature, Key=[E,N]) */
ErlNifBinary data_bin, sign_bin;
unsigned char hmacbuf[SHA512_LEN];
ERL_NIF_TERM head, tail, ret;
int i;
- RSA* rsa = RSA_new();
+ RSA* rsa;
const ERL_NIF_TERM type = argv[0];
+ const ERL_NIF_TERM* tpl_terms;
+ int tpl_arity;
+ struct digest_type_t* digp = NULL;
+ unsigned char* digest = NULL;
+
+ digp = get_digest_type(type);
+ if (!digp) {
+ return enif_make_badarg(env);
+ }
+ if (!digp->len) {
+ return atom_notsup;
+ }
- if (!inspect_mpint(env, argv[1], &data_bin)
- || !inspect_mpint(env, argv[2], &sign_bin)
+ rsa = RSA_new();
+
+ if (!inspect_mpint(env, argv[2], &sign_bin)
|| !enif_get_list_cell(env, argv[3], &head, &tail)
|| !get_bn_from_mpint(env, head, &rsa->e)
|| !enif_get_list_cell(env, tail, &head, &tail)
@@ -1274,59 +1683,38 @@ static ERL_NIF_TERM rsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
|| !enif_is_empty_list(env, tail)) {
ret = enif_make_badarg(env);
+ goto done;
}
- else {
- if (type == atom_sha) {
- SHA1(data_bin.data+4, data_bin.size-4, hmacbuf);
- i = RSA_verify(NID_sha1, hmacbuf, SHA_DIGEST_LENGTH,
- sign_bin.data+4, sign_bin.size-4, rsa);
- }
- else if (type == atom_sha256) {
- #ifdef HAVE_SHA256
- SHA256(data_bin.data+4, data_bin.size-4, hmacbuf);
- i = RSA_verify(NID_sha256, hmacbuf, SHA256_LEN,
- sign_bin.data+4, sign_bin.size-4, rsa);
- #else
- ret = atom_notsup;
- goto done;
- #endif
- }
- else if (type == atom_sha384) {
- #ifdef HAVE_SHA384
- SHA384(data_bin.data+4, data_bin.size-4, hmacbuf);
- i = RSA_verify(NID_sha384, hmacbuf, SHA384_LEN,
- sign_bin.data+4, sign_bin.size-4, rsa);
- #else
- ret = atom_notsup;
- goto done;
- #endif
- }
- else if (type == atom_sha512) {
- #ifdef HAVE_SHA512
- SHA512(data_bin.data+4, data_bin.size-4, hmacbuf);
- i = RSA_verify(NID_sha512, hmacbuf, SHA512_LEN,
- sign_bin.data+4, sign_bin.size-4, rsa);
- #else
- ret = atom_notsup;
- goto done;
- #endif
- }
- else if (type == atom_md5) {
- MD5(data_bin.data+4, data_bin.size-4, hmacbuf);
- i = RSA_verify(NID_md5, hmacbuf, MD5_DIGEST_LENGTH,
- sign_bin.data+4, sign_bin.size-4, rsa);
- }
- else {
+ if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) {
+ if (tpl_arity != 2 || tpl_terms[0] != atom_digest
+ || !enif_inspect_binary(env, tpl_terms[1], &data_bin)
+ || data_bin.size != digp->len) {
+
ret = enif_make_badarg(env);
goto done;
}
- ret = (i==1 ? atom_true : atom_false);
- }
+ digest = data_bin.data;
+ }
+ else if (inspect_mpint(env, argv[1], &data_bin)) {
+ digest = hmacbuf;
+ digp->funcp(data_bin.data+4, data_bin.size-4, digest);
+ }
+ else {
+ ret = enif_make_badarg(env);
+ goto done;
+ }
+
+ i = RSA_verify(digp->NID_type, digest, digp->len,
+ sign_bin.data+4, sign_bin.size-4, rsa);
+
+ ret = (i==1 ? atom_true : atom_false);
+
done:
RSA_free(rsa);
return ret;
}
+
static ERL_NIF_TERM aes_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Key, IVec, Data, IsEncrypt) */
ErlNifBinary key_bin, ivec_bin, data_bin;
@@ -1485,40 +1873,59 @@ static int get_rsa_private_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa)
}
static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Type,Data,Key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C]) */
+{/* (Type, Data|{digest,Digest}, Key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C]) */
ErlNifBinary data_bin, ret_bin;
- unsigned char hmacbuf[SHA_DIGEST_LENGTH];
+ unsigned char hmacbuf[SHA512_LEN];
unsigned rsa_s_len;
- RSA *rsa = RSA_new();
- int i, is_sha;
-
- if (argv[0] == atom_sha) is_sha = 1;
- else if (argv[0] == atom_md5) is_sha = 0;
- else goto badarg;
+ RSA* rsa;
+ int i;
+ const ERL_NIF_TERM* tpl_terms;
+ int tpl_arity;
+ struct digest_type_t *digp;
+ unsigned char* digest;
- if (!inspect_mpint(env,argv[1],&data_bin)
- || !get_rsa_private_key(env, argv[2], rsa)) {
- badarg:
- RSA_free(rsa);
+ digp = get_digest_type(argv[0]);
+ if (!digp) {
return enif_make_badarg(env);
}
- enif_alloc_binary(RSA_size(rsa), &ret_bin);
- if (is_sha) {
- SHA1(data_bin.data+4, data_bin.size-4, hmacbuf);
- ERL_VALGRIND_ASSERT_MEM_DEFINED(hmacbuf, SHA_DIGEST_LENGTH);
- i = RSA_sign(NID_sha1, hmacbuf, SHA_DIGEST_LENGTH,
- ret_bin.data, &rsa_s_len, rsa);
+ if (!digp->len) {
+ return atom_notsup;
+ }
+
+ if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) {
+ if (tpl_arity != 2 || tpl_terms[0] != atom_digest
+ || !enif_inspect_binary(env, tpl_terms[1], &data_bin)
+ || data_bin.size != digp->len) {
+
+ return enif_make_badarg(env);
+ }
+ digest = data_bin.data;
}
else {
- MD5(data_bin.data+4, data_bin.size-4, hmacbuf);
- ERL_VALGRIND_ASSERT_MEM_DEFINED(hmacbuf, MD5_DIGEST_LENGTH);
- i = RSA_sign(NID_md5, hmacbuf,MD5_DIGEST_LENGTH,
- ret_bin.data, &rsa_s_len, rsa);
+ if (!inspect_mpint(env,argv[1],&data_bin)) {
+ return enif_make_badarg(env);
+ }
+ digest = hmacbuf;
+ digp->funcp(data_bin.data+4, data_bin.size-4, digest);
+ }
+
+ rsa = RSA_new();
+ if (!get_rsa_private_key(env, argv[2], rsa)) {
+ RSA_free(rsa);
+ return enif_make_badarg(env);
}
+
+
+ enif_alloc_binary(RSA_size(rsa), &ret_bin);
+
+ ERL_VALGRIND_ASSERT_MEM_DEFINED(digest, digp->len);
+ i = RSA_sign(digp->NID_type, digest, digp->len,
+ ret_bin.data, &rsa_s_len, rsa);
+
RSA_free(rsa);
if (i) {
ERL_VALGRIND_MAKE_MEM_DEFINED(ret_bin.data, rsa_s_len);
- if (rsa_s_len != data_bin.size) {
+ if (rsa_s_len != ret_bin.size) {
enif_realloc_binary(&ret_bin, rsa_s_len);
ERL_VALGRIND_ASSERT_MEM_DEFINED(ret_bin.data, rsa_s_len);
}
@@ -1530,15 +1937,49 @@ static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
}
}
+
static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (DigesType, Data, Key=[P,Q,G,PrivKey]) */
+{/* (DigesType|none, Data|{digest,Digest}, Key=[P,Q,G,PrivKey]) */
ErlNifBinary data_bin, ret_bin;
ERL_NIF_TERM head, tail;
unsigned char hmacbuf[SHA_DIGEST_LENGTH];
unsigned int dsa_s_len;
- DSA* dsa = DSA_new();
+ const ERL_NIF_TERM* tpl_terms;
+ int tpl_arity;
+ unsigned char* digest = NULL;
+ DSA* dsa;
int i;
+ if (argv[0] == atom_sha) {
+ if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) {
+ if (tpl_arity != 2 || tpl_terms[0] != atom_digest
+ || !enif_inspect_binary(env, tpl_terms[1], &data_bin)
+ || data_bin.size != SHA_DIGEST_LENGTH) {
+
+ return enif_make_badarg(env);
+ }
+ digest = data_bin.data;
+ }
+ else {
+ if (!inspect_mpint(env,argv[1],&data_bin)) {
+ return enif_make_badarg(env);
+ }
+ SHA1(data_bin.data+4, data_bin.size-4, hmacbuf);
+ digest = hmacbuf;
+ }
+ }
+ else if (argv[0] == atom_none
+ && enif_inspect_binary(env,argv[1],&data_bin)
+ && data_bin.size == SHA_DIGEST_LENGTH) {
+
+ digest = data_bin.data;
+ }
+ else {
+ return enif_make_badarg(env);
+ }
+
+ dsa = DSA_new();
+
dsa->pub_key = NULL;
if (!enif_get_list_cell(env, argv[2], &head, &tail)
|| !get_bn_from_mpint(env, head, &dsa->p)
@@ -1549,23 +1990,12 @@ static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
|| !enif_get_list_cell(env, tail, &head, &tail)
|| !get_bn_from_mpint(env, head, &dsa->priv_key)
|| !enif_is_empty_list(env,tail)) {
- goto badarg;
- }
- if (argv[0] == atom_sha && inspect_mpint(env, argv[1], &data_bin)) {
- SHA1(data_bin.data+4, data_bin.size-4, hmacbuf);
- }
- else if (argv[0] == atom_none && enif_inspect_binary(env,argv[1],&data_bin)
- && data_bin.size == SHA_DIGEST_LENGTH) {
- memcpy(hmacbuf, data_bin.data, SHA_DIGEST_LENGTH);
- }
- else {
- badarg:
DSA_free(dsa);
return enif_make_badarg(env);
}
enif_alloc_binary(DSA_size(dsa), &ret_bin);
- i = DSA_sign(NID_sha1, hmacbuf, SHA_DIGEST_LENGTH,
+ i = DSA_sign(NID_sha1, digest, SHA_DIGEST_LENGTH,
ret_bin.data, &dsa_s_len, dsa);
DSA_free(dsa);
if (i) {
@@ -1579,6 +2009,7 @@ static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
}
}
+
static int rsa_pad(ERL_NIF_TERM term, int* padding)
{
if (term == atom_rsa_pkcs1_padding) {
@@ -2040,3 +2471,166 @@ static void hmac_sha1(unsigned char *key, int klen,
SHA1_Final((unsigned char *) hmacbuf, &ctx);
}
+#ifdef HAVE_SHA224
+static void hmac_sha224(unsigned char *key, int klen,
+ unsigned char *dbuf, int dlen,
+ unsigned char *hmacbuf)
+{
+ SHA256_CTX ctx;
+ char ipad[HMAC_INT_LEN];
+ char opad[HMAC_INT_LEN];
+ unsigned char nkey[SHA224_DIGEST_LENGTH];
+ int i;
+
+ /* Change key if longer than 64 bytes */
+ if (klen > HMAC_INT_LEN) {
+ SHA224(key, klen, nkey);
+ key = nkey;
+ klen = SHA224_DIGEST_LENGTH;
+ }
+
+ memset(ipad, '\0', sizeof(ipad));
+ memset(opad, '\0', sizeof(opad));
+ memcpy(ipad, key, klen);
+ memcpy(opad, key, klen);
+
+ for (i = 0; i < HMAC_INT_LEN; i++) {
+ ipad[i] ^= HMAC_IPAD;
+ opad[i] ^= HMAC_OPAD;
+ }
+
+ /* inner SHA */
+ SHA224_Init(&ctx);
+ SHA224_Update(&ctx, ipad, HMAC_INT_LEN);
+ SHA224_Update(&ctx, dbuf, dlen);
+ SHA224_Final((unsigned char *) hmacbuf, &ctx);
+ /* outer SHA */
+ SHA224_Init(&ctx);
+ SHA224_Update(&ctx, opad, HMAC_INT_LEN);
+ SHA224_Update(&ctx, hmacbuf, SHA224_DIGEST_LENGTH);
+ SHA224_Final((unsigned char *) hmacbuf, &ctx);
+}
+#endif
+
+#ifdef HAVE_SHA256
+static void hmac_sha256(unsigned char *key, int klen,
+ unsigned char *dbuf, int dlen,
+ unsigned char *hmacbuf)
+{
+ SHA256_CTX ctx;
+ char ipad[HMAC_INT_LEN];
+ char opad[HMAC_INT_LEN];
+ unsigned char nkey[SHA256_DIGEST_LENGTH];
+ int i;
+
+ /* Change key if longer than 64 bytes */
+ if (klen > HMAC_INT_LEN) {
+ SHA256(key, klen, nkey);
+ key = nkey;
+ klen = SHA256_DIGEST_LENGTH;
+ }
+
+ memset(ipad, '\0', sizeof(ipad));
+ memset(opad, '\0', sizeof(opad));
+ memcpy(ipad, key, klen);
+ memcpy(opad, key, klen);
+
+ for (i = 0; i < HMAC_INT_LEN; i++) {
+ ipad[i] ^= HMAC_IPAD;
+ opad[i] ^= HMAC_OPAD;
+ }
+
+ /* inner SHA */
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, ipad, HMAC_INT_LEN);
+ SHA256_Update(&ctx, dbuf, dlen);
+ SHA256_Final((unsigned char *) hmacbuf, &ctx);
+ /* outer SHA */
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, opad, HMAC_INT_LEN);
+ SHA256_Update(&ctx, hmacbuf, SHA256_DIGEST_LENGTH);
+ SHA256_Final((unsigned char *) hmacbuf, &ctx);
+}
+#endif
+
+#ifdef HAVE_SHA384
+static void hmac_sha384(unsigned char *key, int klen,
+ unsigned char *dbuf, int dlen,
+ unsigned char *hmacbuf)
+{
+ SHA512_CTX ctx;
+ char ipad[HMAC_INT2_LEN];
+ char opad[HMAC_INT2_LEN];
+ unsigned char nkey[SHA384_DIGEST_LENGTH];
+ int i;
+
+ /* Change key if longer than 64 bytes */
+ if (klen > HMAC_INT2_LEN) {
+ SHA384(key, klen, nkey);
+ key = nkey;
+ klen = SHA384_DIGEST_LENGTH;
+ }
+
+ memset(ipad, '\0', sizeof(ipad));
+ memset(opad, '\0', sizeof(opad));
+ memcpy(ipad, key, klen);
+ memcpy(opad, key, klen);
+
+ for (i = 0; i < HMAC_INT2_LEN; i++) {
+ ipad[i] ^= HMAC_IPAD;
+ opad[i] ^= HMAC_OPAD;
+ }
+
+ /* inner SHA */
+ SHA384_Init(&ctx);
+ SHA384_Update(&ctx, ipad, HMAC_INT2_LEN);
+ SHA384_Update(&ctx, dbuf, dlen);
+ SHA384_Final((unsigned char *) hmacbuf, &ctx);
+ /* outer SHA */
+ SHA384_Init(&ctx);
+ SHA384_Update(&ctx, opad, HMAC_INT2_LEN);
+ SHA384_Update(&ctx, hmacbuf, SHA384_DIGEST_LENGTH);
+ SHA384_Final((unsigned char *) hmacbuf, &ctx);
+}
+#endif
+
+#ifdef HAVE_SHA512
+static void hmac_sha512(unsigned char *key, int klen,
+ unsigned char *dbuf, int dlen,
+ unsigned char *hmacbuf)
+{
+ SHA512_CTX ctx;
+ char ipad[HMAC_INT2_LEN];
+ char opad[HMAC_INT2_LEN];
+ unsigned char nkey[SHA512_DIGEST_LENGTH];
+ int i;
+
+ /* Change key if longer than 64 bytes */
+ if (klen > HMAC_INT2_LEN) {
+ SHA512(key, klen, nkey);
+ key = nkey;
+ klen = SHA512_DIGEST_LENGTH;
+ }
+
+ memset(ipad, '\0', sizeof(ipad));
+ memset(opad, '\0', sizeof(opad));
+ memcpy(ipad, key, klen);
+ memcpy(opad, key, klen);
+
+ for (i = 0; i < HMAC_INT2_LEN; i++) {
+ ipad[i] ^= HMAC_IPAD;
+ opad[i] ^= HMAC_OPAD;
+ }
+
+ /* inner SHA */
+ SHA512_Init(&ctx);
+ SHA512_Update(&ctx, ipad, HMAC_INT2_LEN);
+ SHA512_Update(&ctx, dbuf, dlen);
+ SHA512_Final((unsigned char *) hmacbuf, &ctx);
+ /* outer SHA */
+ SHA512_Init(&ctx);
+ SHA512_Update(&ctx, opad, HMAC_INT2_LEN);
+ SHA512_Update(&ctx, hmacbuf, SHA512_DIGEST_LENGTH);
+ SHA512_Final((unsigned char *) hmacbuf, &ctx);
+}
+#endif
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 19db6c9dd4..4dcd6fc4ea 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1999</year><year>2011</year>
+ <year>1999</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -256,6 +256,57 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
</desc>
</func>
<func>
+ <name>hash(Type, Data) -> Digest</name>
+ <fsummary></fsummary>
+ <type>
+ <v>Type = md4 | md5 | sha | sha224 | sha256 | sha384 | sha512</v>
+ <v>Data = iodata()</v>
+ <v>Digest = binary()</v>
+ </type>
+ <desc>
+ <p>Computes a message digest of type <c>Type</c> from <c>Data</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>hash_init(Type) -> Context</name>
+ <fsummary></fsummary>
+ <type>
+ <v>Type = md4 | md5 | sha | sha224 | sha256 | sha384 | sha512</v>
+ </type>
+ <desc>
+ <p>Initializes the context for streaming hash operations. <c>Type</c> determines
+ which digest to use. The returned context should be used as argument
+ to <seealso marker="#hash_update/2">hash_update</seealso>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>hash_update(Context, Data) -> NewContext</name>
+ <fsummary></fsummary>
+ <type>
+ <v>Data = iodata()</v>
+ </type>
+ <desc>
+ <p>Updates the digest represented by <c>Context</c> using the given <c>Data</c>. <c>Context</c>
+ must have been generated using <seealso marker="#hash_init/1">hash_init</seealso>
+ or a previous call to this function. <c>Data</c> can be any length. <c>NewContext</c>
+ must be passed into the next call to <c>hash_update</c>
+ or <seealso marker="#hash_final/1">hash_final</seealso>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>hash_final(Context) -> Digest</name>
+ <fsummary></fsummary>
+ <type>
+ <v>Digest = binary()</v>
+ </type>
+ <desc>
+ <p>Finalizes the hash operation referenced by <c>Context</c> returned
+ from a previous call to <seealso marker="#hash_update/2">hash_update</seealso>.
+ The size of <c>Digest</c> is determined by the type of hash
+ function used to generate it.</p>
+ </desc>
+ </func>
+ <func>
<name>md5_mac(Key, Data) -> Mac</name>
<fsummary>Compute an <c>MD5 MAC</c>message authentification code</fsummary>
<type>
@@ -643,16 +694,14 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
<func>
<name>aes_cfb_128_encrypt(Key, IVec, Text) -> Cipher</name>
- <name>aes_cbc_128_encrypt(Key, IVec, Text) -> Cipher</name>
- <fsummary>Encrypt <c>Text</c>according to AES in Cipher Feedback mode or Cipher Block Chaining mode</fsummary>
+ <fsummary>Encrypt <c>Text</c>according to AES in Cipher Feedback mode</fsummary>
<type>
<v>Key = Text = iolist() | binary()</v>
<v>IVec = Cipher = binary()</v>
</type>
<desc>
<p>Encrypts <c>Text</c> according to AES in Cipher Feedback
- mode (CFB) or Cipher Block Chaining mode (CBC). <c>Text</c>
- must be a multiple of 128 bits (16 bytes). <c>Key</c> is the
+ mode (CFB). <c>Key</c> is the
AES key, and <c>IVec</c> is an arbitrary initializing vector.
The lengths of <c>Key</c> and <c>IVec</c> must be 128 bits
(16 bytes).</p>
@@ -660,15 +709,45 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
</func>
<func>
<name>aes_cfb_128_decrypt(Key, IVec, Cipher) -> Text</name>
+ <fsummary>Decrypt <c>Cipher</c>according to AES in Cipher Feedback mode</fsummary>
+ <type>
+ <v>Key = Cipher = iolist() | binary()</v>
+ <v>IVec = Text = binary()</v>
+ </type>
+ <desc>
+ <p>Decrypts <c>Cipher</c> according to AES in Cipher Feedback Mode (CFB).
+ <c>Key</c> is the AES key, and <c>IVec</c> is an arbitrary
+ initializing vector. <c>Key</c> and <c>IVec</c> must have
+ the same values as those used when encrypting. The lengths of
+ <c>Key</c> and <c>IVec</c> must be 128 bits (16 bytes).</p>
+ </desc>
+ </func>
+ <func>
+ <name>aes_cbc_128_encrypt(Key, IVec, Text) -> Cipher</name>
+ <fsummary>Encrypt <c>Text</c>according to AES in Cipher Block Chaining mode</fsummary>
+ <type>
+ <v>Key = Text = iolist() | binary()</v>
+ <v>IVec = Cipher = binary()</v>
+ </type>
+ <desc>
+ <p>Encrypts <c>Text</c> according to AES in Cipher Block Chaining
+ mode (CBC). <c>Text</c>
+ must be a multiple of 128 bits (16 bytes). <c>Key</c> is the
+ AES key, and <c>IVec</c> is an arbitrary initializing vector.
+ The lengths of <c>Key</c> and <c>IVec</c> must be 128 bits
+ (16 bytes).</p>
+ </desc>
+ </func>
+ <func>
<name>aes_cbc_128_decrypt(Key, IVec, Cipher) -> Text</name>
- <fsummary>Decrypt <c>Cipher</c>according to AES in Cipher Feedback mode or Cipher Block Chaining mode</fsummary>
+ <fsummary>Decrypt <c>Cipher</c>according to AES in Cipher Block Chaining mode</fsummary>
<type>
<v>Key = Cipher = iolist() | binary()</v>
<v>IVec = Text = binary()</v>
</type>
<desc>
- <p>Decrypts <c>Cipher</c> according to Cipher Feedback Mode (CFB)
- or Cipher Block Chaining mode (CBC).
+ <p>Decrypts <c>Cipher</c> according to AES in Cipher Block
+ Chaining mode (CBC).
<c>Key</c> is the AES key, and <c>IVec</c> is an arbitrary
initializing vector. <c>Key</c> and <c>IVec</c> must have
the same values as those used when encrypting. <c>Cipher</c>
@@ -865,11 +944,13 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
</func>
<func>
- <name>rsa_sign(Data, Key) -> Signature</name>
- <name>rsa_sign(DigestType, Data, Key) -> Signature</name>
+ <name>rsa_sign(DataOrDigest, Key) -> Signature</name>
+ <name>rsa_sign(DigestType, DataOrDigest, Key) -> Signature</name>
<fsummary>Sign the data using rsa with the given key.</fsummary>
<type>
+ <v>DataOrDigest = Data | {digest,Digest}</v>
<v>Data = Mpint</v>
+ <v>Digest = binary()</v>
<v>Key = [E, N, D] | [E, N, D, P1, P2, E1, E2, C]</v>
<v>E, N, D = Mpint</v>
<d>Where <c>E</c> is the public exponent, <c>N</c> is public modulus and
@@ -879,37 +960,40 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
the calculation faster. <c>P1,P2</c> are first and second prime factors.
<c>E1,E2</c> are first and second exponents. <c>C</c> is the CRT coefficient.
Terminology is taken from RFC 3447.</d>
- <v>DigestType = md5 | sha</v>
+ <v>DigestType = md5 | sha | sha224 | sha256 | sha384 | sha512</v>
<d>The default <c>DigestType</c> is sha.</d>
<v>Mpint = binary()</v>
<v>Signature = binary()</v>
</type>
<desc>
- <p>Calculates a <c>DigestType</c> digest of the <c>Data</c>
- and creates a RSA signature with the private key <c>Key</c>
- of the digest.</p>
+ <p>Creates a RSA signature with the private key <c>Key</c>
+ of a digest. The digest is either calculated as a
+ <c>DigestType</c> digest of <c>Data</c> or a precalculated
+ binary <c>Digest</c>.</p>
</desc>
</func>
<func>
- <name>rsa_verify(Data, Signature, Key) -> Verified</name>
- <name>rsa_verify(DigestType, Data, Signature, Key) -> Verified </name>
+ <name>rsa_verify(DataOrDigest, Signature, Key) -> Verified</name>
+ <name>rsa_verify(DigestType, DataOrDigest, Signature, Key) -> Verified </name>
<fsummary>Verify the digest and signature using rsa with given public key.</fsummary>
<type>
<v>Verified = boolean()</v>
+ <v>DataOrDigest = Data | {digest|Digest}</v>
<v>Data, Signature = Mpint</v>
+ <v>Digest = binary()</v>
<v>Key = [E, N]</v>
<v>E, N = Mpint</v>
<d>Where <c>E</c> is the public exponent and <c>N</c> is public modulus.</d>
- <v>DigestType = md5 | sha | sha256 | sha384 | sha512</v>
- <d> The default <c>DigestType</c> is sha.</d>
+ <v>DigestType = md5 | sha | sha224 | sha256 | sha384 | sha512</v>
+ <d>The default <c>DigestType</c> is sha.</d>
<v>Mpint = binary()</v>
</type>
<desc>
- <p>Calculates a <c>DigestType</c> digest of the <c>Data</c>
- and verifies that the digest matches the RSA signature using the
+ <p>Verifies that a digest matches the RSA signature using the
signer's public key <c>Key</c>.
- </p>
+ The digest is either calculated as a <c>DigestType</c>
+ digest of <c>Data</c> or a precalculated binary <c>Digest</c>.</p>
<p>May throw exception <c>notsup</c> in case the chosen <c>DigestType</c>
is not supported by the underlying OpenSSL implementation.</p>
</desc>
@@ -1022,45 +1106,52 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
</func>
<func>
- <name>dss_sign(Data, Key) -> Signature</name>
- <name>dss_sign(DigestType, Data, Key) -> Signature</name>
+ <name>dss_sign(DataOrDigest, Key) -> Signature</name>
+ <name>dss_sign(DigestType, DataOrDigest, Key) -> Signature</name>
<fsummary>Sign the data using dsa with given private key.</fsummary>
<type>
- <v>DigestType = sha | none (default is sha)</v>
- <v>Data = Mpint | ShaDigest</v>
+ <v>DigestType = sha</v>
+ <v>DataOrDigest = Mpint | {digest,Digest}</v>
<v>Key = [P, Q, G, X]</v>
<v>P, Q, G, X = Mpint</v>
<d> Where <c>P</c>, <c>Q</c> and <c>G</c> are the dss
parameters and <c>X</c> is the private key.</d>
- <v>ShaDigest = binary() with length 20 bytes</v>
+ <v>Digest = binary() with length 20 bytes</v>
<v>Signature = binary()</v>
</type>
<desc>
- <p>Creates a DSS signature with the private key <c>Key</c> of a digest.
- If <c>DigestType</c> is 'sha', the digest is calculated as SHA1 of <c>Data</c>.
- If <c>DigestType</c> is 'none', <c>Data</c> is the precalculated SHA1 digest.</p>
+ <p>Creates a DSS signature with the private key <c>Key</c> of
+ a digest. The digest is either calculated as a SHA1
+ digest of <c>Data</c> or a precalculated binary <c>Digest</c>.</p>
+ <p>A deprecated feature is having <c>DigestType = 'none'</c>
+ in which case <c>DataOrDigest</c> is a precalculated SHA1
+ digest.</p>
</desc>
</func>
<func>
- <name>dss_verify(Data, Signature, Key) -> Verified</name>
- <name>dss_verify(DigestType, Data, Signature, Key) -> Verified</name>
+ <name>dss_verify(DataOrDigest, Signature, Key) -> Verified</name>
+ <name>dss_verify(DigestType, DataOrDigest, Signature, Key) -> Verified</name>
<fsummary>Verify the data and signature using dsa with given public key.</fsummary>
<type>
<v>Verified = boolean()</v>
- <v>DigestType = sha | none</v>
+ <v>DigestType = sha</v>
+ <v>DataOrDigest = Mpint | {digest,Digest}</v>
<v>Data = Mpint | ShaDigest</v>
<v>Signature = Mpint</v>
<v>Key = [P, Q, G, Y]</v>
<v>P, Q, G, Y = Mpint</v>
<d> Where <c>P</c>, <c>Q</c> and <c>G</c> are the dss
parameters and <c>Y</c> is the public key.</d>
- <v>ShaDigest = binary() with length 20 bytes</v>
+ <v>Digest = binary() with length 20 bytes</v>
</type>
<desc>
- <p>Verifies that a digest matches the DSS signature using the public key <c>Key</c>.
- If <c>DigestType</c> is 'sha', the digest is calculated as SHA1 of <c>Data</c>.
- If <c>DigestType</c> is 'none', <c>Data</c> is the precalculated SHA1 digest.</p>
+ <p>Verifies that a digest matches the DSS signature using the
+ public key <c>Key</c>. The digest is either calculated as a SHA1
+ digest of <c>Data</c> or is a precalculated binary <c>Digest</c>.</p>
+ <p>A deprecated feature is having <c>DigestType = 'none'</c>
+ in which case <c>DataOrDigest</c> is a precalculated SHA1
+ digest binary.</p>
</desc>
</func>
diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml
index 3a44550ae2..a9f8dd84af 100644
--- a/lib/crypto/doc/src/notes.xml
+++ b/lib/crypto/doc/src/notes.xml
@@ -30,6 +30,53 @@
</header>
<p>This document describes the changes made to the Crypto application.</p>
+<section><title>Crypto 2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Remove unnecessary dependency to libssl from crypto NIF
+ library. This dependency was introduced by accident in
+ R14B04.</p>
+ <p>
+ Own Id: OTP-10064</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add crypto and public_key support for the hash functions
+ SHA224, SHA256, SHA384 and SHA512 and also hmac and
+ rsa_sign/verify support using these hash functions.
+ Thanks to Andreas Schultz for making a prototype.</p>
+ <p>
+ Own Id: OTP-9908</p>
+ </item>
+ <item>
+ <p>
+ Optimize RSA private key handling in <c>crypto</c> and
+ <c>public_key</c>.</p>
+ <p>
+ Own Id: OTP-10065</p>
+ </item>
+ <item>
+ <p>
+ Make <c>crypto:aes_cfb_128_encrypt</c> and
+ <c>crypto:aes_cfb_128_decrypt</c> handle data and cipher
+ with arbitrary length. (Thanks to Stefan Zegenhagen)</p>
+ <p>
+ Own Id: OTP-10136</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Crypto 2.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/crypto/src/Makefile b/lib/crypto/src/Makefile
index ddafb9168f..910e89363c 100644
--- a/lib/crypto/src/Makefile
+++ b/lib/crypto/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2010. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index d7aac27825..0089e79a4f 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. 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
@@ -22,12 +22,19 @@
-module(crypto).
-export([start/0, stop/0, info/0, info_lib/0, version/0]).
+-export([hash/2, hash_init/1, hash_update/2, hash_final/1]).
-export([md4/1, md4_init/0, md4_update/2, md4_final/1]).
-export([md5/1, md5_init/0, md5_update/2, md5_final/1]).
-export([sha/1, sha_init/0, sha_update/2, sha_final/1]).
+-export([sha224/1, sha224_init/0, sha224_update/2, sha224_final/1]).
-export([sha256/1, sha256_init/0, sha256_update/2, sha256_final/1]).
+-export([sha384/1, sha384_init/0, sha384_update/2, sha384_final/1]).
-export([sha512/1, sha512_init/0, sha512_update/2, sha512_final/1]).
-export([md5_mac/2, md5_mac_96/2, sha_mac/2, sha_mac/3, sha_mac_96/2]).
+-export([sha224_mac/2, sha224_mac/3]).
+-export([sha256_mac/2, sha256_mac/3]).
+-export([sha384_mac/2, sha384_mac/3]).
+-export([sha512_mac/2, sha512_mac/3]).
-export([hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]).
-export([des_cbc_encrypt/3, des_cbc_decrypt/3, des_cbc_ivec/1]).
-export([des_ecb_encrypt/2, des_ecb_decrypt/2]).
@@ -64,10 +71,13 @@
-define(FUNC_LIST, [md4, md4_init, md4_update, md4_final,
md5, md5_init, md5_update, md5_final,
sha, sha_init, sha_update, sha_final,
- sha256, sha256_init, sha256_update, sha256_final,
- sha512, sha512_init, sha512_update, sha512_final,
+ sha224, sha224_init, sha224_update, sha224_final,
+ sha256, sha256_init, sha256_update, sha256_final,
+ sha384, sha384_init, sha384_update, sha384_final,
+ sha512, sha512_init, sha512_update, sha512_final,
md5_mac, md5_mac_96,
sha_mac, sha_mac_96,
+ sha224_mac, sha256_mac, sha384_mac, sha512_mac,
sha_mac_init, sha_mac_update, sha_mac_final,
des_cbc_encrypt, des_cbc_decrypt,
des_cfb_encrypt, des_cfb_decrypt,
@@ -95,8 +105,9 @@
aes_ctr_stream_init, aes_ctr_stream_encrypt, aes_ctr_stream_decrypt,
info_lib]).
--type rsa_digest_type() :: 'md5' | 'sha' | 'sha256' | 'sha384' | 'sha512'.
+-type rsa_digest_type() :: 'md5' | 'sha' | 'sha224' | 'sha256' | 'sha384' | 'sha512'.
-type dss_digest_type() :: 'none' | 'sha'.
+-type data_or_digest() :: binary() | {digest, binary()}.
-type crypto_integer() :: binary() | integer().
-define(nif_stub,nif_stub_error(?LINE)).
@@ -171,7 +182,7 @@ info_lib() -> ?nif_stub.
%% (no version): Driver implementation
%% 2.0 : NIF implementation, requires OTP R14
version() -> ?CRYPTO_VSN.
-
+
%% Below Key and Data are binaries or IO-lists. IVec is a binary.
%% Output is always a binary. Context is a binary.
@@ -179,6 +190,45 @@ version() -> ?CRYPTO_VSN.
%% MESSAGE DIGESTS
%%
+-spec hash(_, iodata()) -> binary().
+hash(md5, Data) -> md5(Data);
+hash(md4, Data) -> md4(Data);
+hash(sha, Data) -> sha(Data);
+hash(sha224, Data) -> sha224(Data);
+hash(sha256, Data) -> sha256(Data);
+hash(sha384, Data) -> sha384(Data);
+hash(sha512, Data) -> sha512(Data).
+
+-spec hash_init('md5'|'md4'|'sha'|'sha224'|'sha256'|'sha384'|'sha512') -> any().
+
+hash_init(md5) -> {md5, md5_init()};
+hash_init(md4) -> {md4, md4_init()};
+hash_init(sha) -> {sha, sha_init()};
+hash_init(sha224) -> {sha224, sha224_init()};
+hash_init(sha256) -> {sha256, sha256_init()};
+hash_init(sha384) -> {sha384, sha384_init()};
+hash_init(sha512) -> {sha512, sha512_init()}.
+
+-spec hash_update(_, iodata()) -> any().
+
+hash_update({md5,Context}, Data) -> {md5, md5_update(Context,Data)};
+hash_update({md4,Context}, Data) -> {md4, md4_update(Context,Data)};
+hash_update({sha,Context}, Data) -> {sha, sha_update(Context,Data)};
+hash_update({sha224,Context}, Data) -> {sha224, sha224_update(Context,Data)};
+hash_update({sha256,Context}, Data) -> {sha256, sha256_update(Context,Data)};
+hash_update({sha384,Context}, Data) -> {sha384, sha384_update(Context,Data)};
+hash_update({sha512,Context}, Data) -> {sha512, sha512_update(Context,Data)}.
+
+-spec hash_final(_) -> binary().
+
+hash_final({md5,Context}) -> md5_final(Context);
+hash_final({md4,Context}) -> md4_final(Context);
+hash_final({sha,Context}) -> sha_final(Context);
+hash_final({sha224,Context}) -> sha224_final(Context);
+hash_final({sha256,Context}) -> sha256_final(Context);
+hash_final({sha384,Context}) -> sha384_final(Context);
+hash_final({sha512,Context}) -> sha512_final(Context).
+
%%
%% MD5
%%
@@ -220,6 +270,40 @@ sha_update(_Context, _Data) -> ?nif_stub.
sha_final(_Context) -> ?nif_stub.
%
+%% SHA224
+%%
+-spec sha224(iodata()) -> binary().
+-spec sha224_init() -> binary().
+-spec sha224_update(binary(), iodata()) -> binary().
+-spec sha224_final(binary()) -> binary().
+
+sha224(Data) ->
+ case sha224_nif(Data) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+sha224_init() ->
+ case sha224_init_nif() of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+sha224_update(Context, Data) ->
+ case sha224_update_nif(Context, Data) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+sha224_final(Context) ->
+ case sha224_final_nif(Context) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+
+sha224_nif(_Data) -> ?nif_stub.
+sha224_init_nif() -> ?nif_stub.
+sha224_update_nif(_Context, _Data) -> ?nif_stub.
+sha224_final_nif(_Context) -> ?nif_stub.
+
+%
%% SHA256
%%
-spec sha256(iodata()) -> binary().
@@ -254,6 +338,40 @@ sha256_update_nif(_Context, _Data) -> ?nif_stub.
sha256_final_nif(_Context) -> ?nif_stub.
%
+%% SHA384
+%%
+-spec sha384(iodata()) -> binary().
+-spec sha384_init() -> binary().
+-spec sha384_update(binary(), iodata()) -> binary().
+-spec sha384_final(binary()) -> binary().
+
+sha384(Data) ->
+ case sha384_nif(Data) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+sha384_init() ->
+ case sha384_init_nif() of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+sha384_update(Context, Data) ->
+ case sha384_update_nif(Context, Data) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+sha384_final(Context) ->
+ case sha384_final_nif(Context) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+
+sha384_nif(_Data) -> ?nif_stub.
+sha384_init_nif() -> ?nif_stub.
+sha384_update_nif(_Context, _Data) -> ?nif_stub.
+sha384_final_nif(_Context) -> ?nif_stub.
+
+%
%% SHA512
%%
-spec sha512(iodata()) -> binary().
@@ -336,6 +454,70 @@ sha_mac_96(Key, Data) ->
sha_mac_n(_Key,_Data,_MacSz) -> ?nif_stub.
%%
+%% SHA224_MAC
+%%
+-spec sha224_mac(iodata(), iodata()) -> binary().
+
+sha224_mac(Key, Data) ->
+ sha224_mac(Key, Data, 224 div 8).
+
+sha224_mac(Key, Data, Size) ->
+ case sha224_mac_nif(Key, Data, Size) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+
+sha224_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub.
+
+%%
+%% SHA256_MAC
+%%
+-spec sha256_mac(iodata(), iodata()) -> binary().
+
+sha256_mac(Key, Data) ->
+ sha256_mac(Key, Data, 256 div 8).
+
+sha256_mac(Key, Data, Size) ->
+ case sha256_mac_nif(Key, Data, Size) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+
+sha256_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub.
+
+%%
+%% SHA384_MAC
+%%
+-spec sha384_mac(iodata(), iodata()) -> binary().
+
+sha384_mac(Key, Data) ->
+ sha384_mac(Key, Data, 384 div 8).
+
+sha384_mac(Key, Data, Size) ->
+ case sha384_mac_nif(Key, Data, Size) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+
+sha384_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub.
+
+%%
+%% SHA512_MAC
+%%
+-spec sha512_mac(iodata(), iodata()) -> binary().
+
+sha512_mac(Key, Data) ->
+ sha512_mac(Key, Data, 512 div 8).
+
+sha512_mac(Key, Data, MacSz) ->
+ case sha512_mac_nif(Key, Data, MacSz) of
+ notsup -> erlang:error(notsup);
+ Bin -> Bin
+ end.
+
+sha512_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub.
+
+%%
%% CRYPTO FUNCTIONS
%%
@@ -576,10 +758,10 @@ mod_exp_nif(_Base,_Exp,_Mod) -> ?nif_stub.
%%
%% DSS, RSA - verify
%%
--spec dss_verify(binary(), binary(), [binary()]) -> boolean().
--spec dss_verify(dss_digest_type(), binary(), binary(), [binary()]) -> boolean().
--spec rsa_verify(binary(), binary(), [binary()]) -> boolean().
--spec rsa_verify(rsa_digest_type(), binary(), binary(), [binary()]) ->
+-spec dss_verify(data_or_digest(), binary(), [binary()]) -> boolean().
+-spec dss_verify(dss_digest_type(), data_or_digest(), binary(), [binary()]) -> boolean().
+-spec rsa_verify(data_or_digest(), binary(), [binary()]) -> boolean().
+-spec rsa_verify(rsa_digest_type(), data_or_digest(), binary(), [binary()]) ->
boolean().
%% Key = [P,Q,G,Y] P,Q,G=DSSParams Y=PublicKey
@@ -590,8 +772,8 @@ dss_verify(_Type,_Data,_Signature,_Key) -> ?nif_stub.
% Key = [E,N] E=PublicExponent N=PublicModulus
rsa_verify(Data,Signature,Key) ->
rsa_verify_nif(sha, Data,Signature,Key).
-rsa_verify(Type, Data, Signature, Key) ->
- case rsa_verify_nif(Type, Data, Signature, Key) of
+rsa_verify(Type, DataOrDigest, Signature, Key) ->
+ case rsa_verify_nif(Type, DataOrDigest, Signature, Key) of
notsup -> erlang:error(notsup);
Bool -> Bool
end.
@@ -603,27 +785,27 @@ rsa_verify_nif(_Type, _Data, _Signature, _Key) -> ?nif_stub.
%% DSS, RSA - sign
%%
%% Key = [P,Q,G,X] P,Q,G=DSSParams X=PrivateKey
--spec dss_sign(binary(), [binary()]) -> binary().
--spec dss_sign(dss_digest_type(), binary(), [binary()]) -> binary().
--spec rsa_sign(binary(), [binary()]) -> binary().
--spec rsa_sign(rsa_digest_type(), binary(), [binary()]) -> binary().
-
-dss_sign(Data,Key) ->
- dss_sign(sha,Data,Key).
-dss_sign(Type, Data, Key) ->
- case dss_sign_nif(Type,Data,Key) of
- error -> erlang:error(badkey, [Data, Key]);
+-spec dss_sign(data_or_digest(), [binary()]) -> binary().
+-spec dss_sign(dss_digest_type(), data_or_digest(), [binary()]) -> binary().
+-spec rsa_sign(data_or_digest(), [binary()]) -> binary().
+-spec rsa_sign(rsa_digest_type(), data_or_digest(), [binary()]) -> binary().
+
+dss_sign(DataOrDigest,Key) ->
+ dss_sign(sha,DataOrDigest,Key).
+dss_sign(Type, DataOrDigest, Key) ->
+ case dss_sign_nif(Type,DataOrDigest,Key) of
+ error -> erlang:error(badkey, [DataOrDigest, Key]);
Sign -> Sign
end.
dss_sign_nif(_Type,_Data,_Key) -> ?nif_stub.
%% Key = [E,N,D] E=PublicExponent N=PublicModulus D=PrivateExponent
-rsa_sign(Data,Key) ->
- rsa_sign(sha, Data, Key).
-rsa_sign(Type, Data, Key) ->
- case rsa_sign_nif(Type,Data,Key) of
- error -> erlang:error(badkey, [Type,Data,Key]);
+rsa_sign(DataOrDigest,Key) ->
+ rsa_sign(sha, DataOrDigest, Key).
+rsa_sign(Type, DataOrDigest, Key) ->
+ case rsa_sign_nif(Type,DataOrDigest,Key) of
+ error -> erlang:error(badkey, [Type,DataOrDigest,Key]);
Sign -> Sign
end.
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 627c966dfb..7ac693f371 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. 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
@@ -33,9 +33,12 @@
sha_update/1,
hmac_update_sha/1,
hmac_update_sha_n/1,
+ hmac_update_sha256/1,
+ hmac_update_sha512/1,
hmac_update_md5/1,
hmac_update_md5_io/1,
hmac_update_md5_n/1,
+ hmac_rfc4231/1,
sha256/1,
sha256_update/1,
sha512/1,
@@ -61,7 +64,9 @@
rsa_verify_test/1,
dsa_verify_test/1,
rsa_sign_test/1,
+ rsa_sign_hash_test/1,
dsa_sign_test/1,
+ dsa_sign_hash_test/1,
rsa_encrypt_decrypt/1,
dh/1,
exor_test/1,
@@ -82,13 +87,15 @@ groups() ->
{rest, [],
[md5, md5_update, md4, md4_update, md5_mac,
md5_mac_io, sha, sha_update,
- hmac_update_sha, hmac_update_sha_n, hmac_update_md5_n,
- hmac_update_md5_io, hmac_update_md5,
+ hmac_update_sha, hmac_update_sha_n, hmac_update_sha256, hmac_update_sha512,
+ hmac_update_md5_n, hmac_update_md5_io, hmac_update_md5,
+ hmac_rfc4231,
des_cbc, aes_cfb, aes_cbc,
aes_cbc_iter, aes_ctr, aes_ctr_stream, des_cbc_iter, des_ecb,
rand_uniform_test, strong_rand_test,
rsa_verify_test, dsa_verify_test, rsa_sign_test,
- dsa_sign_test, rsa_encrypt_decrypt, dh, exor_test,
+ rsa_sign_hash_test, dsa_sign_test, dsa_sign_hash_test,
+ rsa_encrypt_decrypt, dh, exor_test,
rc4_test, rc4_stream_test, mod_exp_test, blowfish_cfb64,
smp]}].
@@ -335,6 +342,60 @@ hmac_update_sha(Config) when is_list(Config) ->
?line Mac = crypto:hmac_final(Ctx3),
?line Exp = crypto:sha_mac(Key, lists:flatten([Data, Data2])),
?line m(Exp, Mac).
+
+hmac_update_sha256(doc) ->
+ ["Generate an SHA256 HMAC using hmac_init, hmac_update, and hmac_final. "
+ "Expected values for examples are generated using crypto:sha256_mac." ];
+hmac_update_sha256(suite) ->
+ [];
+hmac_update_sha256(Config) when is_list(Config) ->
+ case openssl_version() of
+ V when V < 16#908000 ->
+ {skipped,"OpenSSL version too old"};
+ _ ->
+ hmac_update_sha256_do()
+ end.
+
+hmac_update_sha256_do() ->
+ ?line Key = hexstr2bin("00010203101112132021222330313233"
+ "04050607141516172425262734353637"
+ "08090a0b18191a1b28292a2b38393a3b"
+ "0c0d0e0f1c1d1e1f2c2d2e2f3c3d3e3f"),
+ ?line Data = "Sampl",
+ ?line Data2 = "e #1",
+ ?line Ctx = crypto:hmac_init(sha256, Key),
+ ?line Ctx2 = crypto:hmac_update(Ctx, Data),
+ ?line Ctx3 = crypto:hmac_update(Ctx2, Data2),
+ ?line Mac = crypto:hmac_final(Ctx3),
+ ?line Exp = crypto:sha256_mac(Key, lists:flatten([Data, Data2])),
+ ?line m(Exp, Mac).
+
+hmac_update_sha512(doc) ->
+ ["Generate an SHA512 HMAC using hmac_init, hmac_update, and hmac_final. "
+ "Expected values for examples are generated using crypto:sha512_mac." ];
+hmac_update_sha512(suite) ->
+ [];
+hmac_update_sha512(Config) when is_list(Config) ->
+ case openssl_version() of
+ V when V < 16#908000 ->
+ {skipped,"OpenSSL version too old"};
+ _ ->
+ hmac_update_sha512_do()
+ end.
+
+hmac_update_sha512_do() ->
+ ?line Key = hexstr2bin("00010203101112132021222330313233"
+ "04050607141516172425262734353637"
+ "08090a0b18191a1b28292a2b38393a3b"
+ "0c0d0e0f1c1d1e1f2c2d2e2f3c3d3e3f"),
+ ?line Data = "Sampl",
+ ?line Data2 = "e #1",
+ ?line Ctx = crypto:hmac_init(sha512, Key),
+ ?line Ctx2 = crypto:hmac_update(Ctx, Data),
+ ?line Ctx3 = crypto:hmac_update(Ctx2, Data2),
+ ?line Mac = crypto:hmac_final(Ctx3),
+ ?line Exp = crypto:sha512_mac(Key, lists:flatten([Data, Data2])),
+ ?line m(Exp, Mac).
hmac_update_md5(doc) ->
["Generate an MD5 HMAC using hmac_init, hmac_update, and hmac_final. "
@@ -354,7 +415,280 @@ hmac_update_md5(Config) when is_list(Config) ->
?line Mac2 = crypto:hmac_final(CtxD),
?line Exp2 = crypto:md5_mac(Key2, lists:flatten([Long1, Long2, Long3])),
?line m(Exp2, Mac2).
+
+hmac_rfc4231(doc) ->
+ ["Generate an HMAC using crypto:shaXXX_mac and hmac_init, hmac_update, and hmac_final. "
+ "Testvectors are take from RFC4231." ];
+hmac_rfc4231(suite) ->
+ [];
+hmac_rfc4231(Config) when is_list(Config) ->
+ case openssl_version() of
+ V when V < 16#908000 ->
+ {skipped,"OpenSSL version too old"};
+ _ ->
+ hmac_rfc4231_do()
+ end.
+
+hmac_rfc4231_do() ->
+ %% Test Case 1
+ Case1Key = binary:copy(<<16#0b>>, 20),
+ Case1Data = <<"Hi There">>,
+ Case1Exp224 = hexstr2bin("896fb1128abbdf196832107cd49df33f"
+ "47b4b1169912ba4f53684b22"),
+ Case1Exp256 = hexstr2bin("b0344c61d8db38535ca8afceaf0bf12b"
+ "881dc200c9833da726e9376c2e32cff7"),
+ Case1Exp384 = hexstr2bin("afd03944d84895626b0825f4ab46907f"
+ "15f9dadbe4101ec682aa034c7cebc59c"
+ "faea9ea9076ede7f4af152e8b2fa9cb6"),
+ Case1Exp512 = hexstr2bin("87aa7cdea5ef619d4ff0b4241a1d6cb0"
+ "2379f4e2ce4ec2787ad0b30545e17cde"
+ "daa833b7d6b8a702038b274eaea3f4e4"
+ "be9d914eeb61f1702e696c203a126854"),
+
+ ?line Case1Ctx224 = crypto:hmac_init(sha224, Case1Key),
+ ?line Case1Ctx224_2 = crypto:hmac_update(Case1Ctx224, Case1Data),
+ ?line Case1Mac224_1 = crypto:hmac_final(Case1Ctx224_2),
+ ?line Case1Mac224_2 = crypto:sha224_mac(Case1Key, Case1Data),
+ ?line m(Case1Exp224, Case1Mac224_1),
+ ?line m(Case1Exp224, Case1Mac224_2),
+
+ ?line Case1Ctx256 = crypto:hmac_init(sha256, Case1Key),
+ ?line Case1Ctx256_2 = crypto:hmac_update(Case1Ctx256, Case1Data),
+ ?line Case1Mac256_1 = crypto:hmac_final(Case1Ctx256_2),
+ ?line Case1Mac256_2 = crypto:sha256_mac(Case1Key, Case1Data),
+ ?line m(Case1Exp256, Case1Mac256_1),
+ ?line m(Case1Exp256, Case1Mac256_2),
+
+ ?line Case1Ctx384 = crypto:hmac_init(sha384, Case1Key),
+ ?line Case1Ctx384_2 = crypto:hmac_update(Case1Ctx384, Case1Data),
+ ?line Case1Mac384_1 = crypto:hmac_final(Case1Ctx384_2),
+ ?line Case1Mac384_2 = crypto:sha384_mac(Case1Key, Case1Data),
+ ?line m(Case1Exp384, Case1Mac384_1),
+ ?line m(Case1Exp384, Case1Mac384_2),
+
+ ?line Case1Ctx512 = crypto:hmac_init(sha512, Case1Key),
+ ?line Case1Ctx512_2 = crypto:hmac_update(Case1Ctx512, Case1Data),
+ ?line Case1Mac512_1 = crypto:hmac_final(Case1Ctx512_2),
+ ?line Case1Mac512_2 = crypto:sha512_mac(Case1Key, Case1Data),
+ ?line m(Case1Exp512, Case1Mac512_1),
+ ?line m(Case1Exp512, Case1Mac512_2),
+
+ %% Test Case 2
+ Case2Key = <<"Jefe">>,
+ Case2Data = <<"what do ya want for nothing?">>,
+ Case2Exp224 = hexstr2bin("a30e01098bc6dbbf45690f3a7e9e6d0f"
+ "8bbea2a39e6148008fd05e44"),
+ Case2Exp256 = hexstr2bin("5bdcc146bf60754e6a042426089575c7"
+ "5a003f089d2739839dec58b964ec3843"),
+ Case2Exp384 = hexstr2bin("af45d2e376484031617f78d2b58a6b1b"
+ "9c7ef464f5a01b47e42ec3736322445e"
+ "8e2240ca5e69e2c78b3239ecfab21649"),
+ Case2Exp512 = hexstr2bin("164b7a7bfcf819e2e395fbe73b56e0a3"
+ "87bd64222e831fd610270cd7ea250554"
+ "9758bf75c05a994a6d034f65f8f0e6fd"
+ "caeab1a34d4a6b4b636e070a38bce737"),
+
+ ?line Case2Ctx224 = crypto:hmac_init(sha224, Case2Key),
+ ?line Case2Ctx224_2 = crypto:hmac_update(Case2Ctx224, Case2Data),
+ ?line Case2Mac224_1 = crypto:hmac_final(Case2Ctx224_2),
+ ?line Case2Mac224_2 = crypto:sha224_mac(Case2Key, Case2Data),
+ ?line m(Case2Exp224, Case2Mac224_1),
+ ?line m(Case2Exp224, Case2Mac224_2),
+
+ ?line Case2Ctx256 = crypto:hmac_init(sha256, Case2Key),
+ ?line Case2Ctx256_2 = crypto:hmac_update(Case2Ctx256, Case2Data),
+ ?line Case2Mac256_1 = crypto:hmac_final(Case2Ctx256_2),
+ ?line Case2Mac256_2 = crypto:sha256_mac(Case2Key, Case2Data),
+ ?line m(Case2Exp256, Case2Mac256_1),
+ ?line m(Case2Exp256, Case2Mac256_2),
+
+ ?line Case2Ctx384 = crypto:hmac_init(sha384, Case2Key),
+ ?line Case2Ctx384_2 = crypto:hmac_update(Case2Ctx384, Case2Data),
+ ?line Case2Mac384_1 = crypto:hmac_final(Case2Ctx384_2),
+ ?line Case2Mac384_2 = crypto:sha384_mac(Case2Key, Case2Data),
+ ?line m(Case2Exp384, Case2Mac384_1),
+ ?line m(Case2Exp384, Case2Mac384_2),
+
+ ?line Case2Ctx512 = crypto:hmac_init(sha512, Case2Key),
+ ?line Case2Ctx512_2 = crypto:hmac_update(Case2Ctx512, Case2Data),
+ ?line Case2Mac512_1 = crypto:hmac_final(Case2Ctx512_2),
+ ?line Case2Mac512_2 = crypto:sha512_mac(Case2Key, Case2Data),
+ ?line m(Case2Exp512, Case2Mac512_1),
+ ?line m(Case2Exp512, Case2Mac512_2),
+
+ %% Test Case 3
+ Case3Key = binary:copy(<<16#aa>>, 20),
+ Case3Data = binary:copy(<<16#dd>>, 50),
+ Case3Exp224 = hexstr2bin("7fb3cb3588c6c1f6ffa9694d7d6ad264"
+ "9365b0c1f65d69d1ec8333ea"),
+ Case3Exp256 = hexstr2bin("773ea91e36800e46854db8ebd09181a7"
+ "2959098b3ef8c122d9635514ced565fe"),
+ Case3Exp384 = hexstr2bin("88062608d3e6ad8a0aa2ace014c8a86f"
+ "0aa635d947ac9febe83ef4e55966144b"
+ "2a5ab39dc13814b94e3ab6e101a34f27"),
+ Case3Exp512 = hexstr2bin("fa73b0089d56a284efb0f0756c890be9"
+ "b1b5dbdd8ee81a3655f83e33b2279d39"
+ "bf3e848279a722c806b485a47e67c807"
+ "b946a337bee8942674278859e13292fb"),
+
+ ?line Case3Ctx224 = crypto:hmac_init(sha224, Case3Key),
+ ?line Case3Ctx224_2 = crypto:hmac_update(Case3Ctx224, Case3Data),
+ ?line Case3Mac224_1 = crypto:hmac_final(Case3Ctx224_2),
+ ?line Case3Mac224_2 = crypto:sha224_mac(Case3Key, Case3Data),
+ ?line m(Case3Exp224, Case3Mac224_1),
+ ?line m(Case3Exp224, Case3Mac224_2),
+
+ ?line Case3Ctx256 = crypto:hmac_init(sha256, Case3Key),
+ ?line Case3Ctx256_2 = crypto:hmac_update(Case3Ctx256, Case3Data),
+ ?line Case3Mac256_1 = crypto:hmac_final(Case3Ctx256_2),
+ ?line Case3Mac256_2 = crypto:sha256_mac(Case3Key, Case3Data),
+ ?line m(Case3Exp256, Case3Mac256_1),
+ ?line m(Case3Exp256, Case3Mac256_2),
+
+ ?line Case3Ctx384 = crypto:hmac_init(sha384, Case3Key),
+ ?line Case3Ctx384_2 = crypto:hmac_update(Case3Ctx384, Case3Data),
+ ?line Case3Mac384_1 = crypto:hmac_final(Case3Ctx384_2),
+ ?line Case3Mac384_2 = crypto:sha384_mac(Case3Key, Case3Data),
+ ?line m(Case3Exp384, Case3Mac384_1),
+ ?line m(Case3Exp384, Case3Mac384_2),
+
+ ?line Case3Ctx512 = crypto:hmac_init(sha512, Case3Key),
+ ?line Case3Ctx512_2 = crypto:hmac_update(Case3Ctx512, Case3Data),
+ ?line Case3Mac512_1 = crypto:hmac_final(Case3Ctx512_2),
+ ?line Case3Mac512_2 = crypto:sha512_mac(Case3Key, Case3Data),
+ ?line m(Case3Exp512, Case3Mac512_1),
+ ?line m(Case3Exp512, Case3Mac512_2),
+
+ %% Test Case 4
+ Case4Key = list_to_binary(lists:seq(1, 16#19)),
+ Case4Data = binary:copy(<<16#cd>>, 50),
+ Case4Exp224 = hexstr2bin("6c11506874013cac6a2abc1bb382627c"
+ "ec6a90d86efc012de7afec5a"),
+ Case4Exp256 = hexstr2bin("82558a389a443c0ea4cc819899f2083a"
+ "85f0faa3e578f8077a2e3ff46729665b"),
+ Case4Exp384 = hexstr2bin("3e8a69b7783c25851933ab6290af6ca7"
+ "7a9981480850009cc5577c6e1f573b4e"
+ "6801dd23c4a7d679ccf8a386c674cffb"),
+ Case4Exp512 = hexstr2bin("b0ba465637458c6990e5a8c5f61d4af7"
+ "e576d97ff94b872de76f8050361ee3db"
+ "a91ca5c11aa25eb4d679275cc5788063"
+ "a5f19741120c4f2de2adebeb10a298dd"),
+
+ ?line Case4Ctx224 = crypto:hmac_init(sha224, Case4Key),
+ ?line Case4Ctx224_2 = crypto:hmac_update(Case4Ctx224, Case4Data),
+ ?line Case4Mac224_1 = crypto:hmac_final(Case4Ctx224_2),
+ ?line Case4Mac224_2 = crypto:sha224_mac(Case4Key, Case4Data),
+ ?line m(Case4Exp224, Case4Mac224_1),
+ ?line m(Case4Exp224, Case4Mac224_2),
+
+ ?line Case4Ctx256 = crypto:hmac_init(sha256, Case4Key),
+ ?line Case4Ctx256_2 = crypto:hmac_update(Case4Ctx256, Case4Data),
+ ?line Case4Mac256_1 = crypto:hmac_final(Case4Ctx256_2),
+ ?line Case4Mac256_2 = crypto:sha256_mac(Case4Key, Case4Data),
+ ?line m(Case4Exp256, Case4Mac256_1),
+ ?line m(Case4Exp256, Case4Mac256_2),
+
+ ?line Case4Ctx384 = crypto:hmac_init(sha384, Case4Key),
+ ?line Case4Ctx384_2 = crypto:hmac_update(Case4Ctx384, Case4Data),
+ ?line Case4Mac384_1 = crypto:hmac_final(Case4Ctx384_2),
+ ?line Case4Mac384_2 = crypto:sha384_mac(Case4Key, Case4Data),
+ ?line m(Case4Exp384, Case4Mac384_1),
+ ?line m(Case4Exp384, Case4Mac384_2),
+
+ ?line Case4Ctx512 = crypto:hmac_init(sha512, Case4Key),
+ ?line Case4Ctx512_2 = crypto:hmac_update(Case4Ctx512, Case4Data),
+ ?line Case4Mac512_1 = crypto:hmac_final(Case4Ctx512_2),
+ ?line Case4Mac512_2 = crypto:sha512_mac(Case4Key, Case4Data),
+ ?line m(Case4Exp512, Case4Mac512_1),
+ ?line m(Case4Exp512, Case4Mac512_2),
+
+ %% Test Case 6
+ Case6Key = binary:copy(<<16#aa>>, 131),
+ Case6Data = <<"Test Using Larger Than Block-Size Key - Hash Key First">>,
+ Case6Exp224 = hexstr2bin("95e9a0db962095adaebe9b2d6f0dbce2"
+ "d499f112f2d2b7273fa6870e"),
+ Case6Exp256 = hexstr2bin("60e431591ee0b67f0d8a26aacbf5b77f"
+ "8e0bc6213728c5140546040f0ee37f54"),
+ Case6Exp384 = hexstr2bin("4ece084485813e9088d2c63a041bc5b4"
+ "4f9ef1012a2b588f3cd11f05033ac4c6"
+ "0c2ef6ab4030fe8296248df163f44952"),
+ Case6Exp512 = hexstr2bin("80b24263c7c1a3ebb71493c1dd7be8b4"
+ "9b46d1f41b4aeec1121b013783f8f352"
+ "6b56d037e05f2598bd0fd2215d6a1e52"
+ "95e64f73f63f0aec8b915a985d786598"),
+
+ ?line Case6Ctx224 = crypto:hmac_init(sha224, Case6Key),
+ ?line Case6Ctx224_2 = crypto:hmac_update(Case6Ctx224, Case6Data),
+ ?line Case6Mac224_1 = crypto:hmac_final(Case6Ctx224_2),
+ ?line Case6Mac224_2 = crypto:sha224_mac(Case6Key, Case6Data),
+ ?line m(Case6Exp224, Case6Mac224_1),
+ ?line m(Case6Exp224, Case6Mac224_2),
+
+ ?line Case6Ctx256 = crypto:hmac_init(sha256, Case6Key),
+ ?line Case6Ctx256_2 = crypto:hmac_update(Case6Ctx256, Case6Data),
+ ?line Case6Mac256_1 = crypto:hmac_final(Case6Ctx256_2),
+ ?line Case6Mac256_2 = crypto:sha256_mac(Case6Key, Case6Data),
+ ?line m(Case6Exp256, Case6Mac256_1),
+ ?line m(Case6Exp256, Case6Mac256_2),
+
+ ?line Case6Ctx384 = crypto:hmac_init(sha384, Case6Key),
+ ?line Case6Ctx384_2 = crypto:hmac_update(Case6Ctx384, Case6Data),
+ ?line Case6Mac384_1 = crypto:hmac_final(Case6Ctx384_2),
+ ?line Case6Mac384_2 = crypto:sha384_mac(Case6Key, Case6Data),
+ ?line m(Case6Exp384, Case6Mac384_1),
+ ?line m(Case6Exp384, Case6Mac384_2),
+
+ ?line Case6Ctx512 = crypto:hmac_init(sha512, Case6Key),
+ ?line Case6Ctx512_2 = crypto:hmac_update(Case6Ctx512, Case6Data),
+ ?line Case6Mac512_1 = crypto:hmac_final(Case6Ctx512_2),
+ ?line Case6Mac512_2 = crypto:sha512_mac(Case6Key, Case6Data),
+ ?line m(Case6Exp512, Case6Mac512_1),
+ ?line m(Case6Exp512, Case6Mac512_2),
+ %% Test Case 7
+ Case7Key = binary:copy(<<16#aa>>, 131),
+ Case7Data = <<"This is a test using a larger than block-size key and a larger t",
+ "han block-size data. The key needs to be hashed before being use",
+ "d by the HMAC algorithm.">>,
+ Case7Exp224 = hexstr2bin("3a854166ac5d9f023f54d517d0b39dbd"
+ "946770db9c2b95c9f6f565d1"),
+ Case7Exp256 = hexstr2bin("9b09ffa71b942fcb27635fbcd5b0e944"
+ "bfdc63644f0713938a7f51535c3a35e2"),
+ Case7Exp384 = hexstr2bin("6617178e941f020d351e2f254e8fd32c"
+ "602420feb0b8fb9adccebb82461e99c5"
+ "a678cc31e799176d3860e6110c46523e"),
+ Case7Exp512 = hexstr2bin("e37b6a775dc87dbaa4dfa9f96e5e3ffd"
+ "debd71f8867289865df5a32d20cdc944"
+ "b6022cac3c4982b10d5eeb55c3e4de15"
+ "134676fb6de0446065c97440fa8c6a58"),
+
+ ?line Case7Ctx224 = crypto:hmac_init(sha224, Case7Key),
+ ?line Case7Ctx224_2 = crypto:hmac_update(Case7Ctx224, Case7Data),
+ ?line Case7Mac224_1 = crypto:hmac_final(Case7Ctx224_2),
+ ?line Case7Mac224_2 = crypto:sha224_mac(Case7Key, Case7Data),
+ ?line m(Case7Exp224, Case7Mac224_1),
+ ?line m(Case7Exp224, Case7Mac224_2),
+
+ ?line Case7Ctx256 = crypto:hmac_init(sha256, Case7Key),
+ ?line Case7Ctx256_2 = crypto:hmac_update(Case7Ctx256, Case7Data),
+ ?line Case7Mac256_1 = crypto:hmac_final(Case7Ctx256_2),
+ ?line Case7Mac256_2 = crypto:sha256_mac(Case7Key, Case7Data),
+ ?line m(Case7Exp256, Case7Mac256_1),
+ ?line m(Case7Exp256, Case7Mac256_2),
+
+ ?line Case7Ctx384 = crypto:hmac_init(sha384, Case7Key),
+ ?line Case7Ctx384_2 = crypto:hmac_update(Case7Ctx384, Case7Data),
+ ?line Case7Mac384_1 = crypto:hmac_final(Case7Ctx384_2),
+ ?line Case7Mac384_2 = crypto:sha384_mac(Case7Key, Case7Data),
+ ?line m(Case7Exp384, Case7Mac384_1),
+ ?line m(Case7Exp384, Case7Mac384_2),
+
+ ?line Case7Ctx512 = crypto:hmac_init(sha512, Case7Key),
+ ?line Case7Ctx512_2 = crypto:hmac_update(Case7Ctx512, Case7Data),
+ ?line Case7Mac512_1 = crypto:hmac_final(Case7Ctx512_2),
+ ?line Case7Mac512_2 = crypto:sha512_mac(Case7Key, Case7Data),
+ ?line m(Case7Exp512, Case7Mac512_1),
+ ?line m(Case7Exp512, Case7Mac512_2).
hmac_update_md5_io(doc) ->
["Generate an MD5 HMAC using hmac_init, hmac_update, and hmac_final. "
@@ -717,10 +1051,19 @@ aes_cfb(Config) when is_list(Config) ->
?line Key = hexstr2bin("2b7e151628aed2a6abf7158809cf4f3c"),
?line IVec = hexstr2bin("000102030405060708090a0b0c0d0e0f"),
?line Plain = hexstr2bin("6bc1bee22e409f96e93d7e117393172a"),
- ?line Cipher = crypto:aes_cfb_128_encrypt(Key, IVec, Plain),
- ?line m(Cipher, hexstr2bin("3b3fd92eb72dad20333449f8e83cfb4a")),
- ?line m(Plain,
- crypto:aes_cfb_128_decrypt(Key, IVec, Cipher)).
+ ?line Cipher = hexstr2bin("3b3fd92eb72dad20333449f8e83cfb4a"),
+
+ %% Try all prefixes of plain and cipher.
+ aes_cfb_do(byte_size(Plain), Plain, Cipher, Key, IVec).
+
+aes_cfb_do(N, Plain, Cipher, Key, IVec) when N >= 0 ->
+ <<P:N/binary, _/binary>> = Plain,
+ <<C:N/binary, _/binary>> = Cipher,
+ ?line C = crypto:aes_cfb_128_encrypt(Key, IVec, P),
+ ?line P = crypto:aes_cfb_128_decrypt(Key, IVec, C),
+ aes_cfb_do(N-1, Plain, Cipher, Key, IVec);
+aes_cfb_do(_, _, _, _, _) -> ok.
+
%%
%%
@@ -1207,6 +1550,33 @@ rsa_sign_test(Config) when is_list(Config) ->
ok.
+rsa_sign_hash_test(doc) ->
+ "rsa_sign_hash testing";
+rsa_sign_hash_test(suite) ->
+ [];
+rsa_sign_hash_test(Config) when is_list(Config) ->
+ PubEx = 65537,
+ PrivEx = 7531712708607620783801185371644749935066152052780368689827275932079815492940396744378735701395659435842364793962992309884847527234216715366607660219930945,
+ Mod = 7919488123861148172698919999061127847747888703039837999377650217570191053151807772962118671509138346758471459464133273114654252861270845708312601272799123,
+ Msg = <<"7896345786348756234 Hejsan Svejsan, erlang crypto debugger"
+ "09812312908312378623487263487623412039812 huagasd">>,
+
+ PrivKey = [crypto:mpint(PubEx), crypto:mpint(Mod), crypto:mpint(PrivEx)],
+ PubKey = [crypto:mpint(PubEx), crypto:mpint(Mod)],
+ MD5 = crypto:md5(sized_binary(Msg)),
+ SHA = crypto:sha(sized_binary(Msg)),
+ ?line Sig1 = crypto:rsa_sign(sha, {digest,SHA}, PrivKey),
+ ?line m(crypto:rsa_verify(sha, {digest,SHA}, sized_binary(Sig1),PubKey), true),
+
+ ?line Sig2 = crypto:rsa_sign(md5, {digest,MD5}, PrivKey),
+ ?line m(crypto:rsa_verify(md5, {digest,MD5}, sized_binary(Sig2),PubKey), true),
+
+ ?line m(Sig1 =:= Sig2, false),
+ ?line m(crypto:rsa_verify(md5, {digest,MD5}, sized_binary(Sig1),PubKey), false),
+ ?line m(crypto:rsa_verify(sha, {digest,SHA}, sized_binary(Sig2),PubKey), false),
+
+ ok.
+
dsa_sign_test(doc) ->
"dsa_sign testing";
dsa_sign_test(suite) ->
@@ -1237,6 +1607,37 @@ dsa_sign_test(Config) when is_list(Config) ->
ok.
+dsa_sign_hash_test(doc) ->
+ "dsa_sign_hash testing";
+dsa_sign_hash_test(suite) ->
+ [];
+dsa_sign_hash_test(Config) when is_list(Config) ->
+ Msg = <<"7896345786348756234 Hejsan Svejsan, erlang crypto debugger"
+ "09812312908312378623487263487623412039812 huagasd">>,
+ SHA = crypto:sha(sized_binary(Msg)),
+
+ PubKey = _Y = 25854665488880835237281628794585130313500176551981812527054397586638455298000483144002221850980183404910190346416063318160497344811383498859129095184158800144312512447497510551471331451396405348497845813002058423110442376886564659959543650802132345311573634832461635601376738282831340827591903548964194832978,
+ PrivKey = _X = 441502407453038284293378221372000880210588566361,
+ ParamP = 109799869232806890760655301608454668257695818999841877165019612946154359052535682480084145133201304812979481136659521529774182959764860329095546511521488413513097576425638476458000255392402120367876345280670101492199681798674053929238558140260669578407351853803102625390950534052428162468100618240968893110797,
+ ParamQ = 1349199015905534965792122312016505075413456283393,
+ ParamG = 18320614775012672475365915366944922415598782131828709277168615511695849821411624805195787607930033958243224786899641459701930253094446221381818858674389863050420226114787005820357372837321561754462061849169568607689530279303056075793886577588606958623645901271866346406773590024901668622321064384483571751669,
+
+ Params = [crypto:mpint(ParamP), crypto:mpint(ParamQ), crypto:mpint(ParamG)],
+ ?line Sig1 = crypto:dss_sign(sha, {digest,SHA}, Params ++ [crypto:mpint(PrivKey)]),
+
+ ?line m(crypto:dss_verify(none, SHA, sized_binary(Sig1),
+ Params ++ [crypto:mpint(PubKey)]), true),
+
+ ?line m(crypto:dss_verify(sized_binary(one_bit_wrong(Msg)), sized_binary(Sig1),
+ Params ++ [crypto:mpint(PubKey)]), false),
+
+ ?line m(crypto:dss_verify(sized_binary(Msg), sized_binary(one_bit_wrong(Sig1)),
+ Params ++ [crypto:mpint(PubKey)]), false),
+
+ %%?line Bad = crypto:dss_sign(sized_binary(Msg), [Params, crypto:mpint(PubKey)]),
+
+ ok.
+
rsa_encrypt_decrypt(doc) ->
["Test rsa_public_encrypt and rsa_private_decrypt functions."];
@@ -1421,7 +1822,9 @@ worker_loop(N, Config) ->
Funcs = { md5, md5_update, md5_mac, md5_mac_io, sha, sha_update, des_cbc,
aes_cfb, aes_cbc, des_cbc_iter, rand_uniform_test, strong_rand_test,
rsa_verify_test, exor_test, rc4_test, rc4_stream_test, mod_exp_test,
- hmac_update_md5, hmac_update_sha, aes_ctr_stream },
+ hmac_update_md5, hmac_update_sha, hmac_update_sha256, hmac_update_sha512,
+ hmac_rfc4231,
+ aes_ctr_stream },
F = element(random:uniform(size(Funcs)),Funcs),
%%io:format("worker ~p calling ~p\n",[self(),F]),
@@ -1548,3 +1951,11 @@ my_dss_sign(Data,Key) ->
?line S3 = crypto:dss_sign(none, crypto:sha(Raw), Key),
[S1,S2,S3].
+openssl_version() ->
+ case crypto:info_lib() of
+ [{<<"OpenSSL">>,LibVer,_}] when is_integer(LibVer) ->
+ LibVer;
+ _ ->
+ undefined
+ end.
+
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index 7e82e47d38..ccfb1fd66e 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 2.1
+CRYPTO_VSN = 2.2
diff --git a/lib/debugger/doc/src/Makefile b/lib/debugger/doc/src/Makefile
index bc11ed7f46..ba62e41f6b 100644
--- a/lib/debugger/doc/src/Makefile
+++ b/lib/debugger/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/debugger/doc/src/notes.xml b/lib/debugger/doc/src/notes.xml
index 593bf715bf..66638a59f6 100644
--- a/lib/debugger/doc/src/notes.xml
+++ b/lib/debugger/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/debugger/priv/Makefile b/lib/debugger/priv/Makefile
index 2f3002331b..cc19aceaf6 100644
--- a/lib/debugger/priv/Makefile
+++ b/lib/debugger/priv/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/debugger/src/Makefile b/lib/debugger/src/Makefile
index 1f9d6f2058..e8b350c0c7 100644
--- a/lib/debugger/src/Makefile
+++ b/lib/debugger/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/debugger/test/Makefile b/lib/debugger/test/Makefile
index bf948ce8b0..5aa290c61b 100644
--- a/lib/debugger/test/Makefile
+++ b/lib/debugger/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2011. All Rights Reserved.
+# Copyright Ericsson AB 1998-2012. 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/lib/debugger/test/erl_eval_SUITE.erl b/lib/debugger/test/erl_eval_SUITE.erl
index a92251e1af..c675f704b4 100644
--- a/lib/debugger/test/erl_eval_SUITE.erl
+++ b/lib/debugger/test/erl_eval_SUITE.erl
@@ -216,13 +216,13 @@ guard_4(doc) ->
guard_4(suite) ->
[];
guard_4(Config) when is_list(Config) ->
- ?line check(fun() -> if {erlang,'+'}(3,a) -> true ; true -> false end end,
- "if {erlang,'+'}(3,a) -> true ; true -> false end.",
- false),
- ?line check(fun() -> if {erlang,is_integer}(3) -> true ; true -> false end
- end,
- "if {erlang,is_integer}(3) -> true ; true -> false end.",
- true),
+ check(fun() -> if erlang:'+'(3,a) -> true ; true -> false end end,
+ "if erlang:'+'(3,a) -> true ; true -> false end.",
+ false),
+ check(fun() -> if erlang:is_integer(3) -> true ; true -> false end
+ end,
+ "if erlang:is_integer(3) -> true ; true -> false end.",
+ true),
?line check(fun() -> [X || X <- [1,2,3], erlang:is_integer(X)] end,
"[X || X <- [1,2,3], erlang:is_integer(X)].",
[1,2,3]),
@@ -230,11 +230,11 @@ guard_4(Config) when is_list(Config) ->
end,
"if is_atom(is_integer(a)) -> true ; true -> false end.",
true),
- ?line check(fun() -> if {erlang,is_atom}({erlang,is_integer}(a)) -> true;
- true -> false end end,
- "if {erlang,is_atom}({erlang,is_integer}(a)) -> true; "
- "true -> false end.",
- true),
+ check(fun() -> if erlang:is_atom(erlang:is_integer(a)) -> true;
+ true -> false end end,
+ "if erlang:is_atom(erlang:is_integer(a)) -> true; "
+ "true -> false end.",
+ true),
?line check(fun() -> if is_atom(3+a) -> true ; true -> false end end,
"if is_atom(3+a) -> true ; true -> false end.",
false),
@@ -1060,11 +1060,6 @@ do_funs(LFH, EFH) ->
concat(["begin F1 = fun(F,N) -> apply(", M,
",count_down,[F, N]) end, F1(F1,1000) end."]),
0, ['F1'], LFH, EFH),
- ?line check(fun() -> F1 = fun(F,N) -> {?MODULE,count_down}(F,N)
- end, F1(F1, 1000) end,
- concat(["begin F1 = fun(F,N) -> {", M,
- ",count_down}(F, N) end, F1(F1,1000) end."]),
- 0, ['F1'], LFH, EFH),
?line check(fun() -> F = fun(F,N) when N > 0 -> apply(F,[F,N-1]);
(_F,0) -> ok end,
F(F, 1000)
@@ -1096,11 +1091,6 @@ do_funs(LFH, EFH) ->
true = {2,3} == F(2) end,
"begin F = fun(X) -> A = 1+X, {X,A} end,
true = {2,3} == F(2) end.", true, ['F'], LFH, EFH),
- ?line check(fun() -> F = fun(X) -> {erlang,'+'}(X,2) end,
- true = 3 == F(1) end,
- "begin F = fun(X) -> {erlang,'+'}(X,2) end,"
- " true = 3 == F(1) end.", true, ['F'],
- LFH, EFH),
?line check(fun() -> F = fun(X) -> byte_size(X) end,
?MODULE:do_apply(F,<<"hej">>) end,
concat(["begin F = fun(X) -> size(X) end,",
diff --git a/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl b/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl
index c9ac6931e2..0d7883f067 100644
--- a/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl
+++ b/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl
@@ -76,12 +76,6 @@ apply_test(Fun) ->
[a,b,d] = ?MODULE:Func(same([a,b,c,d]), same([c])),
[d,e] = apply(Mod, Func, [same([d,e,f]), same([f])]),
[3] = apply(?MODULE, Func, [same([3,4]),same([4])]),
-
- %% This is obsolete, but it should work anyway.
- HomeMadeFun = {?MODULE,my_subtract},
- [a] = HomeMadeFun(same([a,x,c]), same([x,c])),
- [x] = apply(HomeMadeFun, [[x,y],[y,z]]),
-
ok.
number(X) -> {number,X}.
diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml
index 934cdba437..0c1556308c 100644
--- a/lib/dialyzer/doc/src/notes.xml
+++ b/lib/dialyzer/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2006</year><year>2011</year>
+ <year>2006</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,64 @@
<p>This document describes the changes made to the Dialyzer
application.</p>
+<section><title>Dialyzer 2.5.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Correct handling of type names in contracts. Fix
+ crash related to contract checking. Do not rewrite
+ unchanged PLT. </p>
+ <p>
+ Own Id: OTP-10083</p>
+ </item>
+ <item>
+ <p>
+ Stop a forgotten server process</p>
+ <p>
+ Dialyzer forgot to stop a server process before finishing
+ its analysis. This is a concurrency error detected by
+ Concuerror. Changes to fix warnings identified by running
+ dialyzer -Wunmatched_returns. Thanks to Kostis Sagonas.</p>
+ <p>
+ Own Id: OTP-10231</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Bug fixes and improvements of
+ <c>dialyzer_typesig</c>. </p>
+ <p>
+ Own Id: OTP-10082</p>
+ </item>
+ <item>
+ <p>
+ Add parallel dialyzer support</p>
+ <p>
+ Own Id: OTP-10103</p>
+ </item>
+ <item>
+ <p> An alternative implementation of the solver in
+ <c>dialyzer_typesig</c> has been introduced. It is faster
+ than the original implementation. </p>
+ <p>
+ Own Id: OTP-10110</p>
+ </item>
+ <item>
+ <p> Bugs in <c>erl_types:t_inf()</c> (HiPE) and in
+ <c>dialyzer_dataflow</c> (Dialyzer) have been fixed. </p>
+ <p>
+ Own Id: OTP-10191</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Dialyzer 2.5.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index 3e3c12405f..d875c45c4a 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. 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
@@ -380,8 +380,6 @@ message_to_string({pattern_match_cov, [Pat, Type]}) ->
message_to_string({unmatched_return, [Type]}) ->
io_lib:format("Expression produces a value of type ~s,"
" but this value is unmatched\n", [Type]);
-message_to_string({unused_fun, []}) ->
- io_lib:format("Function will never be called\n", []);
message_to_string({unused_fun, [F, A]}) ->
io_lib:format("Function ~w/~w will never be called\n", [F, A]);
%%----- Warnings for specs and contracts -------------------
diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl
index 1b999a7b99..105a174e31 100644
--- a/lib/dialyzer/src/dialyzer.hrl
+++ b/lib/dialyzer/src/dialyzer.hrl
@@ -2,7 +2,7 @@
%%%
%%% %CopyrightBegin%
%%%
-%%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%%% Copyright Ericsson AB 2006-2012. 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
@@ -111,6 +111,7 @@
-type rep_mode() :: 'quiet' | 'normal' | 'verbose'.
-type start_from() :: 'byte_code' | 'src_code'.
-type mfa_or_funlbl() :: label() | mfa().
+-type solver() :: 'v1' | 'v2'.
%%--------------------------------------------------------------------
%% Record declarations used by various files
@@ -129,7 +130,8 @@
behaviours_chk = false :: boolean(),
timing = false :: boolean() | 'debug',
timing_server :: dialyzer_timing:timing_server(),
- callgraph_file = "" :: file:filename()}).
+ callgraph_file = "" :: file:filename(),
+ solvers :: [solver()]}).
-record(options, {files = [] :: [file:filename()],
files_rec = [] :: [file:filename()],
@@ -149,7 +151,8 @@
output_format = formatted :: format(),
filename_opt = basename :: fopt(),
callgraph_file = "" :: file:filename(),
- check_plt = true :: boolean()}).
+ check_plt = true :: boolean(),
+ solvers = [] :: [solver()]}).
-record(contract, {contracts = [] :: [contract_pair()],
args = [] :: [erl_types:erl_type()],
diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
index 3bbde12481..c237d4e0e9 100644
--- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
@@ -53,7 +53,8 @@
plt :: dialyzer_plt:plt(),
start_from = byte_code :: start_from(),
use_contracts = true :: boolean(),
- timing_server :: dialyzer_timing:timing_server()
+ timing_server :: dialyzer_timing:timing_server(),
+ solvers :: [solver()]
}).
-record(server_state, {parent :: pid(), legal_warnings :: [dial_warn_tag()]}).
@@ -136,7 +137,8 @@ analysis_start(Parent, Analysis) ->
parent = Parent,
start_from = Analysis#analysis.start_from,
use_contracts = Analysis#analysis.use_contracts,
- timing_server = Analysis#analysis.timing_server
+ timing_server = Analysis#analysis.timing_server,
+ solvers = Analysis#analysis.solvers
},
Files = ordsets:from_list(Analysis#analysis.files),
{Callgraph, NoWarn, TmpCServer0} = compile_and_store(Files, State),
@@ -182,6 +184,7 @@ analysis_start(Parent, Analysis) ->
false -> Callgraph
end,
State3 = analyze_callgraph(NewCallgraph, State2#analysis_state{plt = Plt1}),
+ dialyzer_callgraph:dispose_race_server(NewCallgraph),
rcv_and_send_ext_types(Parent),
NonExports = sets:subtract(sets:from_list(AllNodes), Exports),
NonExportsList = sets:to_list(NonExports),
@@ -192,20 +195,21 @@ analysis_start(Parent, Analysis) ->
analyze_callgraph(Callgraph, #analysis_state{codeserver = Codeserver,
doc_plt = DocPlt,
timing_server = TimingServer,
- parent = Parent} = State) ->
+ parent = Parent,
+ solvers = Solvers} = State) ->
Plt = dialyzer_plt:insert_callbacks(State#analysis_state.plt, Codeserver),
{NewPlt, NewDocPlt} =
case State#analysis_state.analysis_type of
plt_build ->
NewPlt0 =
dialyzer_succ_typings:analyze_callgraph(Callgraph, Plt, Codeserver,
- TimingServer, Parent),
+ TimingServer, Solvers, Parent),
{NewPlt0, DocPlt};
succ_typings ->
NoWarn = State#analysis_state.no_warn_unused,
{Warnings, NewPlt0, NewDocPlt0} =
dialyzer_succ_typings:get_warnings(Callgraph, Plt, DocPlt, Codeserver,
- NoWarn, TimingServer, Parent),
+ NoWarn, TimingServer, Solvers, Parent),
send_warnings(State#analysis_state.parent, Warnings),
{NewPlt0, NewDocPlt0}
end,
@@ -516,9 +520,10 @@ rcv_and_send_ext_types(Parent) ->
Self ! {Self, done},
case rcv_ext_types(Self, []) of
[] -> ok;
- ExtTypes -> Parent ! {Self, ext_types, ExtTypes}
- end,
- ok.
+ ExtTypes ->
+ Parent ! {Self, ext_types, ExtTypes},
+ ok
+ end.
rcv_ext_types(Self, ExtTypes) ->
receive
diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl
index 5d253e77fa..6732d96b98 100644
--- a/lib/dialyzer/src/dialyzer_cl.erl
+++ b/lib/dialyzer/src/dialyzer_cl.erl
@@ -2,7 +2,7 @@
%%-------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. 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
@@ -398,7 +398,8 @@ do_analysis(Files, Options, Plt, PltInfo) ->
timing = Options#options.timing,
plt = Plt,
use_contracts = Options#options.use_contracts,
- callgraph_file = Options#options.callgraph_file},
+ callgraph_file = Options#options.callgraph_file,
+ solvers = Options#options.solvers},
State3 = start_analysis(State2, InitAnalysis),
{T1, _} = statistics(wall_clock),
Return = cl_loop(State3),
diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl
index 205b97ccf9..2ea3d3af5a 100644
--- a/lib/dialyzer/src/dialyzer_cl_parse.erl
+++ b/lib/dialyzer/src/dialyzer_cl_parse.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. 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
@@ -198,6 +198,9 @@ cl(["--gui"|T]) ->
cl(["--wx"|T]) ->
put(dialyzer_options_mode, {gui, wx}),
cl(T);
+cl(["--solver",Solver|T]) -> % not documented
+ append_var(dialyzer_solvers, [list_to_atom(Solver)]),
+ cl(T);
cl([H|_] = L) ->
case filelib:is_file(H) orelse filelib:is_dir(H) of
true ->
@@ -258,6 +261,7 @@ init() ->
put(dialyzer_filename_opt, basename),
put(dialyzer_options_check_plt, DefaultOpts#options.check_plt),
put(dialyzer_timing, DefaultOpts#options.timing),
+ put(dialyzer_solvers, DefaultOpts#options.solvers),
ok.
append_defines([Def, Val]) ->
@@ -311,7 +315,8 @@ common_options() ->
{report_mode, get(dialyzer_options_report_mode)},
{use_spec, get(dialyzer_options_use_contracts)},
{warnings, get(dialyzer_warnings)},
- {check_plt, get(dialyzer_options_check_plt)}].
+ {check_plt, get(dialyzer_options_check_plt)},
+ {solvers, get(dialyzer_solvers)}].
%%-----------------------------------------------------------------------
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index 76f23d00b9..0b932d5a1f 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl
index 5719132215..c3b7ce29af 100644
--- a/lib/dialyzer/src/dialyzer_coordinator.erl
+++ b/lib/dialyzer/src/dialyzer_coordinator.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. 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
@@ -116,7 +116,7 @@ spawn_jobs(Mode, Jobs, InitData, Timing) ->
fun(Job, Count) ->
Pid = dialyzer_worker:launch(Mode, Job, InitData, Coordinator),
case TypesigOrDataflow of
- true -> true = ets:insert(SCCtoPID, {Job, Pid});
+ true -> true = ets:insert(SCCtoPID, {Job, Pid}), ok;
false -> request_activation(Regulator, Pid)
end,
Count + 1
@@ -217,7 +217,8 @@ request_activation({_Collector, Regulator, _SCCtoPID}) ->
wait_activation().
request_activation(Regulator, Pid) ->
- Regulator ! {req, Pid}.
+ Regulator ! {req, Pid},
+ ok.
spawn_regulator() ->
InitTickets = dialyzer_utils:parallelism(),
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index cb376daf68..f40bb2d395 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -2468,14 +2468,18 @@ join_maps(Maps, MapOut) ->
Keys = ordsets:from_list(dict:fetch_keys(Dict) ++ dict:fetch_keys(Subst)),
join_maps(Keys, Maps, MapOut).
-join_maps([Key|Left], Maps, MapOut) ->
+join_maps(Keys, Maps, MapOut) ->
+ KTs = join_maps_collect(Keys, Maps, MapOut),
+ lists:foldl(fun({K, T}, M) -> enter_type(K, T, M) end, MapOut, KTs).
+
+join_maps_collect([Key|Left], Maps, MapOut) ->
Type = join_maps_one_key(Maps, Key, t_none()),
case t_is_equal(lookup_type(Key, MapOut), Type) of
- true -> join_maps(Left, Maps, MapOut);
- false -> join_maps(Left, Maps, enter_type(Key, Type, MapOut))
+ true -> join_maps_collect(Left, Maps, MapOut);
+ false -> [{Key, Type} | join_maps_collect(Left, Maps, MapOut)]
end;
-join_maps([], _Maps, MapOut) ->
- MapOut.
+join_maps_collect([], _Maps, _MapOut) ->
+ [].
join_maps_one_key([Map|Left], Key, AccType) ->
case t_is_any(AccType) of
@@ -2802,7 +2806,7 @@ state__get_warnings(#state{tree_map = TreeMap, fun_tab = FunTab,
true ->
{Warn, Msg} =
case dialyzer_callgraph:lookup_name(FunLbl, Callgraph) of
- error -> {true, {unused_fun, []}};
+ error -> {false, {}};
{ok, {_M, F, A} = MFA} ->
{not sets:is_element(MFA, NoWarnUnused),
{unused_fun, [F, A]}}
diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl
index b26aa695bd..c6f7c56227 100644
--- a/lib/dialyzer/src/dialyzer_gui_wx.erl
+++ b/lib/dialyzer/src/dialyzer_gui_wx.erl
@@ -192,48 +192,50 @@ create_window(Wx, #options{init_plts = InitPltFiles} = DialyzerOptions) ->
RunButtons = wxBoxSizer:new(?wxHORIZONTAL),
Buttons = wxFlexGridSizer:new(3),
- wxSizer:add(ChooseButtons, DeleteButton, ?BorderOpt),
- wxSizer:add(ChooseButtons, DeleteAllButton, ?BorderOpt),
- wxSizer:add(ChooseItem, Lab1, Center),
- wxSizer:add(ChooseItem, ChosenBox, Opts),
- wxSizer:add(ChooseItem, ChooseButtons, ?BorderOpt),
- wxSizer:add(FileTypeItem, OptionsLabel),
- wxSizer:add(FileTypeItem, FileType, [{border, 5}, {flag, ?wxALL}]),
- wxSizer:add(LogItem, LogLabel, Center),
- wxSizer:add(LogItem, LogBox, Opts3),
- wxSizer:add(LogItem, ClearLogButton, ?BorderOpt),
- wxSizer:add(FileItem, FileLabel),
- wxSizer:add(FileItem, FilePicker),
- wxSizer:add(DirItem, DirLabel),
- wxSizer:add(DirItem, DirPicker),
- wxSizer:add(AddDirButtons, AddDirButton, ?BorderOpt),
- wxSizer:add(AddDirButtons, AddRecButton, ?BorderOpt),
- wxSizer:add(FileDirItem, FileItem),
- wxSizer:add(FileDirItem, AddButton, ?BorderOpt),
- wxSizer:add(FileDirItem, DirItem, ?BorderOpt),
- wxSizer:add(FileDirItem, AddDirButtons, ?BorderOpt),
- wxSizer:add(WarnButtons, ExplainWarnButton, ?BorderOpt),
- wxSizer:add(WarnButtons, ClearWarningsButton, ?BorderOpt),
- wxSizer:add(RunButtons, RunButton, ?BorderOpt),
- wxSizer:add(RunButtons, StopButton, ?BorderOpt),
- wxSizer:add(Buttons, WarnButtons),
- wxSizer:add(Buttons, wxStaticText:new(Frame, ?LABEL7, ""), [{flag, ?wxEXPAND}]),
- wxSizer:add(Buttons, RunButtons),
- wxFlexGridSizer:addGrowableCol(Buttons, 1),
- wxSizer:add(WarningsItem, WarningsLabel, Center),
- wxSizer:add(WarningsItem, WarningsBox, Opts3),
- wxSizer:add(WarningsItem, Buttons, [{flag, ?wxEXPAND bor ?wxALL},?Border]),
-
- wxSizer:add(Left, ChooseItem, Opts),
- wxSizer:add(Left, FileDirItem, [{proportion, 1}, {border, 60}, {flag, ?wxTOP}]),
- wxSizer:add(RightUp, FileTypeItem, ?BorderOpt),
- wxSizer:add(RightUp, LogItem, Opts3),
- wxSizer:add(Right, RightUp, Opts3),
- wxSizer:add(Right, WarningsItem, Opts3),
- wxSizer:add(Top, Left, Opts),
- wxSizer:add(Top, Right, Opts3),
-
- wxSizer:add(All, Top, Opts),
+ _ = wxSizer:add(ChooseButtons, DeleteButton, ?BorderOpt),
+ _ = wxSizer:add(ChooseButtons, DeleteAllButton, ?BorderOpt),
+ _ = wxSizer:add(ChooseItem, Lab1, Center),
+ _ = wxSizer:add(ChooseItem, ChosenBox, Opts),
+ _ = wxSizer:add(ChooseItem, ChooseButtons, ?BorderOpt),
+ _ = wxSizer:add(FileTypeItem, OptionsLabel),
+ _ = wxSizer:add(FileTypeItem, FileType, [{border, 5}, {flag, ?wxALL}]),
+ _ = wxSizer:add(LogItem, LogLabel, Center),
+ _ = wxSizer:add(LogItem, LogBox, Opts3),
+ _ = wxSizer:add(LogItem, ClearLogButton, ?BorderOpt),
+ _ = wxSizer:add(FileItem, FileLabel),
+ _ = wxSizer:add(FileItem, FilePicker),
+ _ = wxSizer:add(DirItem, DirLabel),
+ _ = wxSizer:add(DirItem, DirPicker),
+ _ = wxSizer:add(AddDirButtons, AddDirButton, ?BorderOpt),
+ _ = wxSizer:add(AddDirButtons, AddRecButton, ?BorderOpt),
+ _ = wxSizer:add(FileDirItem, FileItem),
+ _ = wxSizer:add(FileDirItem, AddButton, ?BorderOpt),
+ _ = wxSizer:add(FileDirItem, DirItem, ?BorderOpt),
+ _ = wxSizer:add(FileDirItem, AddDirButtons, ?BorderOpt),
+ _ = wxSizer:add(WarnButtons, ExplainWarnButton, ?BorderOpt),
+ _ = wxSizer:add(WarnButtons, ClearWarningsButton, ?BorderOpt),
+ _ = wxSizer:add(RunButtons, RunButton, ?BorderOpt),
+ _ = wxSizer:add(RunButtons, StopButton, ?BorderOpt),
+ _ = wxSizer:add(Buttons, WarnButtons),
+ _ = wxSizer:add(Buttons, wxStaticText:new(Frame, ?LABEL7, ""),
+ [{flag, ?wxEXPAND}]),
+ _ = wxSizer:add(Buttons, RunButtons),
+ _ = wxFlexGridSizer:addGrowableCol(Buttons, 1),
+ _ = wxSizer:add(WarningsItem, WarningsLabel, Center),
+ _ = wxSizer:add(WarningsItem, WarningsBox, Opts3),
+ _ = wxSizer:add(WarningsItem, Buttons,
+ [{flag, ?wxEXPAND bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Left, ChooseItem, Opts),
+ _ = wxSizer:add(Left, FileDirItem,
+ [{proportion, 1}, {border, 60}, {flag, ?wxTOP}]),
+ _ = wxSizer:add(RightUp, FileTypeItem, ?BorderOpt),
+ _ = wxSizer:add(RightUp, LogItem, Opts3),
+ _ = wxSizer:add(Right, RightUp, Opts3),
+ _ = wxSizer:add(Right, WarningsItem, Opts3),
+ _ = wxSizer:add(Top, Left, Opts),
+ _ = wxSizer:add(Top, Right, Opts3),
+
+ _ = wxSizer:add(All, Top, Opts),
wxWindow:setSizer(Frame, All),
wxWindow:setSizeHints(Frame, {1150,600}),
wxWindow:show(Frame),
@@ -294,91 +296,67 @@ create_window(Wx, #options{init_plts = InitPltFiles} = DialyzerOptions) ->
createFileMenu() ->
FileMenu = wxMenu:new(),
- wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_WARNINGS},
- {text, "Save &Warnings"}])),
- wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_LOG},
- {text, "Save &Log"}])),
- wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_QUIT},
- {text, "E&xit\tAlt-X"}])),
+ _ = wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_WARNINGS},
+ {text, "Save &Warnings"}])),
+ _ = wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_LOG},
+ {text, "Save &Log"}])),
+ _ = wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_QUIT},
+ {text, "E&xit\tAlt-X"}])),
FileMenu.
createWarningsMenu() ->
WarningsMenu = wxMenu:new(),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_MATCH_FAILURES,
- "Match failures"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_MATCH_FAILURES, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_FAIL_FUN_CALLS,
- "Failing function calls"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_FAIL_FUN_CALLS, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_BAD_FUN,
- "Bad fun applications"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_BAD_FUN, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_OPAQUE,
- "Opaqueness violations"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_OPAQUE, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_LIST_CONSTR,
- "Improper list constructions"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_LIST_CONSTR, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_UNUSED_FUN,
- "Unused functions"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_UNUSED_FUN, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_ERROR_HANDLING_FUN,
- "Error handling functions"),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_NO_RETURN_FUN,
- "Functions of no return"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_NO_RETURN_FUN, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_UNEXPORTED_FUN,
- "Call to unexported function"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_UNEXPORTED_FUN, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_RACE_CONDITIONS,
- "Possible race conditions"),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_WRONG_CONTRACTS,
- "Wrong contracts"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_WRONG_CONTRACTS, true),
- wxMenu:appendCheckItem(WarningsMenu,
- ?menuID_WARN_CONTRACT_SYNTAX,
- "Wrong contract syntax"),
- wxMenu:check(WarningsMenu, ?menuID_WARN_CONTRACT_SYNTAX, true),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_MATCH_FAILURES, "Match failures"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_FAIL_FUN_CALLS,
+ "Failing function calls"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_BAD_FUN, "Bad fun applications"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_OPAQUE, "Opaqueness violations"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_LIST_CONSTR,
+ "Improper list constructions"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_UNUSED_FUN, "Unused functions"),
+ _ = wxMenu:appendCheckItem(WarningsMenu, ?menuID_WARN_ERROR_HANDLING_FUN,
+ "Error handling functions"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_NO_RETURN_FUN,
+ "Functions of no return"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_UNEXPORTED_FUN,
+ "Call to unexported function"),
+ _ = wxMenu:appendCheckItem(WarningsMenu, ?menuID_WARN_RACE_CONDITIONS,
+ "Possible race conditions"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_WRONG_CONTRACTS, "Wrong contracts"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_CONTRACT_SYNTAX,
+ "Wrong contract syntax"),
WarningsMenu.
+addCheckedItem(Menu, ItemId, Str) ->
+ _ = wxMenu:appendCheckItem(Menu, ItemId, Str),
+ wxMenu:check(Menu, ItemId, true).
+
createPltMenu() ->
PltMenu = wxMenu:new(),
- wxMenu:appendCheckItem(PltMenu,
- ?menuID_PLT_INIT_EMPTY,
- "Init with empty PLT"),
- wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SHOW_CONTENTS},
- {text, "Show contents"}])),
- wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SEARCH_CONTENTS},
- {text, "Search contents"}])),
+ _ = wxMenu:appendCheckItem(PltMenu, ?menuID_PLT_INIT_EMPTY,
+ "Init with empty PLT"),
+ _ = wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SHOW_CONTENTS},
+ {text, "Show contents"}])),
+ _ = wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SEARCH_CONTENTS},
+ {text, "Search contents"}])),
PltMenu.
createOptionsMenu() ->
OptsMenu = wxMenu:new(),
- wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_MACRO},
- {text, "Manage Macro Definitions"}])),
- wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_INCLUDE_DIR},
- {text, "Manage Include Directories"}])),
+ _ = wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_MACRO},
+ {text, "Manage Macro Definitions"}])),
+ _ = wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_INCLUDE_DIR},
+ {text, "Manage Include Directories"}])),
OptsMenu.
createHelpMenu() ->
HelpMenu = wxMenu:new(),
- wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_MANUAL},
- {text, "Manual"}])),
- wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_WARNING_OPTIONS},
- {text, "Warning Options"}])),
- wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_ABOUT},
- {text, "About"}])),
+ _ = wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_MANUAL},
+ {text, "Manual"}])),
+ _ = wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_WARNING_OPTIONS},
+ {text, "Warning Options"}])),
+ _ = wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_ABOUT},
+ {text, "About"}])),
HelpMenu.
%% ----------------------------------------------------------------
@@ -583,20 +561,20 @@ search_doc_plt(#gui_state{gui = Wx} = State) ->
ArLayout = wxBoxSizer:new(?wxVERTICAL),
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
- wxSizer:add(ModLayout, ModLabel, ?BorderOpt),
- wxSizer:add(ModLayout,ModText, ?BorderOpt),
- wxSizer:add(FunLayout, FunLabel, ?BorderOpt),
- wxSizer:add(FunLayout,FunText, ?BorderOpt),
- wxSizer:add(ArLayout, ArLabel, ?BorderOpt),
- wxSizer:add(ArLayout,ArText, ?BorderOpt),
- wxSizer:add(Buttons, SearchButton, ?BorderOpt),
- wxSizer:add(Buttons,Cancel, ?BorderOpt),
-
- wxSizer:add(Top, ModLayout),
- wxSizer:add(Top, FunLayout),
- wxSizer:add(Top, ArLayout),
- wxSizer:add(Layout, Top,[{flag, ?wxALIGN_CENTER}]),
- wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER bor ?wxBOTTOM}]),
+ _ = wxSizer:add(ModLayout, ModLabel, ?BorderOpt),
+ _ = wxSizer:add(ModLayout, ModText, ?BorderOpt),
+ _ = wxSizer:add(FunLayout, FunLabel, ?BorderOpt),
+ _ = wxSizer:add(FunLayout,FunText, ?BorderOpt),
+ _ = wxSizer:add(ArLayout, ArLabel, ?BorderOpt),
+ _ = wxSizer:add(ArLayout,ArText, ?BorderOpt),
+ _ = wxSizer:add(Buttons, SearchButton, ?BorderOpt),
+ _ = wxSizer:add(Buttons,Cancel, ?BorderOpt),
+
+ _ = wxSizer:add(Top, ModLayout),
+ _ = wxSizer:add(Top, FunLayout),
+ _ = wxSizer:add(Top, ArLayout),
+ _ = wxSizer:add(Layout, Top,[{flag, ?wxALIGN_CENTER}]),
+ _ = wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER bor ?wxBOTTOM}]),
wxFrame:connect(Dialog, close_window),
wxWindow:setSizer(Dialog, Layout),
wxFrame:show(Dialog),
@@ -654,14 +632,15 @@ error_sms(State, Message) ->
output_sms(State, ?DIALYZER_ERROR_TITLE, Message, error).
output_sms(#gui_state{frame = Frame}, Title, Message, Type) ->
- case Type of
- error ->
- MessageWin = wxMessageDialog:new(Frame,Message,[{caption, Title},{style, ?wxOK bor ?wxICON_ERROR}]);
- info ->
- MessageWin = wxMessageDialog:new(Frame,Message,[{caption, Title},{style, ?wxOK bor ?wxICON_INFORMATION}])
- end,
+ Style = case Type of
+ error -> ?wxOK bor ?wxICON_ERROR;
+ info -> ?wxOK bor ?wxICON_INFORMATION
+ end,
+ Options = [{caption, Title}, {style, Style}],
+ MessageWin = wxMessageDialog:new(Frame, Message, Options),
wxWindow:setSizeHints(MessageWin, {350,100}),
- wxDialog:showModal(MessageWin).
+ wxDialog:showModal(MessageWin),
+ ok.
free_editor(#gui_state{gui = Wx, frame = Frame}, Title, Contents0) ->
Contents = lists:flatten(Contents0),
@@ -685,8 +664,9 @@ free_editor(#gui_state{gui = Wx, frame = Frame}, Title, Contents0) ->
wxButton:connect(Ok, command_button_clicked),
Layout = wxBoxSizer:new(?wxVERTICAL),
- wxSizer:add(Layout, Editor, ?BorderOpt),
- wxSizer:add(Layout, Ok, [{flag, ?wxALIGN_CENTER bor ?wxBOTTOM bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Layout, Editor, ?BorderOpt),
+ Flag = ?wxALIGN_CENTER bor ?wxBOTTOM bor ?wxALL,
+ _ = wxSizer:add(Layout, Ok, [{flag, Flag}, ?Border]),
wxWindow:setSizer(Win, Layout),
wxWindow:show(Win),
show_info_loop(Frame, Win).
@@ -696,7 +676,7 @@ show_info_loop(Frame, Win) ->
#wx{id = ?Message_Ok, event = #wxCommand{type = command_button_clicked}} ->
wxWindow:destroy(Win);
#wx{id = ?Message, event = #wxClose{type = close_window}} ->
- wxWindow:destroy(Win);
+ wxWindow:destroy(Win);
#wx{event = #wxClose{type = close_window}} ->
wxWindow:destroy(Frame)
end.
@@ -921,13 +901,14 @@ save_file(#gui_state{frame = Frame, warnings_box = WBox, log = Log} = State, Typ
{message, Message},
{style,?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}]),
case wxFileDialog:showModal(FileDialog) of
- ?wxID_OK -> Path = wxFileDialog:getPath(FileDialog),
- case wxTextCtrl:saveFile(Box,[{file,Path}]) of
- true -> ok;
- false -> error_sms(State,"Could not write to file:\n" ++ Path)
- end;
+ ?wxID_OK ->
+ Path = wxFileDialog:getPath(FileDialog),
+ case wxTextCtrl:saveFile(Box,[{file,Path}]) of
+ true -> ok;
+ false -> error_sms(State, "Could not write to file:\n" ++ Path)
+ end;
?wxID_CANCEL -> wxWindow:destroy(FileDialog);
- _ -> error_sms(State,"Could not write to file:\n")
+ _ -> error_sms(State, "Could not write to file:\n")
end
end.
@@ -961,16 +942,16 @@ include_dialog(#gui_state{gui = Wx, frame = Frame, options = Options}) ->
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
Buttons1 = wxBoxSizer:new(?wxHORIZONTAL),
- wxSizer:add(Layout, DirLabel, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
- wxSizer:add(Layout, DirPicker, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
- wxSizer:add(Layout,AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
- wxSizer:add(Layout,Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
- wxSizer:add(Buttons, DeleteButton, ?BorderOpt),
- wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt),
- wxSizer:add(Layout,Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
- wxSizer:add(Buttons1, Ok, ?BorderOpt),
- wxSizer:add(Buttons1,Cancel, ?BorderOpt),
- wxSizer:add(Layout,Buttons1,[{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]),
+ _ = wxSizer:add(Layout, DirLabel, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Layout, DirPicker, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Layout,AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Layout,Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Buttons, DeleteButton, ?BorderOpt),
+ _ = wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt),
+ _ = wxSizer:add(Layout,Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Buttons1, Ok, ?BorderOpt),
+ _ = wxSizer:add(Buttons1,Cancel, ?BorderOpt),
+ _ = wxSizer:add(Layout,Buttons1,[{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]),
wxFrame:connect(Dialog, close_window),
wxWindow:setSizer(Dialog, Layout),
@@ -1058,21 +1039,21 @@ macro_dialog(#gui_state{gui = Wx, frame = Frame, options = Options}) ->
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
Buttons1 = wxBoxSizer:new(?wxHORIZONTAL),
- wxSizer:add(MacroItem, MacroLabel, ?BorderOpt),
- wxSizer:add(MacroItem, MacroText, ?BorderOpt),
- wxSizer:add(TermItem, TermLabel, ?BorderOpt),
- wxSizer:add(TermItem, TermText, ?BorderOpt),
- wxSizer:add(Item, MacroItem),
- wxSizer:add(Item, TermItem),
- wxSizer:add(Layout, Item, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
- wxSizer:add(Layout, AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
- wxSizer:add(Layout, Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
- wxSizer:add(Buttons, DeleteButton, ?BorderOpt),
- wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt),
- wxSizer:add(Layout, Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
- wxSizer:add(Buttons1, Ok, ?BorderOpt),
- wxSizer:add(Buttons1, Cancel, ?BorderOpt),
- wxSizer:add(Layout, Buttons1, [{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]),
+ _ = wxSizer:add(MacroItem, MacroLabel, ?BorderOpt),
+ _ = wxSizer:add(MacroItem, MacroText, ?BorderOpt),
+ _ = wxSizer:add(TermItem, TermLabel, ?BorderOpt),
+ _ = wxSizer:add(TermItem, TermText, ?BorderOpt),
+ _ = wxSizer:add(Item, MacroItem),
+ _ = wxSizer:add(Item, TermItem),
+ _ = wxSizer:add(Layout, Item, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Layout, AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Layout, Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Buttons, DeleteButton, ?BorderOpt),
+ _ = wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt),
+ _ = wxSizer:add(Layout, Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Buttons1, Ok, ?BorderOpt),
+ _ = wxSizer:add(Buttons1, Cancel, ?BorderOpt),
+ _ = wxSizer:add(Layout, Buttons1, [{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]),
wxFrame:connect(Dialog, close_window),
wxWindow:setSizer(Dialog, Layout),
@@ -1216,13 +1197,14 @@ show_explanation(#gui_state{gui = Wx} = State, Explanation) ->
wxButton:connect(Ok, command_button_clicked),
Layout = wxBoxSizer:new(?wxVERTICAL),
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
- wxSizer:add(Buttons, ExplButton, ?BorderOpt),
- wxSizer:add(Buttons, Ok, ?BorderOpt),
- wxSizer:add(Layout, Editor,[{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
- wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
+ _ = wxSizer:add(Buttons, ExplButton, ?BorderOpt),
+ _ = wxSizer:add(Buttons, Ok, ?BorderOpt),
+ _ = wxSizer:add(Layout, Editor, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
+ _ = wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
wxWindow:setSizer(Win, Layout),
wxWindow:show(Win),
- show_explanation_loop(State#gui_state{explanation_box = Editor}, Win, Explanation)
+ NewState = State#gui_state{explanation_box = Editor},
+ show_explanation_loop(NewState, Win, Explanation)
end.
show_explanation_loop(#gui_state{frame = Frame, expl_pid = ExplPid} = State, Win, Explanation) ->
diff --git a/lib/dialyzer/src/dialyzer_options.erl b/lib/dialyzer/src/dialyzer_options.erl
index a1e316d6cc..06672e595f 100644
--- a/lib/dialyzer/src/dialyzer_options.erl
+++ b/lib/dialyzer/src/dialyzer_options.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. 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
@@ -196,6 +196,9 @@ build_options([{OptionName, Value} = Term|Rest], Options) ->
build_options(Rest, Options#options{callgraph_file = Value});
timing ->
build_options(Rest, Options#options{timing = Value});
+ solvers ->
+ assert_solvers(Value),
+ build_options(Rest, Options#options{solvers = Value});
_ ->
bad_option("Unknown dialyzer command line option", Term)
end;
@@ -257,6 +260,15 @@ is_plt_mode(plt_remove) -> true;
is_plt_mode(plt_check) -> true;
is_plt_mode(succ_typings) -> false.
+assert_solvers([]) ->
+ ok;
+assert_solvers([v1|Terms]) ->
+ assert_solvers(Terms);
+assert_solvers([v2|Terms]) ->
+ assert_solvers(Terms);
+assert_solvers([Term|_]) ->
+ bad_option("Illegal value for solver", Term).
+
-spec build_warnings([atom()], [dial_warning()]) -> [dial_warning()].
build_warnings([Opt|Opts], Warnings) ->
diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl
index 9ca5a66dab..84379642bf 100644
--- a/lib/dialyzer/src/dialyzer_succ_typings.erl
+++ b/lib/dialyzer/src/dialyzer_succ_typings.erl
@@ -28,8 +28,8 @@
-module(dialyzer_succ_typings).
-export([analyze_callgraph/3,
- analyze_callgraph/5,
- get_warnings/7
+ analyze_callgraph/6,
+ get_warnings/8
]).
-export([
@@ -75,6 +75,7 @@
no_warn_unused :: set(),
parent = none :: parent(),
timing_server :: dialyzer_timing:timing_server(),
+ solvers :: [solver()],
plt :: dialyzer_plt:plt()}).
%%--------------------------------------------------------------------
@@ -84,28 +85,29 @@
dialyzer_plt:plt().
analyze_callgraph(Callgraph, Plt, Codeserver) ->
- analyze_callgraph(Callgraph, Plt, Codeserver, none, none).
+ analyze_callgraph(Callgraph, Plt, Codeserver, none, [], none).
-spec analyze_callgraph(dialyzer_callgraph:callgraph(), dialyzer_plt:plt(),
dialyzer_codeserver:codeserver(),
- dialyzer_timing:timing_server(), parent()) ->
+ dialyzer_timing:timing_server(),
+ [solver()], parent()) ->
dialyzer_plt:plt().
-analyze_callgraph(Callgraph, Plt, Codeserver, TimingServer, Parent) ->
+analyze_callgraph(Callgraph, Plt, Codeserver, TimingServer, Solvers, Parent) ->
NewState =
init_state_and_get_success_typings(Callgraph, Plt, Codeserver,
- TimingServer, Parent),
+ TimingServer, Solvers, Parent),
dialyzer_plt:restore_full_plt(NewState#st.plt, Plt).
%%--------------------------------------------------------------------
init_state_and_get_success_typings(Callgraph, Plt, Codeserver,
- TimingServer, Parent) ->
+ TimingServer, Solvers, Parent) ->
{SCCs, Callgraph1} =
?timing(TimingServer, "order", dialyzer_callgraph:finalize(Callgraph)),
State = #st{callgraph = Callgraph1, plt = dialyzer_plt:get_mini_plt(Plt),
codeserver = Codeserver, parent = Parent,
- timing_server = TimingServer},
+ timing_server = TimingServer, solvers = Solvers},
get_refined_success_typings(SCCs, State).
get_refined_success_typings(SCCs, #st{callgraph = Callgraph,
@@ -136,14 +138,14 @@ get_refined_success_typings(SCCs, #st{callgraph = Callgraph,
-type doc_plt() :: 'undefined' | dialyzer_plt:plt().
-spec get_warnings(dialyzer_callgraph:callgraph(), dialyzer_plt:plt(),
doc_plt(), dialyzer_codeserver:codeserver(), set(),
- dialyzer_timing:timing_server(), pid()) ->
+ dialyzer_timing:timing_server(), [solver()], pid()) ->
{[dial_warning()], dialyzer_plt:plt(), doc_plt()}.
get_warnings(Callgraph, Plt, DocPlt, Codeserver,
- NoWarnUnused, TimingServer, Parent) ->
+ NoWarnUnused, TimingServer, Solvers, Parent) ->
InitState =
init_state_and_get_success_typings(Callgraph, Plt, Codeserver,
- TimingServer, Parent),
+ TimingServer, Solvers, Parent),
NewState = InitState#st{no_warn_unused = NoWarnUnused},
Mods = dialyzer_callgraph:modules(NewState#st.callgraph),
MiniPlt = NewState#st.plt,
@@ -222,9 +224,10 @@ postprocess_dataflow_warns([{?WARN_CONTRACT_RANGE, {CallF, CallL}, Msg}|Rest],
refine_succ_typings(Modules, #st{codeserver = Codeserver,
callgraph = Callgraph,
plt = Plt,
- timing_server = Timing} = State) ->
+ timing_server = Timing,
+ solvers = Solvers} = State) ->
?debug("Module postorder: ~p\n", [Modules]),
- Init = {Codeserver, Callgraph, Plt},
+ Init = {Codeserver, Callgraph, Plt, Solvers},
NotFixpoint =
?timing(Timing, "refine",
dialyzer_coordinator:parallel_job(dataflow, Modules, Init, Timing)),
@@ -236,22 +239,22 @@ refine_succ_typings(Modules, #st{codeserver = Codeserver,
-spec find_depends_on(scc() | module(), fixpoint_init_data()) -> [scc()].
-find_depends_on(SCC, {_Codeserver, Callgraph, _Plt}) ->
+find_depends_on(SCC, {_Codeserver, Callgraph, _Plt, _Solvers}) ->
dialyzer_callgraph:get_depends_on(SCC, Callgraph).
-spec find_required_by(scc() | module(), fixpoint_init_data()) -> [scc()].
-find_required_by(SCC, {_Codeserver, Callgraph, _Plt}) ->
+find_required_by(SCC, {_Codeserver, Callgraph, _Plt, _Solvers}) ->
dialyzer_callgraph:get_required_by(SCC, Callgraph).
-spec lookup_names([label()], fixpoint_init_data()) -> [mfa_or_funlbl()].
-lookup_names(Labels, {_Codeserver, Callgraph, _Plt}) ->
+lookup_names(Labels, {_Codeserver, Callgraph, _Plt, _Solvers}) ->
[lookup_name(F, Callgraph) || F <- Labels].
-spec refine_one_module(module(), dataflow_init_data()) -> [label()]. % ordset
-refine_one_module(M, {CodeServer, Callgraph, Plt}) ->
+refine_one_module(M, {CodeServer, Callgraph, Plt, _Solvers}) ->
ModCode = dialyzer_codeserver:lookup_mod_code(M, CodeServer),
AllFuns = collect_fun_info([ModCode]),
Records = dialyzer_codeserver:lookup_mod_records(M, CodeServer),
@@ -322,8 +325,9 @@ compare_types_1([], [], _Strict, NotFixpoint) ->
end.
find_succ_typings(SCCs, #st{codeserver = Codeserver, callgraph = Callgraph,
- plt = Plt, timing_server = Timing} = State) ->
- Init = {Codeserver, Callgraph, Plt},
+ plt = Plt, timing_server = Timing,
+ solvers = Solvers} = State) ->
+ Init = {Codeserver, Callgraph, Plt, Solvers},
NotFixpoint =
?timing(Timing, "typesig",
dialyzer_coordinator:parallel_job(typesig, SCCs, Init, Timing)),
@@ -335,7 +339,7 @@ find_succ_typings(SCCs, #st{codeserver = Codeserver, callgraph = Callgraph,
-spec find_succ_types_for_scc(scc(), typesig_init_data()) -> [mfa_or_funlbl()].
-find_succ_types_for_scc(SCC, {Codeserver, Callgraph, Plt}) ->
+find_succ_types_for_scc(SCC, {Codeserver, Callgraph, Plt, Solvers}) ->
SCC_Info = [{MFA,
dialyzer_codeserver:lookup_mfa_code(MFA, Codeserver),
dialyzer_codeserver:lookup_mod_records(M, Codeserver)}
@@ -348,8 +352,8 @@ find_succ_types_for_scc(SCC, {Codeserver, Callgraph, Plt}) ->
AllFuns = collect_fun_info([Fun || {_MFA, {_Var, Fun}, _Rec} <- SCC_Info]),
PropTypes = get_fun_types_from_plt(AllFuns, Callgraph, Plt),
%% Assume that the PLT contains the current propagated types
- FunTypes =
- dialyzer_typesig:analyze_scc(SCC_Info, Label, Callgraph, Plt, PropTypes),
+ FunTypes = dialyzer_typesig:analyze_scc(SCC_Info, Label, Callgraph,
+ Plt, PropTypes, Solvers),
AllFunSet = sets:from_list([X || {X, _} <- AllFuns]),
FilteredFunTypes =
dict:filter(fun(X, _) -> sets:is_element(X, AllFunSet) end, FunTypes),
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index e997eedf76..0df003a035 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -28,7 +28,7 @@
-module(dialyzer_typesig).
--export([analyze_scc/5]).
+-export([analyze_scc/6]).
-export([get_safe_underapprox/2]).
-import(erl_types,
@@ -78,6 +78,8 @@
-record(constraint_list, {type :: 'conj' | 'disj',
list :: [constr()],
deps :: [dep()],
+ masks :: [{dep(),[non_neg_integer()]}] |
+ {'d',dict()},
id :: {'list', dep()}}).
-type constraint_list() :: #constraint_list{}.
@@ -109,7 +111,8 @@
records = dict:new() :: dict(),
opaques = [] :: [erl_types:erl_type()],
scc = [] :: [type_var()],
- mfas :: [tuple()]
+ mfas :: [tuple()],
+ solvers = [] :: [solver()]
}).
%%-----------------------------------------------------------------------------
@@ -121,8 +124,10 @@
%%-define(DEBUG_CONSTRAINTS, true).
-ifdef(DEBUG).
-define(DEBUG_NAME_MAP, true).
+-define(DEBUG_LOOP_DETECTION, true).
-endif.
%%-define(DEBUG_NAME_MAP, true).
+%%-define(DEBUG_LOOP_DETECTION, true).
-ifdef(DEBUG).
-define(debug(__String, __Args), io:format(__String, __Args)).
@@ -141,7 +146,7 @@
%%-----------------------------------------------------------------------------
%% Analysis of strongly connected components.
%%
-%% analyze_scc(SCC, NextLabel, CallGraph, PLT, PropTypes) -> FunTypes
+%% analyze_scc(SCC, NextLabel, CallGraph, PLT, PropTypes, Solvers) -> FunTypes
%%
%% SCC - [{MFA, Def, Records}]
%% where Def = {Var, Fun} as in the Core Erlang module definitions.
@@ -154,15 +159,17 @@
%% about functions that can be called by this SCC.
%% PropTypes - A dictionary.
%% FunTypes - A dictionary.
+%% Solvers - User specified solvers.
%%-----------------------------------------------------------------------------
-spec analyze_scc(typesig_scc(), label(),
dialyzer_callgraph:callgraph(),
- dialyzer_plt:plt(), dict()) -> dict().
+ dialyzer_plt:plt(), dict(), [solver()]) -> dict().
-analyze_scc(SCC, NextLabel, CallGraph, Plt, PropTypes) ->
+analyze_scc(SCC, NextLabel, CallGraph, Plt, PropTypes, Solvers0) ->
+ Solvers = solvers(Solvers0),
assert_format_of_scc(SCC),
- State1 = new_state(SCC, NextLabel, CallGraph, Plt, PropTypes),
+ State1 = new_state(SCC, NextLabel, CallGraph, Plt, PropTypes, Solvers),
DefSet = add_def_list([Var || {_MFA, {Var, _Fun}, _Rec} <- SCC], sets:new()),
State2 = traverse_scc(SCC, DefSet, State1),
State3 = state__finalize(State2),
@@ -176,6 +183,9 @@ assert_format_of_scc([{_MFA, {_Var, _Fun}, _Records}|Left]) ->
assert_format_of_scc([]) ->
ok.
+solvers([]) -> [v2];
+solvers(Solvers) -> Solvers.
+
%% ============================================================================
%%
%% Gets the constraints by traversing the code.
@@ -1663,7 +1673,7 @@ get_bif_test_constr(Dst, Arg, Type, State) ->
solve([Fun], State) ->
?debug("============ Analyzing Fun: ~w ===========\n",
[debug_lookup_name(Fun)]),
- solve_fun(Fun, dict:new(), State);
+ solve_fun(Fun, map_new(), State);
solve([_|_] = SCC, State) ->
?debug("============ Analyzing SCC: ~w ===========\n",
[[debug_lookup_name(F) || F <- SCC]]),
@@ -1672,14 +1682,14 @@ solve([_|_] = SCC, State) ->
false -> {false, State};
SplitSCC -> {SplitSCC, minimize_state(State)}
end,
- solve_scc(SCC, Parallel, dict:new(), NewState, false).
+ solve_scc(SCC, Parallel, map_new(), NewState, false).
solve_fun(Fun, FunMap, State) ->
Cs = state__get_cs(Fun, State),
Deps = get_deps(Cs),
Ref = mk_constraint_ref(Fun, Deps),
%% Note that functions are always considered to succeed.
- {ok, _MapDict, NewMap} = solve_ref_or_list(Ref, FunMap, dict:new(), State),
+ NewMap = solve(Fun, Ref, FunMap, State),
NewType = lookup_type(Fun, NewMap),
NewFunMap1 = case state__get_rec_var(Fun, State) of
error -> FunMap;
@@ -1694,7 +1704,7 @@ solve_scc(SCC, Parallel, Map, State, TryingUnit) ->
Types = unsafe_lookup_type_list(Funs, Map),
RecTypes = [t_limit(Type, ?TYPE_LIMIT) || Type <- Types],
CleanMap = lists:foldl(fun(Fun, AccFunMap) ->
- dict:erase(t_var_name(Fun), AccFunMap)
+ erase_type(t_var_name(Fun), AccFunMap)
end, Map, SCC),
Map1 = enter_type_lists(Vars, RecTypes, CleanMap),
?debug("Checking SCC: ~w\n", [[debug_lookup_name(F) || F <- SCC]]),
@@ -1758,7 +1768,8 @@ minimize_state(#state{
fun_arities = FunArities,
self_rec = SelfRec,
prop_types = {d, PropTypes},
- opaques = Opaques
+ opaques = Opaques,
+ solvers = Solvers
}) ->
ETSCMap = ets:new(cmap,[{read_concurrency, true}]),
ETSPropTypes = ets:new(prop_types,[{read_concurrency, true}]),
@@ -1770,7 +1781,8 @@ minimize_state(#state{
fun_arities = FunArities,
self_rec = SelfRec,
prop_types = {e, ETSPropTypes},
- opaques = Opaques
+ opaques = Opaques,
+ solvers = Solvers
}.
dispose_state(#state{cmap = {e, ETSCMap},
@@ -1842,7 +1854,7 @@ scc_fold_fun(F, FunMap, State) ->
Deps = get_deps(state__get_cs(F, State)),
Cs = mk_constraint_ref(F, Deps),
%% Note that functions are always considered to succeed.
- {ok, _NewMapDict, Map} = solve_ref_or_list(Cs, FunMap, dict:new(), State),
+ Map = solve(F, Cs, FunMap, State),
NewType0 = unsafe_lookup_type(F, Map),
NewType = t_limit(NewType0, ?TYPE_LIMIT),
NewFunMap = case state__get_rec_var(F, State) of
@@ -1855,15 +1867,440 @@ scc_fold_fun(F, FunMap, State) ->
format_type(NewType)]),
NewFunMap.
+solve(Fun, Cs, FunMap, State) ->
+ Solvers = State#state.solvers,
+ R = [solver(S, solve_fun(S, Fun, Cs, FunMap, State)) || S <- Solvers],
+ check_solutions(R, Fun, no_solver, no_map).
+
+solver(Solver, SolveFun) ->
+ ?debug("Start solver ~w\n", [Solver]),
+ try timer:tc(SolveFun) of
+ {Time, {ok, Map}} ->
+ ?debug("End solver ~w (~w microsecs)\n", [Solver, Time]),
+ {Solver, Map, Time};
+ {_, _R} ->
+ ?debug("Solver ~w returned unexpected result:\n ~P\n",
+ [Solver, _R, 60]),
+ throw(error)
+ catch E:R ->
+ io:format("Solver ~w failed: ~w:~p\n ~p\n",
+ [Solver, E, R, erlang:get_stacktrace()]),
+ throw(error)
+ end.
+
+solve_fun(v1, _Fun, Cs, FunMap, State) ->
+ fun() ->
+ {ok, _MapDict, NewMap} = solve_ref_or_list(Cs, FunMap, dict:new(), State),
+ {ok, NewMap}
+ end;
+solve_fun(v2, Fun, _Cs, FunMap, State) ->
+ fun() -> v2_solve_ref(Fun, FunMap, State) end.
+
+check_solutions([], _Fun, _S, Map) ->
+ Map;
+check_solutions([{S1,Map1,_Time1}|Maps], Fun, S, Map) ->
+ ?debug("Solver ~w needed ~w microsecs\n", [S1, _Time1]),
+ case Map =:= no_map orelse sane_maps(Map, Map1, [Fun], S, S1) of
+ true ->
+ check_solutions(Maps, Fun, S1, Map1);
+ false ->
+ ?debug("Constraint solvers do not agree on ~w\n", [Fun]),
+ pp_map(atom_to_list(S), Map),
+ pp_map(atom_to_list(S1), Map1),
+ io:format("A bug was found. Please report it, and use the option "
+ "`--solver v1' until the bug has been fixed.\n"),
+ throw(error)
+ end.
+
+sane_maps(Map1, Map2, Keys, _S1, _S2) ->
+ lists:all(fun(Key) ->
+ V1 = unsafe_lookup_type(Key, Map1),
+ V2 = unsafe_lookup_type(Key, Map2),
+ case t_is_equal(V1, V2) of
+ true -> true;
+ false ->
+ ?debug("Constraint solvers do not agree on ~w\n", [Key]),
+ ?debug("~w: ~s\n",
+ [_S1, format_type(unsafe_lookup_type(Key, Map1))]),
+ ?debug("~w: ~s\n",
+ [_S2, format_type(unsafe_lookup_type(Key, Map2))]),
+ false
+ end
+ end, Keys).
+
+%% Solver v2
+
+-record(v2_state, {constr_data = dict:new() :: dict(),
+ state :: #state{}}).
+
+v2_solve_ref(Fun, Map, State) ->
+ V2State = #v2_state{state = State},
+ {ok, NewMap, _, _} = v2_solve_reference(Fun, Map, V2State),
+ {ok, NewMap}.
+
+v2_solve(#constraint{}=C, Map, V2State) ->
+ State = V2State#v2_state.state,
+ case solve_one_c(C, Map, State#state.opaques) of
+ error ->
+ report_failed_constraint(C, Map),
+ {error, V2State};
+ {ok, {NewMap, U}} ->
+ {ok, NewMap, V2State, U}
+ end;
+v2_solve(#constraint_list{type = disj}=C, Map, V2State) ->
+ v2_solve_disjunct(C, Map, V2State);
+v2_solve(#constraint_list{type = conj}=C, Map, V2State) ->
+ v2_solve_conjunct(C, Map, V2State);
+v2_solve(#constraint_ref{id = Id}, Map, V2State) ->
+ v2_solve_reference(Id, Map, V2State).
+
+v2_solve_reference(Id, Map, V2State0) ->
+ ?debug("Checking ref to fun: ~w\n", [debug_lookup_name(Id)]),
+ pp_map("Map", Map),
+ pp_constr_data("solve_ref", V2State0),
+ Map1 = restore_local_map(V2State0, Id, Map),
+ State = V2State0#v2_state.state,
+ Cs = state__get_cs(Id, State),
+ Res =
+ case state__is_self_rec(Id, State) of
+ true -> v2_solve_self_recursive(Cs, Map1, Id, t_none(), V2State0);
+ false -> v2_solve(Cs, Map1, V2State0)
+ end,
+ {FunType, V2State} =
+ case Res of
+ {error, V2State1} ->
+ ?debug("Error solving for function ~p\n", [debug_lookup_name(Id)]),
+ Arity = state__fun_arity(Id, State),
+ FunType0 =
+ case state__prop_domain(t_var_name(Id), State) of
+ error -> t_fun(Arity, t_none());
+ {ok, Dom} -> t_fun(Dom, t_none())
+ end,
+ {FunType0, V2State1};
+ {ok, NewMap, V2State1, U} ->
+ ?debug("Done solving fun: ~p\n", [debug_lookup_name(Id)]),
+ FunType0 = lookup_type(Id, NewMap),
+ V2State2 = save_local_map(V2State1, Id, U, NewMap),
+ {FunType0, V2State2}
+ end,
+ ?debug("ref Id=~w Assigned ~s\n", [Id, format_type(FunType)]),
+ {NewMap1, U1} = enter_var_type(Id, FunType, Map),
+ {NewMap2, U2} =
+ case state__get_rec_var(Id, State) of
+ {ok, Var} -> enter_var_type(Var, FunType, NewMap1);
+ error -> {NewMap1, []}
+ end,
+ {ok, NewMap2, V2State, lists:umerge(U1, U2)}.
+
+v2_solve_self_recursive(Cs, Map, Id, RecType0, V2State0) ->
+ ?debug("Solving self recursive ~w\n", [debug_lookup_name(Id)]),
+ State = V2State0#v2_state.state,
+ {ok, RecVar} = state__get_rec_var(Id, State),
+ ?debug("OldRecType ~s\n", [format_type(RecType0)]),
+ RecType = t_limit(RecType0, ?TYPE_LIMIT),
+ {Map1, U0} = enter_var_type(RecVar, RecType, Map),
+ V2State1 = save_updated_vars1(V2State0, Cs, U0), % Probably not necessary
+ case v2_solve(Cs, Map1, V2State1) of
+ {error, _V2State}=Error ->
+ case t_is_none(RecType0) of
+ true ->
+ %% Try again and assume that this is a non-terminating function.
+ Arity = state__fun_arity(Id, State),
+ NewRecType = t_fun(lists:duplicate(Arity, t_any()), t_unit()),
+ v2_solve_self_recursive(Cs, Map, Id, NewRecType, V2State0);
+ false ->
+ Error
+ end;
+ {ok, NewMap, V2State, U} ->
+ pp_map("recursive finished", NewMap),
+ NewRecType = unsafe_lookup_type(Id, NewMap),
+ case t_is_equal(NewRecType, RecType0) of
+ true ->
+ {NewMap2, U1} = enter_var_type(RecVar, NewRecType, NewMap),
+ {ok, NewMap2, V2State, lists:umerge(U, U1)};
+ false ->
+ v2_solve_self_recursive(Cs, Map, Id, NewRecType, V2State0)
+ end
+ end.
+
+enter_var_type(Var, Type, Map0) ->
+ {Map, Vs} = enter_type2(Var, Type, Map0),
+ {Map, [t_var_name(V) || V <- Vs]}.
+
+v2_solve_disjunct(Disj, Map, V2State0) ->
+ #constraint_list{type = disj, id = _Id, list = Cs, masks = Masks} = Disj,
+ ?debug("disjunct Id=~w~n", [_Id]),
+ pp_map("Map", Map),
+ pp_constr_data("disjunct", V2State0),
+ case get_flags(V2State0, Disj) of
+ {V2State1, failed_list} -> {error, V2State1}; % cannot happen
+ {V2State1, Flags} when Flags =/= [] ->
+ {ok, V2State, Eval, UL, MapL0, Uneval, Failed} =
+ v2_solve_disj(Flags, Cs, 1, Map, V2State1, [], [], [], [], false),
+ ?debug("disj ending _Id=~w Eval=~w, |Uneval|=~w |UL|=~w~n",
+ [_Id, Eval, length(Uneval), length(UL)]),
+ if Eval =:= [], Uneval =:= [] ->
+ {error, failed_list(Disj, V2State0)};
+ true ->
+ {Is0, UnIds} = lists:unzip(Uneval),
+ MapL = [restore_local_map(V2State, Id, Map) ||
+ Id <- UnIds] ++ MapL0,
+ %% If some branch has just failed every variable of the
+ %% non-failed branches need to be checked, not just the
+ %% updated ones.
+ U0 = case Failed of
+ false -> lists:umerge(UL);
+ true -> constrained_keys(MapL)
+ end,
+ if U0 =:= [] -> {ok, Map, V2State, []};
+ true ->
+ NotFailed = lists:umerge(Is0, Eval),
+ U1 = [V || V <- U0,
+ var_occurs_everywhere(V, Masks, NotFailed)],
+ NewMap = join_maps(U1, MapL, Map),
+ pp_map("NewMap", NewMap),
+ U = updated_vars_only(U1, Map, NewMap),
+ ?debug("disjunct finished _Id=~w\n", [_Id]),
+ {ok, NewMap, V2State, U}
+ end
+ end
+ end.
+
+var_occurs_everywhere(V, Masks, NotFailed) ->
+ ordsets:is_subset(NotFailed, get_mask(V, Masks)).
+
+v2_solve_disj([I|Is], [C|Cs], I, Map0, V2State0, UL, MapL, Eval, Uneval,
+ Failed0) ->
+ Id = C#constraint_list.id,
+ Map1 = restore_local_map(V2State0, Id, Map0),
+ case v2_solve(C, Map1, V2State0) of
+ {error, V2State} ->
+ ?debug("disj error I=~w~n", [I]),
+ Failed = Failed0 orelse not is_failed_list(C, V2State0),
+ v2_solve_disj(Is, Cs, I+1, Map0, V2State, UL, MapL, Eval, Uneval, Failed);
+ {ok, Map, V2State1, U} ->
+ ?debug("disj I=~w U=~w~n", [I, U]),
+ V2State = save_local_map(V2State1, Id, U, Map),
+ pp_map("DMap", Map),
+ v2_solve_disj(Is, Cs, I+1, Map0, V2State, [U|UL], [Map|MapL],
+ [I|Eval], Uneval, Failed0)
+ end;
+v2_solve_disj([], [], _I, _Map, V2State, UL, MapL, Eval, Uneval, Failed) ->
+ {ok, V2State, lists:reverse(Eval), UL, MapL, lists:reverse(Uneval), Failed};
+v2_solve_disj(Is, [C|Cs], I, Map, V2State, UL, MapL, Eval, Uneval0, Failed) ->
+ Uneval = [{I,C#constraint_list.id} ||
+ not is_failed_list(C, V2State)] ++ Uneval0,
+ v2_solve_disj(Is, Cs, I+1, Map, V2State, UL, MapL, Eval, Uneval, Failed).
+
+save_local_map(#v2_state{constr_data = ConData}=V2State, Id, U, Map) ->
+ Part0 = [{V,dict:fetch(V, Map)} || V <- U],
+ Part1 =
+ case dict:find(Id, ConData) of
+ error -> []; % cannot happen
+ {ok, {Part2,[]}} -> Part2
+ end,
+ ?debug("save local map Id=~w:\n", [Id]),
+ Part = lists:ukeymerge(1, lists:keysort(1, Part0), Part1),
+ pp_map("New Part", dict:from_list(Part0)),
+ pp_map("Old Part", dict:from_list(Part1)),
+ pp_map(" => Part", dict:from_list(Part)),
+ V2State#v2_state{constr_data = dict:store(Id, {Part,[]}, ConData)}.
+
+restore_local_map(#v2_state{constr_data = ConData}, Id, Map0) ->
+ case dict:find(Id, ConData) of
+ error -> Map0;
+ {ok, failed} -> Map0;
+ {ok, {[],_}} -> Map0;
+ {ok, {Part0,U}} ->
+ Part = [{K,V} || {K,V} <- Part0, not lists:member(K, U)],
+ ?debug("restore local map Id=~w U=~w\n", [Id, U]),
+ pp_map("Part", dict:from_list(Part)),
+ pp_map("Map0", Map0),
+ Map = lists:foldl(fun({K,V}, D) -> dict:store(K, V, D)end, Map0, Part),
+ pp_map("Map", Map),
+ Map
+ end.
+
+v2_solve_conjunct(Conj, Map, V2State0) ->
+ #constraint_list{type = conj, list = Cs} = Conj,
+ ?debug("conjunct Id=~w~n", [Conj#constraint_list.id]),
+ IsFlat = case Cs of [#constraint{}|_] -> true; _ -> false end,
+ case get_flags(V2State0, Conj) of
+ {V2State, failed_list} -> {error, V2State};
+ {V2State, Flags} ->
+ v2_solve_conj(Flags, Cs, 1, Map, Conj, IsFlat, V2State, [], [], [],
+ Map, Flags)
+ end.
+
+%% LastMap and LastFlags are used for loop detection.
+v2_solve_conj([I|Is], [Cs|Tail], I, Map0, Conj, IsFlat, V2State0,
+ UL, NewFs0, VarsUp, LastMap, LastFlags) ->
+ ?debug("conj Id=~w I=~w~n", [Conj#constraint_list.id, I]),
+ true = IsFlat =:= is_record(Cs, constraint),
+ pp_constr_data("conj", V2State0),
+ case v2_solve(Cs, Map0, V2State0) of
+ {error, V2State1} -> {error, failed_list(Conj, V2State1)};
+ {ok, Map, V2State1, []} ->
+ v2_solve_conj(Is, Tail, I+1, Map, Conj, IsFlat, V2State1,
+ UL, NewFs0, VarsUp, LastMap, LastFlags);
+ {ok, Map, V2State1, U} when IsFlat -> % optimization
+ %% It is ensured by enumerate_constraints() that every
+ %% #constraint{} has a conjunct as parent, and that such a
+ %% parent has nothing but #constraint{}:s as children, a fact
+ %% which is used here to simplify the flag calculation.
+ Mask = lists:umerge([get_mask(V, Conj#constraint_list.masks) || V <- U]),
+ {Is1, NewF} = add_mask_to_flags(Is, Mask, I, []),
+ NewFs = [NewF|NewFs0],
+ v2_solve_conj(Is1, Tail, I+1, Map, Conj, IsFlat, V2State1,
+ [U|UL], NewFs, VarsUp, LastMap, LastFlags);
+ {ok, Map, V2State1, U} ->
+ #constraint_list{masks = Masks, list = AllCs} = Conj,
+ M = lists:keydelete(I, 1, vars_per_child(U, Masks)),
+ {V2State2, NewF0} = save_updated_vars_list(AllCs, M, V2State1),
+ {NewF, F} = lists:splitwith(fun(J) -> J < I end, NewF0),
+ Is1 = lists:umerge(Is, F),
+ NewFs = [NewF|NewFs0],
+ v2_solve_conj(Is1, Tail, I+1, Map, Conj, IsFlat, V2State2,
+ [U|UL], NewFs, VarsUp, LastMap, LastFlags)
+ end;
+v2_solve_conj([], _Cs, _I, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp,
+ LastMap, LastFlags) ->
+ U = lists:umerge(UL),
+ case lists:umerge(NewFs) of
+ [] ->
+ ?debug("conjunct finished Id=~w\n", [Conj#constraint_list.id]),
+ {ok, Map, V2State, lists:umerge([U|VarsUp])};
+ NewFlags when NewFlags =:= LastFlags, Map =:= LastMap ->
+ %% A loop was detected! The cause is some bug, possibly in erl_types.
+ %% The evaluation continues, but the results can be wrong.
+ report_detected_loop(Conj),
+ {ok, Map, V2State, lists:umerge([U|VarsUp])};
+ NewFlags ->
+ #constraint_list{type = conj, list = Cs} = Conj,
+ v2_solve_conj(NewFlags, Cs, 1, Map, Conj, IsFlat, V2State,
+ [], [], [U|VarsUp], Map, NewFlags)
+ end;
+v2_solve_conj(Is, [_|Tail], I, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp,
+ LastMap, LastFlags) ->
+ v2_solve_conj(Is, Tail, I+1, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp,
+ LastMap, LastFlags).
+
+-ifdef(DEBUG_LOOP_DETECTION).
+report_detected_loop(Conj) ->
+ io:format("A loop was detected in ~w\n", [Conj#constraint_list.id]).
+-else.
+report_detected_loop(_) ->
+ ok.
+-endif.
+
+add_mask_to_flags(Flags, [Im|M], I, L) when I > Im ->
+ add_mask_to_flags(Flags, M, I, [Im|L]);
+add_mask_to_flags(Flags, [_|M], _I, L) ->
+ {lists:umerge(Flags, M), lists:reverse(L)}.
+
+get_mask(V, {d, Masks}) ->
+ case dict:find(V, Masks) of
+ error -> [];
+ {ok, M} -> M
+ end;
+get_mask(V, Masks) ->
+ case lists:keyfind(V, 1, Masks) of
+ false -> [];
+ {V, M} -> M
+ end.
+
+get_flags(#v2_state{constr_data = ConData}=V2State0, C) ->
+ #constraint_list{id = Id, list = Cs, masks = Masks} = C,
+ case dict:find(Id, ConData) of
+ error ->
+ ?debug("get_flags Id=~w Flags=all ~w\n", [Id, length(Cs)]),
+ V2State = V2State0#v2_state{constr_data = dict:store(Id, {[],[]}, ConData)},
+ {V2State, lists:seq(1, length(Cs))};
+ {ok, failed} ->
+ {V2State0, failed_list};
+ {ok, {Part,U}} when U =/= [] ->
+ ?debug("get_flags Id=~w U=~w\n", [Id, U]),
+ V2State = V2State0#v2_state{constr_data = dict:store(Id, {Part,[]}, ConData)},
+ save_updated_vars_list(Cs, vars_per_child(U, Masks), V2State)
+ end.
+
+vars_per_child(U, Masks) ->
+ family([{I, V} || V <- lists:usort(U), I <- get_mask(V, Masks)]).
+
+save_updated_vars_list(Cs, IU, V2State) ->
+ save_updated_vars_list1(Cs, IU, V2State, 1, []).
+
+save_updated_vars_list1([C|Cs], [{I,U}|IU], V2State0, I, Is) ->
+ V2State = save_updated_vars(C, U, V2State0),
+ save_updated_vars_list1(Cs, IU, V2State, I+1, [I|Is]);
+save_updated_vars_list1([], [], V2State, _I, Is) ->
+ {V2State, lists:reverse(Is)};
+save_updated_vars_list1([_|Cs], IU, V2State, I, Is) ->
+ save_updated_vars_list1(Cs, IU, V2State, I+1, Is).
+
+save_updated_vars(#constraint{}, _, V2State) ->
+ V2State;
+save_updated_vars(#constraint_list{}=C, U, V2State0) ->
+ save_updated_vars1(V2State0, C, U);
+save_updated_vars(#constraint_ref{id = Id}, U, V2State) ->
+ Cs = state__get_cs(Id, V2State#v2_state.state),
+ save_updated_vars(Cs, U, V2State).
+
+save_updated_vars1(V2State, C, U) ->
+ #v2_state{constr_data = ConData} = V2State,
+ #constraint_list{id = Id} = C,
+ case dict:find(Id, ConData) of
+ error -> V2State; % error means everything is flagged
+ {ok, failed} -> V2State;
+ {ok, {Part,U0}} ->
+ %% Duplicates are not so common; let masks/2 remove them.
+ U1 = U ++ U0,
+ V2State#v2_state{constr_data = dict:store(Id, {Part,U1}, ConData)}
+ end.
+
+-ifdef(DEBUG).
+pp_constr_data(_Tag, #v2_state{constr_data = D}) ->
+ io:format("Constr data at ~p\n", [_Tag]),
+ _ = [begin
+ case _PartU of
+ {_Part, _U} ->
+ io:format("Id: ~w Vars: ~w\n", [_Id, _U]),
+ [pp_map("Part", dict:from_list(_Part)) || _Part =/= []];
+ failed ->
+ io:format("Id: ~w failed list\n", [_Id])
+ end
+ end ||
+ {_Id, _PartU} <- lists:keysort(1, dict:to_list(D))],
+ ok.
+
+-else.
+pp_constr_data(_Tag, _V2State) ->
+ ok.
+-endif.
+
+failed_list(#constraint_list{id = Id}, #v2_state{constr_data = D}=V2State) ->
+ ?debug("error list ~w~n", [Id]),
+ V2State#v2_state{constr_data = dict:store(Id, failed, D)}.
+
+is_failed_list(#constraint_list{id = Id}, #v2_state{constr_data = D}) ->
+ dict:find(Id, D) =:= {ok, failed}.
+
+%% Solver v1
+
solve_ref_or_list(#constraint_ref{id = Id, deps = Deps},
Map, MapDict, State) ->
{OldLocalMap, Check} =
case dict:find(Id, MapDict) of
- error -> {dict:new(), false};
+ error -> {map_new(), false};
{ok, M} -> {M, true}
end,
?debug("Checking ref to fun: ~w\n", [debug_lookup_name(Id)]),
+ %% Note: mk_constraint_ref() has already removed Id from Deps. The
+ %% reason for doing it there is that it makes it easy for
+ %% calculate_masks() to make the corresponding adjustment for
+ %% version v2.
CheckDeps = ordsets:del_element(t_var_name(Id), Deps),
+ true = CheckDeps =:= Deps,
case Check andalso maps_are_equal(OldLocalMap, Map, CheckDeps) of
true ->
?debug("Equal\n", []),
@@ -1892,6 +2329,7 @@ solve_ref_or_list(#constraint_ref{id = Id, deps = Deps},
FunType0 = lookup_type(Id, NewMap),
{NewMapDict0, FunType0}
end,
+ ?debug(" Id=~w Assigned ~s\n", [Id, format_type(FunType)]),
NewMap1 = enter_type(Id, FunType, Map),
NewMap2 =
case state__get_rec_var(Id, State) of
@@ -1904,7 +2342,7 @@ solve_ref_or_list(#constraint_list{type=Type, list = Cs, deps = Deps, id = Id},
Map, MapDict, State) ->
{OldLocalMap, Check} =
case dict:find(Id, MapDict) of
- error -> {dict:new(), false};
+ error -> {map_new(), false};
{ok, M} -> {M, true}
end,
?debug("Checking ref to list: ~w\n", [Id]),
@@ -1926,7 +2364,7 @@ solve_self_recursive(Cs, Map, MapDict, Id, RecType0, State) ->
{ok, RecVar} = state__get_rec_var(Id, State),
?debug("OldRecType ~s\n", [format_type(RecType0)]),
RecType = t_limit(RecType0, ?TYPE_LIMIT),
- Map1 = enter_type(RecVar, RecType, dict:erase(t_var_name(Id), Map)),
+ Map1 = enter_type(RecVar, RecType, erase_type(t_var_name(Id), Map)),
pp_map("Map1", Map1),
case solve_ref_or_list(Cs, Map1, MapDict, State) of
{error, _} = Error ->
@@ -1994,14 +2432,9 @@ solve_cs([#constraint_list{} = C|Tail], Map, MapDict, State) ->
solve_cs([#constraint{} = C|Tail], Map, MapDict, State) ->
case solve_one_c(C, Map, State#state.opaques) of
error ->
- ?debug("+++++++++++\nFailed: ~s :: ~s ~w ~s :: ~s\n+++++++++++\n",
- [format_type(C#constraint.lhs),
- format_type(lookup_type(C#constraint.lhs, Map)),
- C#constraint.op,
- format_type(C#constraint.rhs),
- format_type(lookup_type(C#constraint.rhs, Map))]),
+ report_failed_constraint(C, Map),
{error, MapDict};
- {ok, NewMap} ->
+ {ok, {NewMap, _U}} ->
solve_cs(Tail, NewMap, MapDict, State)
end;
solve_cs([], Map, MapDict, _State) ->
@@ -2022,7 +2455,11 @@ solve_one_c(#constraint{lhs = Lhs, rhs = Rhs, op = Op}, Map, Opaques) ->
eq ->
case solve_subtype(Lhs, Inf, Map, Opaques) of
error -> error;
- {ok, Map1} -> solve_subtype(Rhs, Inf, Map1, Opaques)
+ {ok, {Map1, U1}} ->
+ case solve_subtype(Rhs, Inf, Map1, Opaques) of
+ error -> error;
+ {ok, {Map2, U2}} -> {ok, {Map2, lists:umerge(U1, U2)}}
+ end
end
end
end.
@@ -2045,20 +2482,34 @@ solve_subtype(Type, Inf, Map, Opaques) ->
end.
%% end.
+report_failed_constraint(_C, _Map) ->
+ ?debug("+++++++++++\nFailed: ~s :: ~s ~w ~s :: ~s\n+++++++++++\n",
+ [format_type(_C#constraint.lhs),
+ format_type(lookup_type(_C#constraint.lhs, _Map)),
+ _C#constraint.op,
+ format_type(_C#constraint.rhs),
+ format_type(lookup_type(_C#constraint.rhs, _Map))]).
+
%% ============================================================================
%%
%% Maps and types.
%%
%% ============================================================================
+map_new() ->
+ dict:new().
+
join_maps([Map]) ->
Map;
join_maps(Maps) ->
- Keys = lists:foldl(fun(TmpMap, AccKeys) ->
- [Key || Key <- AccKeys, dict:is_key(Key, TmpMap)]
- end,
- dict:fetch_keys(hd(Maps)), tl(Maps)),
- join_maps(Keys, Maps, dict:new()).
+ Keys = constrained_keys(Maps),
+ join_maps(Keys, Maps, map_new()).
+
+constrained_keys(Maps) ->
+ lists:foldl(fun(TmpMap, AccKeys) ->
+ [Key || Key <- AccKeys, dict:is_key(Key, TmpMap)]
+ end,
+ dict:fetch_keys(hd(Maps)), tl(Maps)).
join_maps([Key|Left], Maps = [Map|MapsLeft], AccMap) ->
NewType = join_one_key(Key, MapsLeft, lookup_type(Key, Map)),
@@ -2121,13 +2572,13 @@ enter_type(Key, Val, Map) when is_integer(Key) ->
?debug("Entering ~s :: ~s\n", [format_type(t_var(Key)), format_type(Val)]),
case t_is_any(Val) of
true ->
- dict:erase(Key, Map);
+ erase_type(Key, Map);
false ->
LimitedVal = t_limit(Val, ?INTERNAL_TYPE_LIMIT),
case dict:find(Key, Map) of
{ok, LimitedVal} -> Map;
- {ok, _} -> dict:store(Key, LimitedVal, Map);
- error -> dict:store(Key, LimitedVal, Map)
+ {ok, _} -> map_store(Key, LimitedVal, Map);
+ error -> map_store(Key, LimitedVal, Map)
end
end;
enter_type(Key, Val, Map) ->
@@ -2135,13 +2586,13 @@ enter_type(Key, Val, Map) ->
KeyName = t_var_name(Key),
case t_is_any(Val) of
true ->
- dict:erase(KeyName, Map);
+ erase_type(KeyName, Map);
false ->
LimitedVal = t_limit(Val, ?INTERNAL_TYPE_LIMIT),
case dict:find(KeyName, Map) of
{ok, LimitedVal} -> Map;
- {ok, _} -> dict:store(KeyName, LimitedVal, Map);
- error -> dict:store(KeyName, LimitedVal, Map)
+ {ok, _} -> map_store(KeyName, LimitedVal, Map);
+ error -> map_store(KeyName, LimitedVal, Map)
end
end.
@@ -2151,11 +2602,25 @@ enter_type_lists([Key|KeyTail], [Val|ValTail], Map) ->
enter_type_lists([], [], Map) ->
Map.
-enter_type_list([{Key, Val}|Tail], Map) ->
+enter_type_list(KeyVals, Map) ->
+ enter_type_list(KeyVals, Map, []).
+
+enter_type_list([{Key, Val}|Tail], Map, U0) ->
+ {Map1,U1} = enter_type2(Key, Val, Map),
+ enter_type_list(Tail, Map1, U1++U0);
+enter_type_list([], Map, U) ->
+ {Map, ordsets:from_list(U)}.
+
+enter_type2(Key, Val, Map) ->
Map1 = enter_type(Key, Val, Map),
- enter_type_list(Tail, Map1);
-enter_type_list([], Map) ->
- Map.
+ {Map1, [Key || not is_same(Key, Map, Map1)]}.
+
+map_store(Key, Val, Map) ->
+ ?debug("Storing ~w :: ~s\n", [Key, format_type(Val)]),
+ dict:store(Key, Val, Map).
+
+erase_type(Key, Map) ->
+ dict:erase(Key, Map).
lookup_type_list(List, Map) ->
[lookup_type(X, Map) || X <- List].
@@ -2206,19 +2671,24 @@ mk_var_no_lit(Var) ->
mk_var_no_lit_list(List) ->
[mk_var_no_lit(X) || X <- List].
+updated_vars_only(U, OldMap, NewMap) ->
+ [V || V <- U, not is_same(V, OldMap, NewMap)].
+
+is_same(Key, Map1, Map2) ->
+ t_is_equal(lookup_type(Key, Map1), lookup_type(Key, Map2)).
+
pp_map(_S, _Map) ->
?debug("\t~s: ~p\n",
[_S, [{X, lists:flatten(format_type(Y))} ||
{X, Y} <- lists:keysort(1, dict:to_list(_Map))]]).
-
%% ============================================================================
%%
%% The State.
%%
%% ============================================================================
-new_state(SCC0, NextLabel, CallGraph, Plt, PropTypes) ->
+new_state(SCC0, NextLabel, CallGraph, Plt, PropTypes, Solvers) ->
List = [{MFA, Var} || {MFA, {Var, _Fun}, _Rec} <- SCC0],
NameMap = dict:from_list(List),
MFAs = [MFA || {MFA, _Var} <- List],
@@ -2235,7 +2705,7 @@ new_state(SCC0, NextLabel, CallGraph, Plt, PropTypes) ->
end,
#state{callgraph = CallGraph, name_map = NameMap, next_label = NextLabel,
prop_types = {d, PropTypes}, plt = Plt, scc = ordsets:from_list(SCC),
- mfas = MFAs, self_rec = SelfRec}.
+ mfas = MFAs, self_rec = SelfRec, solvers = Solvers}.
state__set_rec_dict(State, RecDict) ->
State#state{records = RecDict}.
@@ -2458,7 +2928,7 @@ mk_constraint(Lhs, Op, Rhs) ->
case Deps =:= [] of
true ->
%% This constraint is constant. Solve it immediately.
- case solve_one_c(C, dict:new(), []) of
+ case solve_one_c(C, map_new(), []) of
error -> throw(error);
_ ->
%% This is always true, keep it anyway for logistic reasons
@@ -2481,8 +2951,9 @@ constraint_opnd_is_any(Type) -> t_is_any(Type).
-ifdef(DEBUG).
--spec mk_fun_var(fun((_) -> erl_types:erl_type()), [erl_types:erl_type()],
- integer()) -> #fun_var{}.
+-spec mk_fun_var(integer(),
+ fun((_) -> erl_types:erl_type()),
+ [erl_types:erl_type()]) -> #fun_var{}.
mk_fun_var(Line, Fun, Types) ->
Deps = [t_var_name(Var) || Var <- t_collect_vars(t_product(Types))],
@@ -2530,7 +3001,9 @@ mk_constraints([], _Op, []) ->
[].
mk_constraint_ref(Id, Deps) ->
- #constraint_ref{id = Id, deps = Deps}.
+ %% See also solve_ref_or_list(), #constraint_ref{}.
+ Ds = ordsets:del_element(t_var_name(Id), Deps),
+ #constraint_ref{id = Id, deps = Ds}.
mk_constraint_list(Type, List) ->
List1 = ordsets:from_list(lift_lists(Type, List)),
@@ -2680,7 +3153,7 @@ enumerate_constraints([#constraint_list{type = conj, list = List} = C|Tail],
NewDeep =:= [] -> {NewFlat, N2};
true ->
TmpCList = mk_conj_constraint_list(NewFlat),
- {[TmpCList#constraint_list{id = {list, N2}} | NewDeep],
+ {[TmpCList#constraint_list{id = {list, N2}}| NewDeep],
N2 + 1}
end,
NewAcc = [C#constraint_list{list = NewList, id = {list, N3}}|Acc],
@@ -2725,7 +3198,9 @@ order_fun_constraints([#constraint_list{list = List, type = Type} = C|Tail],
end,
lists:mapfoldl(FoldFun, State, List)
end,
- NewAcc = [update_constraint_list(C, NewList)|Acc],
+ C1 = update_constraint_list(C, NewList),
+ Masks = calculate_masks(NewList, 1, []),
+ NewAcc = [update_masks(C1, Masks)|Acc],
order_fun_constraints(Tail, Funs, NewAcc, NewState);
order_fun_constraints([#constraint{} = C|Tail], Funs, Acc, State) ->
order_fun_constraints(Tail, Funs, [C|Acc], State);
@@ -2733,6 +3208,22 @@ order_fun_constraints([], Funs, Acc, State) ->
NewState = order_fun_constraints(Funs, State),
{lists:reverse(Acc)++Funs, NewState}.
+update_masks(C, Masks) ->
+ C#constraint_list{masks = Masks}.
+
+-define(VARS_LIMIT, 50).
+
+calculate_masks([C|Cs], I, L0) ->
+ calculate_masks(Cs, I+1, [{V, I} || V <- get_deps(C)] ++ L0);
+calculate_masks([], _I, L) ->
+ M = family(L),
+ case length(M) > ?VARS_LIMIT of
+ true ->
+ {d, dict:from_list(M)};
+ false ->
+ M
+ end.
+
%% ============================================================================
%%
%% Utilities.
@@ -2810,6 +3301,9 @@ lookup_record(Records, Tag, Arity) ->
error
end.
+family(L) ->
+ sofs:to_external(sofs:rel2fam(sofs:relation(L))).
+
%% ============================================================================
%%
%% Pretty printer and debug facilities.
@@ -2834,8 +3328,8 @@ format_type(Type) ->
join_chars([], _Sep) ->
[];
-join_chars([H | T], Sep) ->
- [H | [[Sep,X] || X <- T]].
+join_chars([H|T], Sep) ->
+ [H|[[Sep,X] || X <- T]].
debug_lookup_name(Var) ->
case dict:find(t_var_name(Var), get(dialyzer_typesig_map)) of
diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl
index 149e777e1f..8046b48838 100644
--- a/lib/dialyzer/src/dialyzer_utils.erl
+++ b/lib/dialyzer/src/dialyzer_utils.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. 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/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl
index 50b2e31ed8..1338997899 100644
--- a/lib/dialyzer/src/dialyzer_worker.erl
+++ b/lib/dialyzer/src/dialyzer_worker.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. 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/lib/dialyzer/test/r9c_SUITE_data/results/asn1 b/lib/dialyzer/test/r9c_SUITE_data/results/asn1
index 292275dd6e..c11105b76d 100644
--- a/lib/dialyzer/test/r9c_SUITE_data/results/asn1
+++ b/lib/dialyzer/test/r9c_SUITE_data/results/asn1
@@ -68,7 +68,6 @@ asn1rt_check.erl:100: The variable _ can never match since previous clauses comp
asn1rt_check.erl:85: The variable _ can never match since previous clauses completely covered the type [any()]
asn1rt_driver_handler.erl:32: The pattern 'already_done' can never match the type {'error',_}
asn1rt_per.erl:1065: The pattern {'BMPString', {'octets', Ol}} can never match the type {_,[{'bits',1 | 2 | 4 | 8 | 16 | 32,_}]}
-asn1rt_per.erl:1066: Function will never be called
asn1rt_per.erl:1231: The call erlang:'not'('implemented') will never return since it differs in the 1st argument from the success typing arguments: (boolean())
asn1rt_per.erl:1233: The call erlang:'not'('implemented') will never return since it differs in the 1st argument from the success typing arguments: (boolean())
asn1rt_per.erl:1235: The call erlang:'not'('implemented') will never return since it differs in the 1st argument from the success typing arguments: (boolean())
@@ -76,7 +75,6 @@ asn1rt_per.erl:1237: The call erlang:'not'('implemented') will never return sinc
asn1rt_per.erl:989: The pattern <_C, 'true', _Val> can never match the type <_,'false',_>
asn1rt_per_bin.erl:1361: The pattern <_, 'true', _> can never match the type <_,'false',_>
asn1rt_per_bin.erl:1436: The pattern {'BMPString', {'octets', Ol}} can never match the type {'BMPString' | 'IA5String' | 'NumericString' | 'PrintableString' | 'UniversalString' | 'VisibleString',[{'bits',1 | 2 | 4 | 8 | 16 | 32,_}]}
-asn1rt_per_bin.erl:1437: Function will never be called
asn1rt_per_bin.erl:161: The call asn1rt_per_bin:getbit({0,maybe_improper_list()}) will never return since it differs in the 1st argument from the success typing arguments: (<<_:8,_:_*8>> | {non_neg_integer(),<<_:1,_:_*1>>})
asn1rt_per_bin.erl:1812: The pattern {Name, Val} can never match since previous clauses completely covered the type any()
asn1rt_per_bin.erl:2106: Cons will produce an improper list since its 2nd argument is binary()
@@ -94,7 +92,6 @@ asn1rt_per_bin.erl:487: The variable _ can never match since previous clauses co
asn1rt_per_bin.erl:498: The variable _ can never match since previous clauses completely covered the type integer()
asn1rt_per_bin_rt2ct.erl:152: The call asn1rt_per_bin_rt2ct:getbit({0,maybe_improper_list()}) will never return since it differs in the 1st argument from the success typing arguments: (<<_:8,_:_*8>> | {non_neg_integer(),<<_:1,_:_*1>>})
asn1rt_per_bin_rt2ct.erl:1533: The pattern {'BMPString', {'octets', Ol}} can never match the type {_,[[any(),...]]}
-asn1rt_per_bin_rt2ct.erl:1534: Function will never be called
asn1rt_per_bin_rt2ct.erl:1875: The pattern {Name, Val} can never match since previous clauses completely covered the type any()
asn1rt_per_bin_rt2ct.erl:443: The variable _ can never match since previous clauses completely covered the type integer()
asn1rt_per_bin_rt2ct.erl:464: The variable _ can never match since previous clauses completely covered the type integer()
@@ -103,4 +100,3 @@ asn1rt_per_bin_rt2ct.erl:484: The variable _ can never match since previous clau
asn1rt_per_bin_rt2ct.erl:495: The variable _ can never match since previous clauses completely covered the type integer()
asn1rt_per_v1.erl:1209: The pattern <_, 'true', _> can never match the type <_,'false',_>
asn1rt_per_v1.erl:1290: The pattern {'BMPString', {'octets', Ol}} can never match the type {'BMPString' | 'IA5String' | 'NumericString' | 'PrintableString' | 'UniversalString' | 'VisibleString',[{'bits',1 | 2 | 4 | 8 | 16 | 32,_}]}
-asn1rt_per_v1.erl:1291: Function will never be called
diff --git a/lib/dialyzer/test/r9c_SUITE_data/results/inets b/lib/dialyzer/test/r9c_SUITE_data/results/inets
index 773525eb7f..d789d8d246 100644
--- a/lib/dialyzer/test/r9c_SUITE_data/results/inets
+++ b/lib/dialyzer/test/r9c_SUITE_data/results/inets
@@ -31,11 +31,11 @@ httpd_sup.erl:92: The variable Else can never match since previous clauses compl
mod_auth.erl:559: The pattern {'error', Reason} can never match the type {_,integer(),maybe_improper_list(),_}
mod_auth_dets.erl:120: The call lists:foreach(fun((_) -> 'true' | {'error','no_such_group' | 'no_such_group_member'}),{'ok',[any()]}) will never return since it differs in the 2nd argument from the success typing arguments: (fun((_) -> any()),[any()])
mod_auth_plain.erl:100: The variable _ can never match since previous clauses completely covered the type {'ok',[any()]}
-mod_auth_plain.erl:159: The variable _ can never match since previous clauses completely covered the type [any()]
-mod_auth_plain.erl:83: The variable O can never match since previous clauses completely covered the type [any()]
+mod_auth_plain.erl:159: The variable _ can never match since previous clauses completely covered the type [[any()]]
+mod_auth_plain.erl:83: The variable O can never match since previous clauses completely covered the type [[any()]]
mod_cgi.erl:372: The pattern {'http_response', NewAccResponse} can never match the type 'ok'
mod_dir.erl:101: The call lists:flatten(nonempty_improper_list(atom() | [any()] | char(),atom())) will never return since it differs in the 1st argument from the success typing arguments: ([any()])
-mod_dir.erl:72: The pattern {'error', Reason} can never match the type {'ok',[[[any()] | char()],...]}
+mod_dir.erl:72: The pattern {'error', Reason} can never match the type {'ok',[[[any()] | non_neg_integer()],...]}
mod_get.erl:135: The pattern <{'enfile', _}, _Info, Path> can never match the type <atom(),#mod{},atom() | binary() | [atom() | [any()] | char()]>
mod_head.erl:80: The pattern <{'enfile', _}, _Info, Path> can never match the type <atom(),#mod{},atom() | binary() | [atom() | [any()] | char()]>
mod_htaccess.erl:460: The pattern {'error', BadData} can never match the type {'ok',_}
@@ -47,9 +47,9 @@ mod_include.erl:692: The pattern <{'read', Reason}, Info, Path> can never match
mod_include.erl:706: The pattern <{'enfile', _}, _Info, Path> can never match the type <atom(),#mod{},atom() | binary() | [atom() | [any()] | char()]>
mod_include.erl:716: Function read_error/3 will never be called
mod_include.erl:719: Function read_error/4 will never be called
-mod_security_server.erl:386: The variable O can never match since previous clauses completely covered the type [any()]
-mod_security_server.erl:433: The variable Other can never match since previous clauses completely covered the type [any()]
-mod_security_server.erl:585: The variable _ can never match since previous clauses completely covered the type [any()]
-mod_security_server.erl:608: The variable _ can never match since previous clauses completely covered the type [any()]
-mod_security_server.erl:641: The variable _ can never match since previous clauses completely covered the type [any()]
+mod_security_server.erl:386: The variable O can never match since previous clauses completely covered the type [tuple()]
+mod_security_server.erl:433: The variable Other can never match since previous clauses completely covered the type [tuple()]
+mod_security_server.erl:585: The variable _ can never match since previous clauses completely covered the type [tuple()]
+mod_security_server.erl:608: The variable _ can never match since previous clauses completely covered the type [tuple()]
+mod_security_server.erl:641: The variable _ can never match since previous clauses completely covered the type [tuple()]
uri.erl:146: The pattern {'error', Error} can never match since previous clauses completely covered the type {_,{[],[]}}
diff --git a/lib/dialyzer/test/small_SUITE_data/results/fun_ref_match b/lib/dialyzer/test/small_SUITE_data/results/fun_ref_match
index 60b34530b4..e69de29bb2 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/fun_ref_match
+++ b/lib/dialyzer/test/small_SUITE_data/results/fun_ref_match
@@ -1,2 +0,0 @@
-
-fun_ref_match.erl:14: Function will never be called
diff --git a/lib/dialyzer/test/user_SUITE_data/results/wsp_pdu b/lib/dialyzer/test/user_SUITE_data/results/wsp_pdu
index a47b1f1f2c..d1f8f4caf2 100644
--- a/lib/dialyzer/test/user_SUITE_data/results/wsp_pdu
+++ b/lib/dialyzer/test/user_SUITE_data/results/wsp_pdu
@@ -6,7 +6,7 @@ wsp_pdu.erl:2403: The call wsp_pdu:d_date(Data1::binary()) will never return sin
wsp_pdu.erl:2406: Guard test is_integer(Sec::{[byte()] | byte() | {'long',binary()} | {'short',binary()},binary()}) can never succeed
wsp_pdu.erl:2408: The pattern {'short', Data2} can never match the type {[byte()] | byte() | {'long',binary()} | {'short',binary()},binary()}
wsp_pdu.erl:2755: Function parse_push_flag/1 has no local return
-wsp_pdu.erl:2756: The call erlang:integer_to_list(Value::[any()]) will never return since it differs in the 1st argument from the success typing arguments: (integer())
+wsp_pdu.erl:2756: The call erlang:integer_to_list(Value::[any()]) breaks the contract (Integer) -> string() when is_subtype(Integer,integer())
wsp_pdu.erl:2875: The call wsp_pdu:d_text_string(Data::byte()) will never return since it differs in the 1st argument from the success typing arguments: (binary())
wsp_pdu.erl:2976: The call wsp_pdu:d_q_value(QData::byte()) will never return since it differs in the 1st argument from the success typing arguments: (<<_:8,_:_*8>>)
wsp_pdu.erl:3336: The call wsp_pdu:encode_typed_field(Ver::any(),'Q-value',ParamValue::any()) will never return since it differs in the 2nd argument from the success typing arguments: (any(),'Constrained-encoding' | 'Date-value' | 'No-value' | 'Short-integer' | 'Text-string' | 'Text-value' | 'Well-known-charset',any())
diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk
index edafcc4afb..64d05156b1 100644
--- a/lib/dialyzer/vsn.mk
+++ b/lib/dialyzer/vsn.mk
@@ -1 +1 @@
-DIALYZER_VSN = 2.5.1
+DIALYZER_VSN = 2.5.2
diff --git a/lib/diameter/autoconf/configure.vxworks b/lib/diameter/autoconf/configure.vxworks
deleted file mode 100755
index 33aa497680..0000000000
--- a/lib/diameter/autoconf/configure.vxworks
+++ /dev/null
@@ -1,147 +0,0 @@
-#!/bin/sh
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2011. 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
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-# Author:
-# Patrik Winroth
-#
-
-
-# vxworks_ppc860 vxworks_ppc603 vxworks_ppc603_longcall vxworks_cpu32 vxworks_sparc
-# vxworks_ppc750 vxworks_simso
-
-case $# in
-1) host=$1 ;;
-*) echo "usage: configure.vxworks host-configuration"; exit 1 ;;
-esac
-
-case $1 in
-vxworks_cpu32) ;;
-vxworks_ppc750) ;;
-vxworks_ppc860) ;;
-vxworks_ppc603) ;;
-vxworks_ppc603_nolongcall) ;;
-vxworks_sparc) ;;
-vxworks_simso) ;;
-vxworks_simlinux) ;;
-vxworks_ppc32) ;;
-*) echo "usage: configure.vxworks TARGET";
- echo "where TARGET is one of vxworks_cpu32, vxworks_ppc750, vxworks_ppc860, vxworks_ppc603, vxworks_ppc603_nolongcall, vxworks_sparc, vxworks_simso, vxworks_simlinux, vxworks_ppc32"; exit 1;;
-esac
-
-if [ "x$ERL_TOP" = x ]; then
- echo "You need to set ERL_TOP!"
- exit 1
-fi
-
-
-target=$host
-
-# Find out the HOST and WIND_BASE environment
-HOST_TYPE=${HOST_TYPE:=sun4-solaris2}
-case $1 in
-vxworks_ppc750) VXTOP=Tornado2.2 ;;
-vxworks_simso) VXTOP=WindRiver ;;
-vxworks_simlinux) VXTOP=WindRiver ;;
-vxworks_ppc32) VXTOP=WindRiver ;;
-*) VXTOP=wind ;;
-esac
-
-WIND_BASE=${WIND_BASE:=`ypmatch tornado passwd | awk -F: '{print $6}'`/$VXTOP}
-
-# These are created by autoconf.
-MKDIRS="${ERL_TOP}/lib/os_mon/priv/bin/$target
- ${ERL_TOP}/lib/os_mon/priv/obj/$target
- ${ERL_TOP}/lib/orber/priv/obj/$target
- ${ERL_TOP}/lib/orber/priv/bin/$target
- ${ERL_TOP}/lib/ic/priv/lib/$target
- ${ERL_TOP}/lib/ic/priv/obj/$target
- ${ERL_TOP}/lib/asn1/priv/lib/$target
- ${ERL_TOP}/lib/asn1/priv/obj/$target
- ${ERL_TOP}/lib/erl_interface/obj/$target
- ${ERL_TOP}/lib/erl_interface/obj.debug/$target
- ${ERL_TOP}/lib/erl_interface/bin/$target
- ${ERL_TOP}/lib/runtime_tools/priv/lib/$target
- ${ERL_TOP}/lib/runtime_tools/priv/obj/$target
- ${ERL_TOP}/erts/obj/$target
- ${ERL_TOP}/erts/obj.debug/$target
- ${ERL_TOP}/bin/$target"
-
-for dir in $MKDIRS; do
- test ! -d "$dir" && mkdir -p "$dir"
-done
-
-#
-# Create Makefiles for vxWorks.
-#
-my_root=${ERL_TOP}/erts/emulator
-emu_test=$my_root/test
-beam=$my_root/beam
-erts_lib_src=${ERL_TOP}/erts/lib_src
-erts_incl=${ERL_TOP}/erts/include
-erts_incl_intrnl=${ERL_TOP}/erts/include/internal
-etcdir=${ERL_TOP}/erts/etc/common
-erlint_dir=${ERL_TOP}/lib/erl_interface/src
-epmd_dir=${ERL_TOP}/erts/epmd/src
-os_mon_dir=${ERL_TOP}/lib/os_mon/c_src
-orber_dir=${ERL_TOP}/lib/orber/c_src
-ic_dir=${ERL_TOP}/lib/ic/c_src
-asn1_dir=${ERL_TOP}/lib/asn1/c_src
-internal_tools_dir=${ERL_TOP}
-libdir=${ERL_TOP}/lib
-tsdir=$libdir/test_server/src
-zlibdir=${ERL_TOP}/erts/emulator/zlib
-runtime_tools_dir=${ERL_TOP}/lib/runtime_tools/c_src
-tools_dir=${ERL_TOP}/lib/tools/c_src
-
-CONFIG_FILES="${ERL_TOP}/erts/emulator/$host/Makefile
- $erts_lib_src/$host/Makefile
- $erts_incl/$host/erl_int_sizes_config.h
- $erts_incl_intrnl/$host/ethread.mk
- $erts_incl_intrnl/$host/ethread_header_config.h
- $etcdir/$host/Makefile
- $erlint_dir/$host/Makefile
- $erlint_dir/$host/eidefs.mk
- $epmd_dir/$host/Makefile
- $internal_tools_dir/make/$host/otp.mk
- $os_mon_dir/$host/Makefile
- $zlibdir/$host/Makefile
- $ic_dir/$host/Makefile
- $asn1_dir/$host/Makefile
- $runtime_tools_dir/$host/Makefile
- $tools_dir/$host/Makefile
- $orber_dir/$host/Makefile"
-
-for file in $CONFIG_FILES; do
- new_name=`echo $file|sed "s%/$host/%/$target/%"`
- dir=`echo $new_name|sed 's%/[^/][^/]*$%%'`
- if test "$dir" != "$new_name" && test "$dir" != .; then
- test ! -d "$dir" && mkdir "$dir"
- fi
-
- sole_name=`echo $file|sed "s%.*$target/%%"`
- in_file=`echo $dir|sed "s%/[^/][^/]*$%/$sole_name.in%"`
- echo "creating $new_name"
- sed -f vxworks/sed.$target -f vxworks/sed.general \
- -e "s,@HOST_TYPE@,$HOST_TYPE,g" \
- -e "s,@WIND_BASE@,$WIND_BASE,g" \
- -e "s,@TARGET@,$target,g" \
- $in_file > $new_name
-done
-
-
diff --git a/lib/diameter/autoconf/vxworks/sed.general b/lib/diameter/autoconf/vxworks/sed.general
deleted file mode 100644
index 9199983e16..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.general
+++ /dev/null
@@ -1,124 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2011. 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
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles
-# for vxworks from the generic Makefile.in that is found in a number
-# of directories (see configure.vxworks).
-#
-# This is the general part that is common for all architectures.
-#
-
-# Size of data types.
-s|^#undef SIZEOF_CHAR|#define SIZEOF_CHAR 1|
-s|^#undef SIZEOF_SHORT|#define SIZEOF_SHORT 2|
-s|^#undef SIZEOF_INT|#define SIZEOF_INT 4|
-s|^#undef SIZEOF_LONG_LONG|#define SIZEOF_LONG_LONG 8|
-s|^#undef SIZEOF_LONG$|#define SIZEOF_LONG 4|
-
-# General stuff.
-s|@erts_rootdir@|/clearcase/otp/erts|
-
-s|@LIBOBJS@|$(OBJDIR)/elib_malloc.o|
-s|@DLOAD_LIB@||
-s|@LDFLAGS@||
-# FIXME: A bit strange to clear out remaining DED_*
-s|@DED_LDFLAGS@||
-s|@DED_CFLAGS@||
-s|@STATIC_CFLAGS@||
-s|@GCCLIB@|libgcc.a|
-s|@DEFS@||
-s|@DEXPORT@||
-s|@DCFLAGS@||
-s|@THR_DEFS@||
-s|@THR_LIBS@||
-s|@THR_LIB_NAME@||
-s|@THR_X_LIBS@||
-s|@ETHR_X_LIBS@||
-s|@ETHR_LIBS@||
-s|@ETHR_LIB_NAME@||
-s|@ETHR_DEFS@||
-s|@ETHR_THR_LIB_BASE@||
-s|@EMU_THR_DEFS@||
-s|@EMU_THR_LIBS@||
-s|@EMU_THR_LIB_NAME@|ethread|
-s|@ERTS_ENABLE_KERNEL_POLL@|no|
-s|@cc_root@|/clearcase/otp/|
-# Define VxWorks even though cross-compiling.
-s|@HCFLAGS@|-DVXWORKS|
-s|@HCLIBS@||
-s|@ENABLE_ALLOC_TYPE_VARS@||
-s|@TERMCAP_LIB@||
-s|@ERTS_BUILD_SMP_EMU@|no|
-s|@HAVE_VALGRIND@|no|
-s|@EXEEXT@||
-s|@WITH_SCTP@||
-
-# HiPE
-s|@HIPE_ENABLED@||
-s|@PERFCTR_PATH@||
-s|@USE_PERFCTR@||
-
-# m4
-s|@OPSYS@|noopsys|
-
-# Conditional inclusion of applications
-s|@HIPE_APP@||
-s|@SSL_APP@|ssl|
-s|@CRYPTO_APP@|crypto|
-s|@SSH_APP@|ssh|
-
-# The target tools prefix, prepended to all cc,ld,as etc commands
-s|@TTPREFIX@|GCC_EXEC_PREFIX=@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/host/@HOST_TYPE@/bin/|
-
-# Install programs etc
-s|@PERL@|perl|
-s|@INSTALL@|/usr/ucb/install -c|
-s|@INSTALL_PROGRAM@|${INSTALL}|
-s|@INSTALL_SCRIPT@|${INSTALL}|
-s|@INSTALL_DATA@|${INSTALL} -m 644|
-s|@INSTALL_DIR@|$(INSTALL) -d|
-s|@RM@|/bin/rm|
-s|@MKDIR@|/bin/mkdir|
-s|@ERLANG_OSTYPE@|vxworks|
-s|@vxworks_reclaim@|reclaim.h|
-s|@os_mon_programs@||
-s|@erlexec@|erl.exec|
-s|@EMU_LIBOBJS@||
-
-# General CFLAGS
-s|@GENERAL_CFLAGS@|-DHAVE_LOCALTIME_R -DHAVE_GMTIME_R -DENABLE_ELIB_MALLOC -DELIB_HEAP_USER -DELIB_SORTED_BLOCKS -DWORDS_BIGENDIAN -DELIB_DONT_INITIALIZE -DSIZEOF_CHAR=1 -DSIZEOF_SHORT=2 -DSIZEOF_INT=4 -DSIZEOF_LONG=4 -DSIZEOF_LONG_LONG=8 -DSIZEOF_VOID_P=4 -DERTS_USE_PORT_TASKS=1|g
-s|@WFLAGS@||
-
-# Thread flags for eidefs.mk (erl_interface)
-s|@EI_THREADS@|false|
-
-# Make java code compile although we don't test it on VxWorks (no license)
-s|@JAVAC@|javac|
-
-# What is this anyway?
-# Disable it and see what breaks.
-#s|@ded_soname@||
-
-# Only variable substituted directly
-s|$(LDFLAGS)|-r -d|
-s|@LIBRT@||
-# XXX What is EFFLAGS? Not used in the emulator Makefile.in anyway.
-s|$(EFLAGS)|-DENABLE_ELIB_MALLOC -DELIB_HEAP_USER -DELIB_SORTED_BLOCKS|
-
diff --git a/lib/diameter/autoconf/vxworks/sed.vxworks_cpu32 b/lib/diameter/autoconf/vxworks/sed.vxworks_cpu32
deleted file mode 100644
index 5a1590e786..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.vxworks_cpu32
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2011. 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
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_cpu32|
-s|@system_type@|vxworks_cpu32|
-s|@CC@|@TTPREFIX@cc68k|
-s|@HCC@|gcc|
-s|@LD@|@TTPREFIX@ld68k|
-s|@LIBS@||
-s|@DED_LD@|@TTPREFIX@ld68k|
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_FLAGS@|-g|
-s|@GCCLIB_PATH@|@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/m68k-wrs-vxworks/cygnus-2.7.2-960126/m68000/msoft-float/libgcc.a|
-s|@RANLIB@|@TTPREFIX@ranlib68k|
-s|@AR@|@TTPREFIX@ar68k|
-s|@STRIP@|@TTPREFIX@strip68k|
-s|@SYMPREFIX@|_|
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/m68k-wrs-vxworks/cygnus-2.7.2-960126/m68000/msoft-float -lgcc|
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=CPU32 -mnobitfield -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -fno-builtin -nostdinc -fvolatile -msoft-float|
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=CPU32 -mnobitfield -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -fno-builtin -nostdinc -fvolatile -msoft-float|
diff --git a/lib/diameter/autoconf/vxworks/sed.vxworks_ppc32 b/lib/diameter/autoconf/vxworks/sed.vxworks_ppc32
deleted file mode 100644
index 9104b24ed3..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.vxworks_ppc32
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2006-2011. 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
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Peter Andersson
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_ppc32|
-s|@system_type@|vxworks_ppc32|
-s|@ARCH@|ppc32|
-s|@CC@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ccppc -mlongcall|
-s|@HCC@|gcc|
-s|@LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldppc|
-s|@STRIP@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/workbench-2.3/@HOST_TYPE@/bin/stripppc|
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/vxworks-6.3/target/lib/ppc/PPC32/common -lgcc|
-s|@DED_LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldppc|
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# generate dwarf debug code on PPC ..
-s|@DEBUG_FLAGS@|-gdwarf|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/vxworks-6.3/target/lib/ppc/PPC32/common/libgcc.a|
-s|@RANLIB@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ranlibppc|
-s|@AR@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/arppc|
-# -Dasm(X)= is for beam
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC32 -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc/powerpc-wrs-vxworks/3.4.4/include -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -mstrict-align -fvolatile -fno-builtin |
-
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC32 -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -I@WIND_BASE@/vxworks-6.3/target/h -mstrict-align -fvolatile -fno-builtin |
diff --git a/lib/diameter/autoconf/vxworks/sed.vxworks_ppc603 b/lib/diameter/autoconf/vxworks/sed.vxworks_ppc603
deleted file mode 100644
index e1dd7c5afd..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.vxworks_ppc603
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2000-2011. 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
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_ppc603|
-s|@system_type@|vxworks_ppc603|
-s|@ARCH@|ppc603|
-s|@CC@|@TTPREFIX@ccppc -mlongcall|
-s|@HCC@|gcc|
-s|@LD@|@TTPREFIX@ldppc|
-s|@STRIP@|@TTPREFIX@stripppc|
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126 -lgcc|
-s|@DED_LD@|@TTPREFIX@ldppc|
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# generate dwarf debug code on PPC ..
-s|@DEBUG_FLAGS@|-gdwarf|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126/libgcc.a|
-s|@RANLIB@|@TTPREFIX@ranlibppc|
-s|@AR@|@TTPREFIX@arppc|
-# -Dasm(X)= is for beam
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC603 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mstrict-align -fvolatile -fno-builtin -fno-for-scope -D_GNU_TOOL|
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC603 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mstrict-align -fvolatile -fno-builtin -fno-for-scope -D_GNU_TOOL|
diff --git a/lib/diameter/autoconf/vxworks/sed.vxworks_ppc603_nolongcall b/lib/diameter/autoconf/vxworks/sed.vxworks_ppc603_nolongcall
deleted file mode 100644
index 5d7673d323..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.vxworks_ppc603_nolongcall
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2011. 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
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_ppc603|
-s|@system_type@|vxworks_ppc603|
-s|@ARCH@|ppc603|
-s|@CC@|@TTPREFIX@ccppc|
-s|@HCC@|gcc|
-s|@LD@|@TTPREFIX@ldppc|
-s|@STRIP@|@TTPREFIX@stripppc|
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126 -lgcc|
-s|@DED_LD@|@TTPREFIX@ldppc|
-s|@DED_CFLAGS@|@CFLAGS@|
-# generate dwarf debug code on PPC ..
-s|@DEBUG_FLAGS@|-gdwarf|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126/libgcc.a|
-s|@RANLIB@|@TTPREFIX@ranlibppc|
-s|@AR@|@TTPREFIX@arppc|
-# -Dasm(X)= is for beam
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC603 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mstrict-align -fvolatile -fno-builtin -fno-for-scope -D_GNU_TOOL|
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC603 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mstrict-align -fvolatile -fno-builtin -fno-for-scope -D_GNU_TOOL|
-
diff --git a/lib/diameter/autoconf/vxworks/sed.vxworks_ppc860 b/lib/diameter/autoconf/vxworks/sed.vxworks_ppc860
deleted file mode 100644
index 7a3c32df5b..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.vxworks_ppc860
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2011. 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
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_ppc860|
-s|@system_type@|vxworks_ppc860|
-s|@ARCH@|ppc860|
-s|@CC@|@TTPREFIX@ccppc -mlongcall|
-s|@HCC@|gcc|
-s|@LD@|@TTPREFIX@ldppc|
-s|@STRIP@|@TTPREFIX@stripppc|
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126/soft-float -lgcc|
-s|@DED_LD@|@TTPREFIX@ldppc|
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# generate dwarf debug code on PPC ..
-s|@DEBUG_FLAGS@|-gdwarf|
-# remove -g option (go for dwarf)
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/powerpc-wrs-vxworks/cygnus-2.7.2-960126/soft-float/libgcc.a|
-s|@RANLIB@|@TTPREFIX@ranlibppc|
-s|@AR@|@TTPREFIX@arppc|
-
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC860 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mcpu=860 -fvolatile -fno-builtin -fno-for-scope -msoft-float -D_GNU_TOOL -nostdinc|
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=PPC860 -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DVXWORKS -I@WIND_BASE@/target/h -mcpu=powerpc -fvolatile -fno-builtin -fno-for-scope -msoft-float -D_GNU_TOOL -nostdinc|
diff --git a/lib/diameter/autoconf/vxworks/sed.vxworks_simlinux b/lib/diameter/autoconf/vxworks/sed.vxworks_simlinux
deleted file mode 100644
index 56eae6507c..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.vxworks_simlinux
+++ /dev/null
@@ -1,59 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2008-2011. 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
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Peter Andersson
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_simlinux|
-s|@system_type@|vxworks_simlinux|
-s|@ARCH@|simlinux|
-
-s|@CC@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ccpentium|
-
-s|@HCC@|gcc|
-
-s|@LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldpentium|
-
-#s|@STRIP@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/strip|
-s|@STRIP@||
-
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/vxworks-6.3/target/lib/simlinux/SIMLINUX/common -lgcc|
-
-s|@DED_LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldpentium|
-
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/vxworks-6.3/target/lib/simlinux/SIMLINUX/common/libgcc.a|
-
-s|@RANLIB@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ranlibpentium|
-
-s|@AR@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/arpentium|
-
-# -Dasm(X)= is for beam
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=SIMLINUX -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -DDEBUG -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc/i586-wrs-vxworks/3.4.4/include -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -fvolatile -fno-builtin |
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=SIMLINUX -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -DDEBUG -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -mstrict-align -fvolatile -fno-builtin |
diff --git a/lib/diameter/autoconf/vxworks/sed.vxworks_simso b/lib/diameter/autoconf/vxworks/sed.vxworks_simso
deleted file mode 100644
index 6b845d31de..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.vxworks_simso
+++ /dev/null
@@ -1,64 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2005-2011. 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
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Peter Andersson
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-#
-#
-s|@host@|vxworks_simso|
-s|@system_type@|vxworks_simso|
-s|@ARCH@|simso|
-
-# Tornado2.2: s|@CC@|@TTPREFIX@ccsimso|
-s|@CC@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ccsparc|
-
-s|@HCC@|gcc|
-
-# Tornado2.2: s|@LD@|@TTPREFIX@ldsimso|
-s|@LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldsparc|
-
-# Tornado2.2: s|@STRIP@|@TTPREFIX@stripsimso|
-s|@STRIP@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/stripsparc|
-
-s|@SYMPREFIX@||
-s|@LIBS@||
-s|@GCCLIBFLAGS@|-L@WIND_BASE@/vxworks-6.3/target/lib/simso/SIMSPARCSOLARIS/common -lgcc|
-
-# Tornado2.2: s|@DED_LD@|@TTPREFIX@ldsimso|
-s|@DED_LD@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ldsparc|
-
-s|@DED_CFLAGS@|@CFLAGS@|
-s|@DEBUG_CFLAGS@|@CFLAGS@|
-# remove -g option
-s|TYPE_FLAGS = -g |TYPE_FLAGS = |
-s|@GCCLIB_PATH@|@WIND_BASE@/vxworks-6.3/target/lib/simso/SIMSPARCSOLARIS/common/libgcc.a|
-
-# Tornado2.2: s|@RANLIB@|@TTPREFIX@ranlibsimso|
-s|@RANLIB@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/ranlibsparc|
-
-# Tornado2.2: s|@AR@|arsimso|
-s|@AR@|GCC_EXEC_PREFIX=@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc-lib/ @WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/bin/arsparc|
-
-# -Dasm(X)= is for beam
-s|@LIB_CFLAGS@|@CFLAGS@|
-
-s|@CFLAGS@|@GENERAL_CFLAGS@ -DCPU=SIMSPARCSOLARIS -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -DDEBUG -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/gnu/3.4.4-vxworks-6.3/@HOST_TYPE@/lib/gcc/sparc-wrs-vxworks/3.4.4/include -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -fvolatile -fno-builtin |
-#s|@LIB_CFLAGS@|@GENERAL_CFLAGS@ -DCPU=SIMSPARCSOLARIS -DTOOL_FAMILY=gnu -DTOOL=gnu -DWANT_NONBLOCKING -DHAVE_SENS -DHAVE_MEMMOVE -DVXWORKS -DDEBUG -I@WIND_BASE@/vxworks-6.3/target/h -I@WIND_BASE@/vxworks-6.3/target/h/wrn/coreip -mstrict-align -fvolatile -fno-builtin |
diff --git a/lib/diameter/autoconf/vxworks/sed.vxworks_sparc b/lib/diameter/autoconf/vxworks/sed.vxworks_sparc
deleted file mode 100644
index 6f637d8746..0000000000
--- a/lib/diameter/autoconf/vxworks/sed.vxworks_sparc
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2011. 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
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-# Author: Patrik Winroth
-#
-# This sed program file is intended to be used when creating Makefiles for vxworks
-# from the generic Makefile.in that is found in a number of directories (see configure.vxworks)
-#
-
-# ccsparc -O2 doesn't work when compiling "rundir"/gc.c - signal 11 is generated when trying
-# therefore it is compiled with -O1 instead, which works - get a new ccsparc !
-s/\$(COMPILE\.emu) -o \$@ -c gc\.c/$(CC) @CFLAGS@ @DEFS@ -O1 $(BEAM_MODE) -I$(SYSDIR) -I$(EMUDIR) -I. $(CPPFLAGS) -c -o $@ -c gc.c/
-s/@host@/vxworks_sparc/
-s/@system_type@/vxworks_sparc/
-s/@CC@/\/home\/gandalf\/bsproj\/tools\/vw-gnu\/solaris.sparc\/bin\/ccsparc/
-s/@HCC@/gcc/
-s/@LD@/\/home\/gandalf\/bsproj\/tools\/vw-gnu\/solaris.sparc\/bin\/ldsparc/
-s/@DEBUG_FLAGS@/-g/
-s/@GCCLIB_PATH@/\/home\/gandalf\/bsproj\/tools\/vw-gnu\/solaris.sparc\/lib\/gcc-lib\/sparc-wrs-vxworks\/cygnus-2.2.3.1\/libgcc.a/
-s/@RANLIB@/ranlibsparc/
-s/@AR@/arsparc/
-s/@CFLAGS@/-I\/home\/gandalf\/bsproj\/BS.2\/UOS\/vw\/5.2\/h -DWANT_NONBLOCKING -DHAVE_MEMMOVE -DCPU=SPARC -DVXWORKS -fno-builtin -nostdinc/
-
diff --git a/lib/diameter/doc/src/Makefile b/lib/diameter/doc/src/Makefile
index cdf63e7fb6..59ca660797 100644
--- a/lib/diameter/doc/src/Makefile
+++ b/lib/diameter/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2011. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. 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/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index 93e2603c10..b8652a7482 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -43,13 +43,13 @@ under the License.
<description>
<p>
-This module provides the interface with which a user
-creates a service that sends and receives messages using the
+This module provides the interface with which a user can
+implement a Diameter node that sends and receives messages using the
Diameter protocol as defined in RFC 3588.</p>
<p>
Basic usage consists of creating a representation of a
-locally implemented Diameter peer and its capabilities with <seealso
+locally implemented Diameter node and its capabilities with <seealso
marker="#start_service">start_service/2</seealso>, adding transport
capability using <seealso
marker="#add_transport">add_transport/2</seealso> and sending Diameter
@@ -61,17 +61,15 @@ marker="diameter_app">diameter_app(3)</seealso> callback modules as
specified in the service configuration.</p>
<p>
-Beware the difference between <em>diameter application</em> and
-<em>Diameter application</em>.
+Beware the difference between <em>diameter</em> (not capitalised) and
+<em>Diameter</em> (capitalised).
The former refers to the Erlang application named diameter whose main
-api is defined here, the latter to an application of the Diameter
-protocol in the sense of RFC 3588.
-More generally, capitalized Diameter refers to the RFC
-and diameter to this implementation.</p>
+api is defined here, the latter to Diameter protocol in the sense of
+RFC 3588.</p>
<p>
-The diameter application must be started before calling functions in
-this module.</p>
+The diameter application must be started before calling most functions
+in this module.</p>
</description>
@@ -84,13 +82,15 @@ this module.</p>
<tag><c>Address()</c></tag>
<tag><c>DiameterIdentity()</c></tag>
+<tag><c>Grouped()</c></tag>
+<tag><c>OctetString()</c></tag>
<tag><c>Time()</c></tag>
<tag><c>Unsigned32()</c></tag>
<tag><c>UTF8String()</c></tag>
<item>
<p>
Types corresponding to RFC 3588 AVP Data Formats.
-Defined in <seealso marker="diameter_dict">diameter_dict(4)</seealso>.</p>
+Defined in <seealso marker="diameter_dict#DATA_TYPES">diameter_dict(4)</seealso>.</p>
<marker id="application_alias"/>
</item>
@@ -99,10 +99,9 @@ Defined in <seealso marker="diameter_dict">diameter_dict(4)</seealso>.</p>
<item>
<p>
A name identifying a Diameter application in
-service configuration passed to <seealso
-marker="#start_service">start_service/2</seealso> and passed to
-<seealso marker="#call">call/4</seealso> when sending requests
-belonging to the application.</p>
+service configuration.
+Passed to <seealso marker="#call">call/4</seealso> when sending requests
+defined by the application.</p>
<marker id="application_module"/>
</item>
@@ -120,19 +119,14 @@ ExtraArgs = list()
A module implementing the callback interface defined in <seealso
marker="diameter_app">diameter_app(3)</seealso>, along with any
extra arguments to be appended to those documented for the interface.
-Any extra arguments are appended to the documented list of arguments for
-each function.
-Note that additional arguments specific to an outgoing request be
+Note that extra arguments specific to an outgoing request can be
specified to <seealso marker="#call">call/4</seealso>, in which case
-the call-specific arguments are appended to any specified with the
-callback module.</p>
+those are are appended to any module-specific extra arguments.</p>
<p>
Specifying a <c>#diameter_callback{}</c> record allows individual
functions to be configured in place of the usual <seealso
-marker="diameter_app">diameter_app(3)</seealso> callbacks, with
-default implementations provided by module <c>diameter_callback</c>
-unless otherwise specified.
+marker="diameter_app">diameter_app(3)</seealso> callbacks.
See that module for details.</p>
<marker id="application_opt"/>
@@ -142,18 +136,18 @@ See that module for details.</p>
<item>
<p>
-Options defining a Diameter application as configured in an
-<c>application</c> option passed to
-<seealso marker="#start_service">start_service/2</seealso>.</p>
+Options defining a Diameter application.
+Has one the following types.</p>
<taglist>
-<tag><c>{alias, application_alias()}</c></tag>
+<tag><c>{alias, <seealso marker="#application_alias">application_alias()</seealso>}</c></tag>
<item>
<p>
An unique identifier for the application in the scope of the
service.
-Optional, defaults to the value of the <c>dictionary</c> option.</p>
+Defaults to the value of the <c>dictionary</c> option if
+unspecified.</p>
</item>
<tag><c>{dictionary, atom()}</c></tag>
@@ -166,23 +160,23 @@ documented in <seealso
marker="diameter_dict">diameter_dict(4)</seealso>.</p>
</item>
-<tag><c>{module, application_module()}</c></tag>
+<tag><c>{module, <seealso marker="#application_module">application_module()</seealso>}</c></tag>
<item>
<p>
-A callback module with which messages of the Diameter application are
+The callback module with which messages of the Diameter application are
handled.
See <seealso marker="diameter_app">diameter_app(3)</seealso> for
-information on the required interface and semantics.</p>
+the required interface and semantics.</p>
</item>
<tag><c>{state, term()}</c></tag>
<item>
<p>
The initial callback state.
-Defaults to the value of the <c>alias</c> option if unspecified.
-The prevailing state is passed to certain
+The prevailing state is passed to some
<seealso marker="diameter_app">diameter_app(3)</seealso>
-callbacks, which can then return a new state.</p>
+callbacks, which can then return a new state.
+Defaults to the value of the <c>alias</c> option if unspecified.</p>
</item>
<tag><c>{call_mutates_state, true|false}</c></tag>
@@ -190,19 +184,18 @@ callbacks, which can then return a new state.</p>
<p>
Specifies whether or not the <seealso
marker="diameter_app#pick_peer">pick_peer/4</seealso>
-application callback (following from a call to
-<seealso marker="#call">call/4</seealso>)
-can modifiy state,
+application callback can modify the application state,
Defaults to <c>false</c> if unspecified.</p>
+<note>
<p>
-Note that <seealso
-marker="diameter_app#pick_peer">pick_peer</seealso> callbacks are
-serialized when these are allowed to modify state, which is a
+<seealso marker="diameter_app#pick_peer">pick_peer</seealso> callbacks
+are serialized when these are allowed to modify state, which is a
potential performance bottleneck.
A simple Diameter client may suffer no ill effects from using mutable
-state but a server or agent that responds to incoming request but
-sending its own requests should probably avoid it.</p>
+state but a server or agent that responds to incoming request should
+probably avoid it.</p>
+</note>
</item>
<tag><c>{answer_errors, callback|report|discard}</c></tag>
@@ -213,9 +206,9 @@ decode errors are handled.
If <c>callback</c> then errors result in a <seealso
marker="diameter_app#handle_answer">handle_answer/4</seealso>
callback in the same fashion as for <seealso
-marker="diameter_app#handle_request">handle_request/3</seealso>, in the
-<c>errors</c> field of the <c>diameter_packet</c> record passed into
-the callback.
+marker="diameter_app#handle_request">handle_request/3</seealso>, with
+errors communicated in the <c>errors</c> field of the
+<c>#diameter_packet{}</c> record passed to the callback.
If <c>report</c> then an answer containing errors is discarded
without a callback and a warning report is written to the log.
If <c>discard</c> then an answer containing errors is silently
@@ -239,7 +232,8 @@ Defaults to <c>report</c> if unspecified.</p>
<p>
Options available to <seealso marker="#call">call/4</seealso> when
-sending an outgoing Diameter request.</p>
+sending an outgoing Diameter request.
+Has one of the following types.</p>
<taglist>
@@ -248,12 +242,12 @@ sending an outgoing Diameter request.</p>
<p>
Extra arguments to append to callbacks to the callback
module in question.
-These are appended to any extra arguments configured with the callback
+These are appended to any extra arguments configured on the callback
itself.
Multiple options append to the argument list.</p>
</item>
-<tag><c>{filter, peer_filter()}</c></tag>
+<tag><c>{filter, <seealso marker="#peer_filter">peer_filter()</seealso>}</c></tag>
<item>
<p>
A filter to apply to the list of available peers before passing them to
@@ -264,7 +258,7 @@ corresponding list of filters.
Defaults to <c>none</c>.</p>
</item>
-<tag><c>{timeout, Unsigned32()}</c></tag>
+<tag><c>{timeout, <seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>}</c></tag>
<item>
<p>
The number of milliseconds after which the request should
@@ -298,121 +292,55 @@ to fail.</p>
<item>
<p>
-AVP values used in outgoing CER/CEA messages during capabilities exchange.
-Can be configured both on a service and a transport, the latter taking
-precedence over the former.</p>
+AVP values sent in outgoing CER or CEA messages during capabilities
+exchange.
+Can be configured both on a service and a transport, values specified
+on the latter taking precedence over any specified on the former.
+Has one of the following types.</p>
<taglist>
-<tag><c>{'Origin-Host', DiameterIdentity()}</c></tag>
-<item>
-<p>
-Value of the Origin-Host AVP in outgoing messages.</p>
-</item>
-
-<tag><c>{'Origin-Realm', DiameterIdentity()}</c></tag>
-<item>
-<p>
-Value of the Origin-Realm AVP in outgoing messages.</p>
-</item>
-
-<tag><c>{'Host-IP-Address', [Address()]}</c></tag>
-<item>
-<p>
-Values of Host-IP-Address AVPs.
-Optional.</p>
-
-<p>
-The list of addresses is available to the start function of a
-transport module, which either uses them as is or returns a new list
-(typically as configured as <c>transport_config()</c> on the
-transport module in question) in order for the correct list of
-addresses to be sent in capabilities exchange messages.</p>
-</item>
-
-<tag><c>{'Vendor-Id', Unsigned32()}</c></tag>
-<item>
-<p>
-Value of the Vendor-Id AVP sent in an outgoing capabilities
-exchange message.</p>
-</item>
-
-<tag><c>{'Product-Name', UTF8String()}</c></tag>
+<tag><c>{'Origin-Host', <seealso marker="diameter_dict#DATA_TYPES">DiameterIdentity()</seealso>}</c></tag>
+<tag><c>{'Origin-Realm', <seealso marker="diameter_dict#DATA_TYPES">DiameterIdentity()</seealso>}</c></tag>
+<tag><c>{'Host-IP-Address', [<seealso marker="diameter_dict#DATA_TYPES">Address()</seealso>]}</c></tag>
<item>
<p>
-Value of the Product-Name AVP sent in an outgoing capabilities
-exchange message.</p>
+An address list is available to the start function of a
+<seealso marker="diameter_transport">transport module</seealso>, which
+can return a new list for use in the subsequent CER or CEA.
+Host-IP-Address need not be specified if the transport start function
+returns an address list.</p>
</item>
-<tag><c>{'Origin-State-Id', Unsigned32()}</c></tag>
+<tag><c>{'Vendor-Id', <seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>}</c></tag>
+<tag><c>{'Product-Name', <seealso marker="diameter_dict#DATA_TYPES">UTF8String()</seealso>}</c></tag>
+<tag><c>{'Origin-State-Id', <seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>}</c></tag>
<item>
<p>
-Value of Origin-State-Id to be included in outgoing messages sent by
-diameter itself.
-In particular, the AVP will be included in CER/CEA and DWR/DWA messages.
-Optional.</p>
-
-<p>
+Origin-State-Id is optional but will be included in outgoing messages
+sent by diameter itself: CER/CEA, DWR/DWA and DPR/DPA.
Setting a value of <c>0</c> (zero) is equivalent to not setting a
value as documented in RFC 3588.
The function <seealso
marker="#origin_state_id">origin_state_id/0</seealso>
-can be used as to retrieve a value that is set when the diameter
+can be used as to retrieve a value that is computed when the diameter
application is started.</p>
</item>
-<tag><c>{'Supported-Vendor-Id', [Unsigned32()]}</c></tag>
-<item>
-<p>
-Values of Supported-Vendor-Id AVPs sent in an outgoing
-capabilities exchange message.
-Optional, defaults to the empty list.</p>
-</item>
-
-<tag><c>{'Auth-Application-Id', [Unsigned32()]}</c></tag>
+<tag><c>{'Supported-Vendor-Id', [<seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>]}</c></tag>
+<tag><c>{'Auth-Application-Id', [<seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>]}</c></tag>
+<tag><c>{'Inband-Security-Id', [<seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>]}</c></tag>
<item>
<p>
-Values of Auth-Application-Id AVPs sent in an outgoing
-capabilities exchange message.
-Optional, defaults to the empty list.</p>
-</item>
-
-<tag><c>{'Inband-Security-Id', [Unsigned32()]}</c></tag>
-<item>
-<p>
-Values of Inband-Security-Id AVPs sent in an outgoing
-capabilities exchange message.
-Optional, defaults to the empty list, which is equivalent to a
-list containing only 0 (= NO_INBAND_SECURITY).</p>
-
-<p>
+Inband-Security-Id defaults to the empty list, which is equivalent to a
+list containing only 0 (= NO_INBAND_SECURITY).
If 1 (= TLS) is specified then TLS is selected if the CER/CEA received
from the peer offers it.</p>
</item>
-<tag><c>{'Acct-Application-Id', [Unsigned32()]}</c></tag>
-<item>
-<p>
-Values of Acct-Application-Id AVPs sent in an outgoing
-capabilities exchange message.
-Optional, defaults to the empty list.</p>
-</item>
-
-<tag><c>{'Vendor-Specific-Application-Id', [Grouped()]}</c></tag>
-<item>
-<p>
-Values of Vendor-Specific-Application-Id AVPs sent in
-an outgoing capabilities exchange message.
-Optional, defaults to the empty list.</p>
-</item>
-
-<tag><c>{'Firmware-Revision', Unsigned32()}</c></tag>
-<item>
-<p>
-Value of the Firmware-Revision AVP sent in an outgoing capabilities
-exchange message.
-Optional.</p>
-</item>
+<tag><c>{'Acct-Application-Id', [<seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>]}</c></tag>
+<tag><c>{'Vendor-Specific-Application-Id', [<seealso marker="diameter_dict#DATA_TYPES">Grouped()</seealso>]}</c></tag>
+<tag><c>{'Firmware-Revision', <seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>}</c></tag>
</taglist>
@@ -441,14 +369,17 @@ eval(F) ->
</code>
<p>
-Applying an evaluable() <c>E</c> to an argument list <c>A</c>
+Applying an <c><seealso marker="#evaluable">evaluable()</seealso></c>
+<c>E</c> to an argument list <c>A</c>
is meant in the sense of <c>eval([E|A])</c>.</p>
+<warning>
<p>
-Beware of using local funs (that is, fun expressions not of the
-form <c>fun Module:Name/Arity</c>) in situations in which the fun is
-not short-lived and code is to be upgraded at runtime since any
-processes retaining such a fun will have a reference to old code.</p>
+Beware of using fun expressions of the form <c>fun Name/Arity</c> (not
+fun Mod:Name/Arity) in situations in which the fun is not short-lived
+and code is to be upgraded at runtime since any processes retaining
+such a fun will have a reference to old code.</p>
+</warning>
<marker id="peer_filter"/>
</item>
@@ -490,63 +421,76 @@ or any peer if the request does not contain
a <c>Destination-Realm</c> AVP.</p>
</item>
-<tag><c>{host, any|DiameterIdentity()}</c></tag>
+<tag><c>{host, any|<seealso marker="diameter_dict#DATA_TYPES">DiameterIdentity()</seealso>}</c></tag>
<item>
<p>
Matches only those peers whose <c>Origin-Host</c> has the
specified value, or all peers if the atom <c>any</c>.</p>
</item>
-<tag><c>{realm, any|DiameterIdentity()</c></tag>
+<tag><c>{realm, any|<seealso marker="diameter_dict#DATA_TYPES">DiameterIdentity()</seealso></c></tag>
<item>
<p>
Matches only those peers whose <c>Origin-Realm</c> has the
-value, or all peers if the atom <c>any</c>.</p>
+specified value, or all peers if the atom <c>any</c>.</p>
</item>
-<tag><c>{eval, evaluable()}</c></tag>
+<tag><c>{eval, <seealso marker="#evaluable">evaluable()</seealso>}</c></tag>
<item>
<p>
-Matches only those peers for which the specified evaluable() returns
+Matches only those peers for which the specified <c><seealso
+marker="#evaluable">evaluable()</seealso></c> returns
<c>true</c> on the connection's <c>diameter_caps</c> record.
Any other return value or exception is equivalent to <c>false</c>.</p>
</item>
-<tag><c>{neg, peer_filter()}</c></tag>
+<tag><c>{neg, <seealso marker="#peer_filter">peer_filter()</seealso>}</c></tag>
<item>
<p>
Matches only those peers not matched by the specified filter.</p>
</item>
-<tag><c>{all, [peer_filter()]}</c></tag>
+<tag><c>{all, [<seealso marker="#peer_filter">peer_filter()</seealso>]}</c></tag>
<item>
<p>
-Matches only those peers matched by each filter of the specified list.</p>
+Matches only those peers matched by each filter in the specified list.</p>
</item>
-<tag><c>{any, [peer_filter()]}</c></tag>
+<tag><c>{any, [<seealso marker="#peer_filter">peer_filter()</seealso>]}</c></tag>
<item>
<p>
-Matches only those peers matched by at least one filter of the
+Matches only those peers matched by at least one filter in the
specified list.</p>
</item>
</taglist>
<p>
-Note that the <c>host</c> and <c>realm</c> filters examine the
+An invalid filter is equivalent to <c>{any,[]}</c>, a filter
+that matches no peer.</p>
+
+<note>
+<p>
+The <c>host</c> and <c>realm</c> filters examine the
outgoing request as passed to <seealso marker="#call">call/4</seealso>,
-assuming that this is a record- or list-valued message() as documented
-in <seealso marker="diameter_app">diameter_app(3)</seealso>, and that
-the message contains at most one of each AVP.
-If this is not the case then the <c>{host|realm, DiameterIdentity()}</c>
+assuming that this is a record- or list-valued <c><seealso
+marker="diameter_app#message">diameter_app:message()</seealso></c>,
+and that the message contains at most one of each AVP.
+If this is not the case then the <c>{host|realm, <seealso
+marker="diameter_dict#DATA_TYPES">DiameterIdentity()</seealso>}</c>
filters must be used to achieve the desired result.
-Note also that an empty host/realm (which should not be typical)
-is equivalent to an unspecified one for the purposes of filtering.</p>
+An empty <c><seealso
+marker="diameter_dict#DATA_TYPES">DiameterIdentity()</seealso></c>
+(which should not be typical)
+matches all hosts/realms for the purposes of filtering.</p>
+</note>
+<warning>
<p>
-An invalid filter is equivalent to <c>{any, []}</c>, a filter
-that matches no peer.</p>
+A <c>host</c> filter is not typically desirable when setting
+Destination-Host since it will remove peer agents from the
+candidates list.</p>
+</warning>
<marker id="service_event"/>
</item>
@@ -554,12 +498,12 @@ that matches no peer.</p>
<tag><c>service_event() = #diameter_event{}</c></tag>
<item>
<p>
-Event message sent to processes that have subscribed using <seealso
-marker="#subscribe">subscribe/1</seealso>.</p>
+An event message sent to processes that have subscribed to these using
+<seealso marker="#subscribe">subscribe/1</seealso>.</p>
<p>
-The <c>info</c> field of the event record can be one of the
-following.</p>
+The <c>info</c> field of the event record can have one of the
+following types.</p>
<taglist>
@@ -568,27 +512,27 @@ following.</p>
<tag><c>{down, Ref, Peer, Config}</c></tag>
<item>
<code>
-Ref = transport_ref()
-Peer = diameter_app:peer()
-Config = {connect|listen, [transport_opt()]}
+Ref = <seealso marker="#transport_ref">transport_ref()</seealso>
+Peer = <seealso marker="diameter_app#peer">diameter_app:peer()</seealso>
+Config = {connect|listen, [<seealso marker="#transport_opt">transport_opt()</seealso>]}
Pkt = #diameter_packet{}
</code>
<p>
The RFC 3539 watchdog state machine has
-transitioned into (<c>up</c>) or out of (<c>down</c>) the open
+transitioned into (<c>up</c>) or out of (<c>down</c>) the OKAY
state.
-If a <c>diameter_packet</c> record is present in an <c>up</c> tuple
-then there has been an exchange of capabilities exchange messages and
-the record contains the received CER or CEA, otherwise the
-connection has reestablished without the loss or transport
+If a <c>#diameter_packet{}</c> record is present in an <c>up</c> event
+then there has been a capabilties exchange on a newly established
+transport connection and the record contains the received CER or CEA.
+Otherwise a connection has reestablished without the loss or
connectivity.</p>
<p>
-Note that a single up/down event for a given peer corresponds to
-as many <seealso marker="diameter_app#peer_up">peer_up/peer_down</seealso>
-callbacks as there are Diameter applications shared by the peer,
-as determined during capablilities exchange.
+Note that a single <c>up</c>/<c>down</c> event for a given peer
+corresponds to one <seealso marker="diameter_app#peer_up">peer_up/peer_down</seealso>
+callback for each of the Diameter applications negotiated during
+capablilities exchange.
That is, the event communicates connectivity with the
peer as a whole while the callbacks communicate connectivity with
respect to individual Diameter applications.</p>
@@ -597,26 +541,28 @@ respect to individual Diameter applications.</p>
<tag><c>{reconnect, Ref, Opts}</c></tag>
<item>
<code>
-Ref = transport_ref()
-Opts = [transport_opt()]
+Ref = <seealso marker="#transport_ref">transport_ref()</seealso>
+Opts = [<seealso marker="#transport_opt">transport_opt()</seealso>]
</code>
<p>
A connecting transport is attempting to establish/reestablish a
-transport connection with a peer following <c>reconnect_timer</c> or
-<c>watchdog_timer</c> expiry.</p>
+transport connection with a peer following <seealso
+marker="#reconnect_timer">reconnect_timer</seealso> or
+<seealso marker="#watchdog_timer">watchdog_timer</seealso>
+expiry.</p>
</item>
<tag><c>{closed, Ref, Reason, Config}</c></tag>
<item>
<code>
-Ref = transport_ref()
-Config = {connect|listen, [transport_opt()]}
+Ref = <seealso marker="#transport_ref">transport_ref()</seealso>
+Config = {connect|listen, [<seealso marker="#transport_opt">transport_opt()</seealso>]}
</code>
<p>
-Capabilities exchange has failed. <c>Reason</c> can be one of
-the following.</p>
+Capabilities exchange has failed.
+<c>Reason</c> can have one of the following types.</p>
<taglist>
@@ -627,17 +573,17 @@ Result = ResultCode | {capabilities_cb, CB, ResultCode|discard}
Caps = #diameter_caps{}
Pkt = #diameter_packet{}
ResultCode = integer()
-CB = evaluable()
+CB = <seealso marker="#evaluable">evaluable()</seealso>
</code>
<p>
An incoming CER has been answered with the indicated result code or
discarded.
-The capabilities record contains pairs of values for the the local
-node and remote peer.
-The packet record contains the CER in question.
+<c>Caps</c> contains pairs of values for the the local node and remote
+peer.
+<c>Pkt</c> contains the CER in question.
In the case of rejection by a capabilities callback, the tuple
-indicates the rejecting callback.</p>
+contains the rejecting callback.</p>
</item>
<tag><c>{'CER', Caps, {ResultCode, Pkt}}</c></tag>
@@ -651,9 +597,8 @@ Pkt = #diameter_packet{}
<p>
An incoming CER contained errors and has been answered with the
indicated result code.
-The capabilities record contains only values for the the local
-node.
-The packet record contains the CER in question.</p>
+<c>Caps</c> contains only values for the the local node.
+<c>Pkt</c> contains the CER in question.</p>
</item>
<tag><c>{'CEA', Result, Caps, Pkt}</c></tag>
@@ -669,11 +614,11 @@ ResultCode = integer()
An incoming CEA has been rejected for the indicated reason.
An integer-valued <c>Result</c> indicates the result code sent
by the peer.
-The capabilities record contains pairs of values for the the local
-node and remote peer.
-The packet record contains the CEA in question.
+<c>Caps</c> contains pairs of values for the the local node and remote
+peer.
+<c>Pkt</c> contains the CEA in question.
In the case of rejection by a capabilities callback, the tuple
-indicates the rejecting callback.</p>
+contains the rejecting callback.</p>
</item>
<tag><c>{'CEA', Caps, Pkt}</c></tag>
@@ -684,14 +629,27 @@ Pkt = #diameter_packet{}
</code>
<p>
-An incoming CER contained errors and has been rejected.
-The capabilities record contains only values for the the local node.
-The packet record contains the CEA in question.</p>
+An incoming CEA contained errors and has been rejected.
+<c>Caps</c> contains only values for the the local node.
+<c>Pkt</c> contains the CEA in question.</p>
</item>
</taglist>
</item>
+<tag><c>{watchdog, Ref, PeerRef, {From, To}, Config}</c></tag>
+<item>
+<code>
+Ref = <seealso marker="#transport_ref">transport_ref()</seealso>
+PeerRef = <seealso marker="diameter_app#peer_ref">diameter_app:peer_ref()</seealso>
+From, To = initial | okay | suspect | down | reopen
+Config = {connect|listen, [transport_opt()]}
+</code>
+
+<p>
+An RFC 3539 watchdog state machine has changed state.</p>
+</item>
+
</taglist>
<p>
@@ -708,8 +666,8 @@ The name of a service as passed to <seealso
marker="#start_service">start_service/2</seealso> and with which the
service is identified.
There can be at most one service with a given name on a given node.
-Note that <c>erlang:make_ref/0</c> can be used to generate a service name
-that is somewhat unique.</p>
+Note that <seealso marker="erts:erlang#make_ref-0">erlang:make_ref/0</seealso>
+can be used to generate a service name that is somewhat unique.</p>
<marker id="service_opt"/>
</item>
@@ -717,29 +675,28 @@ that is somewhat unique.</p>
<tag><c>service_opt()</c></tag>
<item>
<p>
-Options accepted by <seealso
+An option passed to <seealso
marker="#start_service">start_service/2</seealso>.
-Can be any <c>capability()</c> tuple as
+Can be any <c><seealso marker="#capability">capability()</seealso></c> as
well as the following.</p>
<taglist>
-<tag><c>{application, [application_opt()]}</c></tag>
+<tag><c>{application, [<seealso marker="#application_opt">application_opt()</seealso>]}</c></tag>
<item>
<p>
Defines a Diameter application supported by the service.</p>
<p>
-A service must define one application for each Diameter application it
-intends to support.
-For an outgoing Diameter request, the application is specified by
-passing the desired application's <c>application_alias()</c> to
-<seealso marker="#call">call/4</seealso>, while for an
+A service must configure one <c>application</c> for each Diameter
+application it intends to support.
+For an outgoing Diameter request, the relevant <c><seealso
+marker="#application_alias">application_alias()</seealso></c> is
+passed to <seealso marker="#call">call/4</seealso>, while for an
incoming request the application identifier in the message
-header determines the application (and callback module), the
-application identifier being specified in the <seealso
-marker="diameter_dict">dictionary</seealso> file defining the
-application.</p>
+header determines the application, the identifier being specified in
+the application's <seealso marker="diameter_dict">dictionary</seealso>
+file.</p>
</item>
</taglist>
@@ -750,8 +707,9 @@ application.</p>
<tag><c>transport_opt()</c></tag>
<item>
<p>
-Options accepted by <seealso
-marker="#add_transport">add_transport/2</seealso>.</p>
+An option passed to <seealso
+marker="#add_transport">add_transport/2</seealso>.
+Has one of the following types.</p>
<taglist>
<tag><c>{transport_module, atom()}</c></tag>
@@ -762,53 +720,77 @@ marker="diameter_transport">diameter_transport(3)</seealso>.
Defaults to <c>diameter_tcp</c> if unspecified.</p>
<p>
-The interface required of a transport module is documented in <seealso
-marker="diameter_transport">diameter_transport(3)</seealso>.</p>
+Multiple <c>transport_module</c> and <c>transport_config</c>
+options are allowed.
+The order of these is significant in this case (and only in this case),
+a <c>transport_module</c> being paired with the first
+<c>transport_config</c> following it in the options list, or the
+default value for trailing modules.
+Transport starts will be attempted with each of the
+modules in order until one establishes a connection within the
+corresponding timeout (see below) or all fail.</p>
</item>
<tag><c>{transport_config, term()}</c></tag>
+<tag><c>{transport_config, term(), <seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>}</c></tag>
<item>
<p>
A term passed as the third argument to the <seealso
marker="diameter_transport#start">start/3</seealso> function of
the relevant <c>transport_module</c> in order to start a transport process.
Defaults to the empty list if unspecified.</p>
+
+<p>
+The 3-tuple form additionally specifies an interval, in milliseconds,
+after which a started transport process should be terminated if it has
+not yet established a connection.
+For example, the following options on a connecting transport
+request a connection with one peer over SCTP or another
+(typically the same) over TCP.</p>
+
+<code>
+{transport_module, diameter_sctp}
+{transport_config, SctpOpts, 5000}
+{transport_module, diameter_tcp}
+{transport_config, TcpOpts}
+</code>
+
+<p>
+To listen on both SCTP and TCP, define one transport for each.</p>
</item>
-<tag><c>{applications, [application_alias()]}</c></tag>
+<tag><c>{applications, [<seealso marker="#application_alias">application_alias()</seealso>]}</c></tag>
<item>
<p>
-The list of Diameter applications to which usage of the transport
-should be restricted.
-Defaults to all applications configured on the service
-in question.</p>
+The list of Diameter applications to which the transport should be
+restricted.
+Defaults to all applications configured on the service in question.
+Applications not configured on the service in question are ignored.</p>
</item>
-<tag><c>{capabilities, [capability()]}</c></tag>
+<tag><c>{capabilities, [<seealso marker="#capability">capability()</seealso>]}</c></tag>
<item>
<p>
AVP's used to construct outgoing CER/CEA messages.
-Any AVP specified takes precedence over a corresponding value specified
-for the service in question.</p>
+Values take precedence over any specified on the service in
+question.</p>
<p>
Specifying a capability as a transport option
-may be particularly appropriate for Inband-Security-Id in case
+may be particularly appropriate for Inband-Security-Id, in case
TLS is desired over TCP as implemented by
-<seealso marker="diameter_tcp">diameter_tcp(3)</seealso> but
-not over SCTP as implemented by
-<seealso marker="diameter_sctp">diameter_sctp(3)</seealso>.</p>
+<seealso marker="diameter_tcp">diameter_tcp(3)</seealso>.</p>
</item>
-<tag><c>{capabilities_cb, evaluable()}</c></tag>
+<tag><c>{capabilities_cb, <seealso marker="#evaluable">evaluable()</seealso>}</c></tag>
<item>
<p>
A callback invoked upon reception of CER/CEA during capabilities
exchange in order to ask whether or not the connection should
be accepted.
-Applied to the transport reference (as returned by <seealso
-marker="#add_transport">add_transport/2</seealso>) and
-<c>diameter_caps</c> record of the connection.
+Applied to the relevant <c><seealso
+marker="#transport_ref">transport_ref()</seealso></c> and the
+<c>#diameter_caps{}</c> record of the connection.
Returning <c>ok</c> accepts the connection.
Returning <c>integer()</c> causes an incoming
CER to be answered with the specified Result-Code.
@@ -823,12 +805,14 @@ code causes the transport connection to be broken.</p>
Multiple <c>capabilities_cb</c> options can be specified, in which
case the corresponding callbacks are applied until either all return
<c>ok</c> or one does not.</p>
+
+<marker id="watchdog_timer"/>
</item>
<tag><c>{watchdog_timer, TwInit}</c></tag>
<item>
<code>
-TwInit = Unsigned32()
+TwInit = <seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>
| {M,F,A}
</code>
@@ -844,17 +828,19 @@ the callback.</p>
<p>
An integer value must be at least 6000 as required by RFC 3539.
Defaults to 30000 if unspecified.</p>
+
+<marker id="reconnect_timer"/>
</item>
<tag><c>{reconnect_timer, Tc}</c></tag>
<item>
<code>
-Tc = Unsigned32()
+Tc = <seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>
</code>
<p>
For a connecting transport, the RFC 3588 Tc timer, in milliseconds.
-Note that this timer determines the frequency with which the transport
+Note that this timer determines the frequency with which a transport
will attempt to establish a connection with its peer only <em>before</em>
an initial connection is established: once there is an initial
connection it's watchdog_timer that determines the frequency of
@@ -867,12 +853,13 @@ regarded as an initial connection rather than a reestablishment,
causing the RFC 3539 state machine to pass to state OPEN rather than
REOPEN.
Note that these semantics are not goverened by the RFC and
-that a listening transport's reconnect_timer should be greater than its
-peers's Tc plus jitter.</p>
+that a listening transport's <c>reconnect_timer</c> should be greater
+than its peer's Tw plus jitter.</p>
<p>
Defaults to 30000 for a connecting transport and 60000 for a listening
transport.</p>
+
</item>
</taglist>
@@ -884,6 +871,15 @@ marker="#service_info">service_info/2</seealso> and can be referred to
in predicate functions passed to <seealso
marker="#remove_transport">remove_transport/2</seealso>.</p>
+<marker id="transport_ref"/>
+</item>
+
+<tag><c>transport_ref() = reference()</c></tag>
+<item>
+<p>
+An reference returned by <seealso
+marker="#add_transport">add_transport/2</seealso> that
+identifies the configuration.</p>
</item>
</taglist>
@@ -896,13 +892,13 @@ marker="#remove_transport">remove_transport/2</seealso>.</p>
<!-- ===================================================================== -->
<func>
-<name>add_transport(SvcName, {connect|listen, Options})
+<name>add_transport(SvcName, {connect|listen, [Opt]})
-> {ok, Ref} | {error, Reason}</name>
<fsummary>Add transport capability to a service.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Options = [transport_opt()]</v>
-<v>Ref = ref()</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
+<v>Opt = <seealso marker="#transport_opt">transport_opt()</seealso></v>
+<v>Ref = <seealso marker="#transport_ref">transport_ref()</seealso></v>
<v>Reason = term()</v>
</type>
<desc>
@@ -910,8 +906,8 @@ marker="#remove_transport">remove_transport/2</seealso>.</p>
Add transport capability to a service.</p>
<p>
-The service will start a transport process(es) in order to establish a
-connection with the peer, either by connecting to the peer
+The service will start transport processes as required in order to
+establish a connection with the peer, either by connecting to the peer
(<c>connect</c>) or by accepting incoming connection requests
(<c>listen</c>).
A connecting transport establishes transport connections with at most
@@ -926,16 +922,20 @@ marker="diameter_app#peer_up">peer_up/3</seealso> callback
after which the caller can exchange Diameter messages with the peer over
the transport.
In addition to CER/CEA, the service takes responsibility for the
-handling of DWR/DWA and required by RFC 3539 as well as for DPR/DPA.</p>
+handling of DWR/DWA and required by RFC 3539, as well as for DPR/DPA.</p>
<p>
The returned reference uniquely identifies the transport within the
scope of the service.
Note that the function returns before a transport connection has been
-established.
+established.</p>
+
+<note>
+<p>
It is not an error to add a transport to a service that has not yet
been configured: a service can be started after configuring
-transports.</p>
+its transports.</p>
+</note>
<marker id="call"/>
</desc>
@@ -944,48 +944,47 @@ transports.</p>
<!-- ===================================================================== -->
<func>
-<name>call(SvcName, App, Request, Options) -> ok | Answer | {error, Reason}</name>
+<name>call(SvcName, App, Request, [Opt]) -> Answer | ok | {error, Reason}</name>
<fsummary>Send a Diameter request message.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>App = application_alias()</v>
-<v>Request = diameter_app:message() | term()</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
+<v>App = <seealso marker="#application_alias">application_alias()</seealso></v>
+<v>Request = <seealso marker="diameter_app#message">diameter_app:message()</seealso></v>
<v>Answer = term()</v>
-<v>Options = [call_opt()]</v>
+<v>Opt = <seealso marker="#call_opt">call_opt()</seealso></v>
</type>
<desc>
<p>
-Send a Diameter request message and possibly return the answer or error.</p>
+Send a Diameter request message.</p>
<p>
-<c>App</c> identifies the Diameter application in which the request is
+<c>App</c> specifies the Diameter application in which the request is
defined and callbacks to the corresponding callback module
will follow as described below and in <seealso
marker="diameter_app">diameter_app(3)</seealso>.
-Unless the <c>detach</c> option has been specified to cause an earlier
-return, the call returns either when an answer message is received
-from the peer or an error occurs.
-In the case of an answer, the return value is as returned by a
+Unless the <c>detach</c> option is specified, the call returns either
+when an answer message is received from the peer or an error occurs.
+In the answer case, the return value is as returned by a
<seealso
marker="diameter_app#handle_answer">handle_answer/4</seealso>
callback.
-In the case of an error, whether or not the error is returned directly
+In the error case, whether or not the error is returned directly
by diameter or from a <seealso
marker="diameter_app#handle_error">handle_error/4</seealso>
callback depends on whether or not the outgoing request is
-successfully encoded for transmission from the peer, the cases being
+successfully encoded for transmission to the peer, the cases being
documented below.</p>
<p>
If there are no suitable peers, or if
<seealso marker="diameter_app#pick_peer">pick_peer/4</seealso>
-rejects them by returning 'false', then <c>{error, no_connection}</c>
+rejects them by returning <c>false</c>, then <c>{error,no_connection}</c>
is returned.
Otherwise <seealso marker="diameter_app#pick_peer">pick_peer/4</seealso>
is followed by a
<seealso
marker="diameter_app#prepare_request">prepare_request/3</seealso>
-callback, the message is encoded and sent.</p>
+callback, the message is encoded and then sent.</p>
<p>
There are several error cases which may prevent an
@@ -998,7 +997,7 @@ callback:</p>
<item>
<p>
If the initial encode of the outgoing request
-fails, then the request process fails and <c>{error, encode}</c>
+fails, then the request process fails and <c>{error,encode}</c>
is returned.</p>
</item>
@@ -1015,7 +1014,7 @@ callback takes place with <c>Reason = timeout</c>.</p>
If the request is successfully encoded and sent but the service in
question is stopped before an answer is received then a
<seealso marker="diameter_app#handle_error">handle_error/4</seealso>
-callback takes place <c>Reason = cancel</c>.</p>
+callback takes place with <c>Reason = cancel</c>.</p>
</item>
<item>
@@ -1042,7 +1041,7 @@ callback.</p>
<p>
If an encode error takes place during
retransmission then the request process fails and
-<c>{error, failure}</c> is returned.</p>
+<c>{error,failure}</c> is returned.</p>
</item>
<item>
@@ -1050,7 +1049,7 @@ retransmission then the request process fails and
If an application callback made in processing the request fails
(pick_peer, prepare_request, prepare_retransmit,
handle_answer or handle_error) then either
-<c>{error, encode}</c> or <c>{error, failure}</c>
+<c>{error,encode}</c> or <c>{error,failure}</c>
is returned depending on whether or not there has been an
attempt to send the request over the transport.</p>
</item>
@@ -1058,9 +1057,9 @@ attempt to send the request over the transport.</p>
</list>
<p>
-Note that <c>{error, encode}</c> is the only return value which
+Note that <c>{error,encode}</c> is the only return value which
guarantees that the request has <em>not</em> been sent over the
-transport.</p>
+transport connection.</p>
<marker id="origin_state_id"/>
</desc>
@@ -1069,7 +1068,7 @@ transport.</p>
<!-- ===================================================================== -->
<func>
-<name>origin_state_id() -> Unsigned32()</name>
+<name>origin_state_id() -> <seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso></name>
<fsummary>Returns a reasonable Origin-State-Id.</fsummary>
<desc>
<p>
@@ -1078,7 +1077,7 @@ outgoing messages.</p>
<p>
The value returned is the number of seconds since 19680120T031408Z,
-the first value that can be encoded as a Diameter Time(),
+the first value that can be encoded as a Diameter <c><seealso marker="diameter_dict#DATA_TYPES">Time()</seealso></c>,
at the time the diameter application was started.</p>
<marker id="remove_transport"/>
@@ -1091,11 +1090,11 @@ at the time the diameter application was started.</p>
<name>remove_transport(SvcName, Pred) -> ok</name>
<fsummary>Remove previously added transports.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Pred = Fun | MFA | ref() | list() | true | false</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
+<v>Pred = Fun | MFA | <seealso marker="#transport_ref">transport_ref()</seealso> | list() | true | false</v>
<v></v>
-<v>Fun = fun((reference(), connect|listen, list()) -> boolean())</v>
-<v>&nbsp;&nbsp;&nbsp; | fun((reference(), list()) -> boolean())</v>
+<v>Fun = fun((<seealso marker="#transport_ref">transport_ref()</seealso>, connect|listen, list()) -> boolean())</v>
+<v>&nbsp;&nbsp;&nbsp; | fun((<seealso marker="#transport_ref">transport_ref()</seealso>, list()) -> boolean())</v>
<v>&nbsp;&nbsp;&nbsp; | fun((list()) -> boolean())</v>
<v>MFA = {atom(), atom(), list()}</v>
</type>
@@ -1109,23 +1108,23 @@ An arity-3-valued <c>Pred</c> removes all transports for which
<c>Pred(Ref, Type, Opts)</c> returns <c>true</c>, where <c>Type</c> and
<c>Opts</c> are as passed to <seealso
marker="#add_transport">add_transport/2</seealso> and <c>Ref</c> is
-as returned by the corresponding call.
+as returned by it.
The remaining forms are equivalent to an arity-3 fun as follows.</p>
<code>
-Pred = fun(reference(), list()): fun(Ref, _, Opts) -> Pred(Ref, Opts) end
-Pred = fun(list()): fun(_, _, Opts) -> Pred(Opts) end
-Pred = reference(): fun(Ref, _, _) -> Pred == Ref end
-Pred = list(): fun(_, _, Opts) -> [] == Pred -- Opts end
-Pred = true: fun(_, _, _) -> true end
-Pred = false: fun(_, _, _) -> false end
+Pred = fun(transport_ref(), list()): fun(Ref, _, Opts) -> Pred(Ref, Opts) end
+Pred = fun(list()): fun(_, _, Opts) -> Pred(Opts) end
+Pred = transport_ref(): fun(Ref, _, _) -> Pred == Ref end
+Pred = list(): fun(_, _, Opts) -> [] == Pred -- Opts end
+Pred = true: fun(_, _, _) -> true end
+Pred = false: fun(_, _, _) -> false end
Pred = {M,F,A}: fun(Ref, Type, Opts) -> apply(M, F, [Ref, Type, Opts | A]) end
</code>
<p>
Removing a transport causes all associated transport connections to
be broken.
-A base application DPR message with
+A DPR message with
Disconnect-Cause <c>DO_NOT_WANT_TO_TALK_TO_YOU</c> will be sent
to each connected peer before disassociating the transport configuration
from the service and terminating the transport upon reception of
@@ -1140,15 +1139,293 @@ DPA or timeout.</p>
<!-- ===================================================================== -->
<func>
-<name>service_info(SvcName, Item) -> Value</name>
-<fsummary>Return specific information about a started service.</fsummary>
+<name>service_info(SvcName, Info) -> term()</name>
+<fsummary>Return information about a started service.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Value = term()</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
+<v>Info = Item | [Info]</v>
+<v>Item = atom()</v>
</type>
<desc>
<p>
-Return information about a started service.</p>
+Return information about a started service.
+<c>Item</c> can be one of the following.</p>
+
+<taglist>
+
+<tag><c>'Origin-Host'</c></tag>
+<tag><c>'Origin-Realm'</c></tag>
+<tag><c>'Vendor-Id'</c></tag>
+<tag><c>'Product-Name'</c></tag>
+<tag><c>'Origin-State-Id'</c></tag>
+<tag><c>'Host-IP-Address'</c></tag>
+<tag><c>'Supported-Vendor'</c></tag>
+<tag><c>'Auth-Application-Id'</c></tag>
+<tag><c>'Inband-Security-Id'</c></tag>
+<tag><c>'Acct-Application-Id'</c></tag>
+<tag><c>'Vendor-Specific-Application-Id'</c></tag>
+<tag><c>'Firmware-Revision'</c></tag>
+<item>
+<p>
+Return a capability value as configured with <seealso
+marker="#start_service">start_service/2</seealso>.</p>
+</item>
+
+<tag><c>applications</c></tag>
+<item>
+<p>
+Return the list of applications as configured with <seealso
+marker="#start_service">start_service/2</seealso>.
+</p>
+</item>
+
+<tag><c>capabilities</c></tag>
+<item>
+<p>
+Return a tagged list of all capabilities values as configured with
+<seealso
+marker="#start_service">start_service/2</seealso>.</p>
+</item>
+
+<tag><c>transport</c></tag>
+<item>
+<p>
+Return a list containing one entry for each of the service's transport
+as configured with <seealso
+marker="#add_transport">add_transport/2</seealso>.
+Each entry is a tagged list containing both configuration and
+information about established peer connections.
+An example return value with for a client service with Origin-Host
+"client.example.com" configured with a single transport connected to
+"server.example.com" might look as follows.</p>
+
+<code>
+[[{ref,#Ref&lt;0.0.0.93>},
+ {type,connect},
+ {options,[{transport_module,diameter_tcp},
+ {transport_config,[{ip,{127,0,0,1}},
+ {raddr,{127,0,0,1}},
+ {rport,3868},
+ {reuseaddr,true}]}]},
+ {watchdog,{&lt;0.66.0>,{1346,171491,996448},okay}},
+ {peer,{&lt;0.67.0>,{1346,171491,999906}}},
+ {apps,[{0,common}]},
+ {caps,[{origin_host,{"client.example.com","server.example.com"}},
+ {origin_realm,{"example.com","example.com"}},
+ {host_ip_address,{[{127,0,0,1}],[{127,0,0,1}]}},
+ {vendor_id,{0,193}},
+ {product_name,{"Client","Server"}},
+ {origin_state_id,{[],[]}},
+ {supported_vendor_id,{[],[]}},
+ {auth_application_id,{[0],[0]}},
+ {inband_security_id,{[],[0]}},
+ {acct_application_id,{[],[]}},
+ {vendor_specific_application_id,{[],[]}},
+ {firmware_revision,{[],[]}},
+ {avp,{[],[]}}]},
+ {port,[{owner,&lt;0.69.0>},
+ {module,diameter_tcp},
+ {socket,{{127,0,0,1},48758}},
+ {peer,{{127,0,0,1},3868}},
+ {statistics,[{recv_oct,656},
+ {recv_cnt,6},
+ {recv_max,148},
+ {recv_avg,109},
+ {recv_dvi,19},
+ {send_oct,836},
+ {send_cnt,6},
+ {send_max,184},
+ {send_avg,139},
+ {send_pend,0}]}]},
+ {statistics,[{{{0,258,0},recv},3},
+ {{{0,258,1},send},3},
+ {{{0,257,0},recv},1},
+ {{{0,257,1},send},1},
+ {{{0,258,0},recv,{'Result-Code',2001}},3},
+ {{{0,280,1},recv},2},
+ {{{0,280,0},send},2}]}]]
+</code>
+
+<p>
+Here <c>ref</c> is a <c><seealso
+marker="#transport_ref">transport_ref()</seealso></c> and <c>options</c>
+the corresponding <c><seealso
+marker="#transport_opt">transport_opt()</seealso></c> list passed to <seealso
+marker="#add_transport">add_transport/2</seealso>.
+The <c>watchdog</c> entry shows the state of a connection's RFC 3539 watchdog
+state machine.
+The <c>peer</c> entry identifies the <c><seealso
+marker="diameter_app#peer_ref">diameter_app:peer_ref()</seealso></c> for
+which there will have been <seealso
+marker="diameter_app#peer_up">peer_up</seealso> callbacks for the
+Diameter applications identified by the <c>apps</c> entry,
+<c>common</c> being the <c><seealso
+marker="#application_alias">application_alias()</seealso></c>.
+The <c>caps</c> entry identifies the capabilities sent by the local
+node and received from the peer during capabilities exchange.
+The <c>port</c> entry displays socket-level information about the
+transport connection.
+The <c>statistics</c> entry presents Diameter-level counters,
+an entry like <c>{{{0,280,1},recv},2}</c> saying that the client has
+received 2 DWR messages: <c>{0,280,1} = {Application_Id, Command_Code,
+R_Flag}</c>.</p>
+
+<p>
+Note that <c>watchdog</c>, <c>peer</c>, <c>apps</c>, <c>caps</c>
+and <c>port</c> entries depend on connectivity
+with the peer and may not be present.
+Note also that the <c>statistics</c> entry presents values acuumulated
+during the lifetime of the transport configuration.</p>
+
+<p>
+A listening transport presents its information slightly differently
+since there may be multiple accepted connections for the same <c><seealso
+marker="#transport_ref">transport_ref()</seealso></c>.
+The <c>transport</c> info returned by a server with a single client
+connection might look as follows.</p>
+
+<code>
+[[{ref,#Ref&lt;0.0.0.61>},
+ {type,listen},
+ {options,[{transport_module,diameter_tcp},
+ {transport_config,[{reuseaddr,true},
+ {ip,{127,0,0,1}},
+ {port,3868}]}]},
+ {accept,[[{watchdog,{&lt;0.56.0>,{1346,171481,226895},okay}},
+ {peer,{&lt;0.58.0>,{1346,171491,999511}}},
+ {apps,[{0,common}]},
+ {caps,[{origin_host,{"server.example.com","client.example.com"}},
+ {origin_realm,{"example.com","example.com"}},
+ {host_ip_address,{[{127,0,0,1}],[{127,0,0,1}]}},
+ {vendor_id,{193,0}},
+ {product_name,{"Server","Client"}},
+ {origin_state_id,{[],[]}},
+ {supported_vendor_id,{[],[]}},
+ {auth_application_id,{[0],[0]}},
+ {inband_security_id,{[],[]}},
+ {acct_application_id,{[],[]}},
+ {vendor_specific_application_id,{[],[]}},
+ {firmware_revision,{[],[]}},
+ {avp,{[],[]}}]},
+ {port,[{owner,&lt;0.62.0>},
+ {module,diameter_tcp},
+ {socket,{{127,0,0,1},3868}},
+ {peer,{{127,0,0,1},48758}},
+ {statistics,[{recv_oct,1576},
+ {recv_cnt,16},
+ {recv_max,184},
+ {recv_avg,98},
+ {recv_dvi,26},
+ {send_oct,1396},
+ {send_cnt,16},
+ {send_max,148},
+ {send_avg,87},
+ {send_pend,0}]}]}],
+ [{watchdog,{&lt;0.72.0>,{1346,171491,998404},initial}}]]},
+ {statistics,[{{{0,280,0},recv},7},
+ {{{0,280,1},send},7},
+ {{{0,258,0},send,{'Result-Code',2001}},3},
+ {{{0,258,1},recv},3},
+ {{{0,258,0},send},3},
+ {{{0,280,1},recv},5},
+ {{{0,280,0},send},5},
+ {{{0,257,1},recv},1},
+ {{{0,257,0},send},1}]}]]
+</code>
+
+<p>
+The information presented here is as in the <c>connect</c> case except
+that the client connections are grouped under an <c>accept</c> tuple.</p>
+
+</item>
+
+<tag><c>connections</c></tag>
+<item>
+<p>
+Return a list containing one entry for every established transport
+connection whose watchdog state machine is not in the <c>down</c>
+state.
+This is a flat view of <c>transport</c> info which lists only active
+connections and for which Diameter-level statistics are accumulated
+only for the lifetime of the transport connection.
+A return value for the server above might look as follows.</p>
+
+<code>
+[[{ref,#Ref&lt;0.0.0.61>},
+ {type,accept},
+ {options,[{transport_module,diameter_tcp},
+ {transport_config,[{reuseaddr,true},
+ {ip,{127,0,0,1}},
+ {port,3868}]}]},
+ {watchdog,{&lt;0.56.0>,{1346,171481,226895},okay}},
+ {peer,{&lt;0.58.0>,{1346,171491,999511}}},
+ {apps,[{0,common}]},
+ {caps,[{origin_host,{"server.example.com","client.example.com"}},
+ {origin_realm,{"example.com","example.com"}},
+ {host_ip_address,{[{127,0,0,1}],[{127,0,0,1}]}},
+ {vendor_id,{193,0}},
+ {product_name,{"Server","Client"}},
+ {origin_state_id,{[],[]}},
+ {supported_vendor_id,{[],[]}},
+ {auth_application_id,{[0],[0]}},
+ {inband_security_id,{[],[]}},
+ {acct_application_id,{[],[]}},
+ {vendor_specific_application_id,{[],[]}},
+ {firmware_revision,{[],[]}},
+ {avp,{[],[]}}]},
+ {port,[{owner,&lt;0.62.0>},
+ {module,diameter_tcp},
+ {socket,{{127,0,0,1},3868}},
+ {peer,{{127,0,0,1},48758}},
+ {statistics,[{recv_oct,10124},
+ {recv_cnt,132},
+ {recv_max,184},
+ {recv_avg,76},
+ {recv_dvi,9},
+ {send_oct,10016},
+ {send_cnt,132},
+ {send_max,148},
+ {send_avg,75},
+ {send_pend,0}]}]},
+ {statistics,[{{{0,280,0},recv},62},
+ {{{0,280,1},send},62},
+ {{{0,258,0},send,{'Result-Code',2001}},3},
+ {{{0,258,1},recv},3},
+ {{{0,258,0},send},3},
+ {{{0,280,1},recv},66},
+ {{{0,280,0},send},66},
+ {{{0,257,1},recv},1},
+ {{{0,257,0},send},1}]}]]
+</code>
+
+<p>
+Note that there may be multiple entries with the same <c>ref</c>, in
+contrast to <c>transport</c> info.</p>
+</item>
+
+<tag><c>statistics</c></tag>
+<item>
+<p>
+Return a <c>{{Counter, Ref}, non_neg_integer()}</c> list of counter values.
+<c>Ref</c> can be either a <c><seealso
+marker="#transport_ref">transport_ref()</seealso></c>
+or a <c><seealso
+marker="diameter_app#peer_ref">diameter_app:peer_ref()</seealso></c>.
+Entries for the latter are folded into corresponding entries for the
+former as peer connections go down.
+Entries for both are removed at <seealso
+marker="#remove_transport">remove_transport/2</seealso>.
+The Diameter-level statistics returned by <c>transport</c> and
+<c>connections</c> info are based upon these entries.</p>
+</item>
+
+</taglist>
+
+<p>
+Requesting info for an unknown service causes <c>undefined</c> to be
+returned.
+Requesting a list of items causes a tagged list to be
+returned.</p>
<marker id="services"/>
</desc>
@@ -1160,7 +1437,7 @@ Return information about a started service.</p>
<name>services() -> [SvcName]</name>
<fsummary>Return the list of started services.</fsummary>
<type>
-<v>SvcName = service_name()</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
</type>
<desc>
<p>
@@ -1173,10 +1450,10 @@ Return the list of started services.</p>
<!-- ===================================================================== -->
<func>
-<name>session_id(Ident) -> OctetString()</name>
+<name>session_id(Ident) -> <seealso marker="diameter_dict#DATA_TYPES">OctetString()</seealso></name>
<fsummary>Return a value for a Session-Id AVP.</fsummary>
<type>
-<v>Ident = DiameterIdentity()</v>
+<v>Ident = <seealso marker="diameter_dict#DATA_TYPES">DiameterIdentity()</seealso></v>
</type>
<desc>
<p>
@@ -1201,7 +1478,7 @@ Start the diameter application.</p>
<p>
The diameter application must be started before starting a service.
-In a production system this will typically be accomplished by a boot
+In a production system this is typically accomplished by a boot
file, not by calling <c>start/0</c> explicitly.</p>
<marker id="start_service"/>
@@ -1213,8 +1490,8 @@ file, not by calling <c>start/0</c> explicitly.</p>
<name>start_service(SvcName, Options) -> ok | {error, Reason}</name>
<fsummary>Start a Diameter service.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Options = [service_opt()]</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
+<v>Options = [<seealso marker="#service_opt">service_opt()</seealso>]</v>
<v>Reason = term()</v>
</type>
<desc>
@@ -1222,11 +1499,19 @@ file, not by calling <c>start/0</c> explicitly.</p>
Start a diameter service.</p>
<p>
-A service defines a locally-implemented Diameter peer, specifying the
-capabilities of the peer to be used during capabilities exchange and
-the Diameter applications that it supports.
+A service defines a locally-implemented Diameter node, specifying the
+capabilities to be advertised during capabilities exchange.
Transports are added to a service using <seealso
-marker="#add_transport">add_transport/2</seealso>.</p>
+marker="#add_transport">add_transport/2</seealso>.
+</p>
+
+<note>
+<p>
+A transport can both override its service's
+capabilities and restrict its supported Diameter applications so
+"service = Diameter node as identified by Origin-Host" is not
+necessarily the case.</p>
+</note>
<marker id="stop_service"/>
</desc>
@@ -1252,13 +1537,26 @@ Stop the diameter application.</p>
<name>stop_service(SvcName) -> ok | {error, Reason}</name>
<fsummary>Stop a Diameter service.</fsummary>
<type>
-<v>SvcName = service_name()</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
<v>Reason = term()</v>
</type>
<desc>
<p>
Stop a diameter service.</p>
+<p>
+Stopping a service causes all associated transport connections to be
+broken.
+A DPR message with be sent as in the case of <seealso
+marker="#remove_transport">remove_transport/2</seealso>.</p>
+
+<note>
+<p>
+Stopping a transport does not remove any associated transports:
+<seealso marker="#remove_transport">remove_transport/2</seealso> must
+be called to remove transport configuration.</p>
+</note>
+
<marker id="subscribe"/>
</desc>
</func>
@@ -1269,15 +1567,19 @@ Stop a diameter service.</p>
<name>subscribe(SvcName) -> true</name>
<fsummary>Subscribe to event messages.</fsummary>
<type>
-<v>SvcName = service_name()</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
</type>
<desc>
<p>
-Subscribe to <c>service_event()</c> messages from a service.</p>
+Subscribe to <c><seealso
+marker="#service_event">service_event()</seealso></c> messages from
+a service.</p>
<p>
It is not an error to subscribe to events from a service
-that does not yet exist.</p>
+that does not yet exist.
+Doing so before adding transports is required to guarantee the
+reception of all related events.</p>
<marker id="unsubscribe"/>
</desc>
@@ -1289,7 +1591,7 @@ that does not yet exist.</p>
<name>unsubscribe(SvcName) -> true</name>
<fsummary>Unsubscribe to event messages.</fsummary>
<type>
-<v>SvcName = service_name()</v>
+<v>SvcName = <seealso marker="#service_name">service_name()</seealso></v>
</type>
<desc>
<p>
diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml
index a9ae0ebbec..4a4b212787 100644
--- a/lib/diameter/doc/src/diameter_app.xml
+++ b/lib/diameter/doc/src/diameter_app.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -45,8 +45,8 @@ A diameter service as started by <seealso
marker="diameter#start_service">diameter:start_service/2</seealso>
configures one of more Diameter applications, each of whose
configuration specifies a callback that handles messages specific to
-its application.
-The messages and AVPs of the Diameter application are defined in a
+the application.
+The messages and AVPs of the application are defined in a
dictionary file whose format is documented in
<seealso marker="diameter_dict">diameter_dict(4)</seealso>
while the callback module is documented here.
@@ -106,11 +106,13 @@ and, for the call-specific callbacks, any extra arguments passed to
<taglist>
+<marker id="capabilities"/>
+
<tag><c>capabilities() = #diameter_caps{}</c></tag>
<item>
<p>
A record containing the identities of
-the local and remote Diameter peers having an established transport
+the local Diameter node and the remote Diameter peer having an established transport
connection, as well as the capabilities as
determined by capabilities exchange.
Each field of the record is a 2-tuple consisting of
@@ -119,6 +121,8 @@ Optional or possibly multiple values are encoded as lists of values,
mandatory values as the bare value.</p>
</item>
+<marker id="message"/>
+
<tag><c>message() = record() | list()</c></tag>
<item>
<p>
@@ -136,8 +140,8 @@ list of <c>{FieldName, FieldValue}</c> pairs.</p>
<p>
A third representation allows a message to be specified as a list
-whose head is a <c>diameter_header</c> record and whose tail is a list
-of <c>diameter_avp</c> records.
+whose head is a <c>#diameter_header{}</c> record and whose tail is a list
+of <c>#diameter_avp{}</c> records.
This representation is used by diameter itself when relaying requests
as directed by the return value of a
<seealso marker="#handle_request">handle_request/3</seealso>
@@ -149,15 +153,18 @@ are sent exactly as specified.</p>
</item>
+<marker id="packet"/>
+
<tag><c>packet() = #diameter_packet{}</c></tag>
<item>
<p>
-A container for incoming and outgoing Diameters message that's passed
+A container for incoming and outgoing Diameter messages that's passed
through encode/decode and transport.
-Fields of a packet() record should not be set in return values except
-as documented.</p>
+Fields should not be set in return values except as documented.</p>
</item>
+<marker id="peer_ref"/>
+
<tag><c>peer_ref() = term()</c></tag>
<item>
<p>
@@ -165,20 +172,15 @@ A term identifying a transport connection with a Diameter peer.
Should be treated opaquely.</p>
</item>
-<tag><c>peer() = {peer_ref(), capabilities()}</c></tag>
+<marker id="peer"/>
+
+<tag><c>peer() = {<seealso marker="#peer_ref">peer_ref()</seealso>, <seealso marker="#capabilities">capabilities()</seealso>}</c></tag>
<item>
<p>
A tuple representing a Diameter peer connection.</p>
</item>
-<tag><c>service_name() = term()</c></tag>
-<item>
-<p>
-The service supporting the Diameter application.
-Specified to <seealso
-marker="diameter#start_service">diameter:start_service/2</seealso>
-when starting the service.</p>
-</item>
+<marker id="state"/>
<tag><c>state() = term()</c></tag>
<item>
@@ -211,9 +213,9 @@ process.</p>
<name>Mod:peer_up(SvcName, Peer, State) -> NewState</name>
<fsummary>Invoked when a transport connection has been established</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
-<v>State = NewState = state()</v>
+<v>SvcName = <seealso marker="diameter#service_name">diameter:service_name()</seealso></v>
+<v>Peer = <seealso marker="#peer">peer()</seealso></v>
+<v>State = NewState = <seealso marker="#state">state()</seealso></v>
</type>
<desc>
<p>
@@ -230,9 +232,9 @@ the callback module in question has been configured.</p>
<name>Mod:peer_down(SvcName, Peer, State) -> NewState</name>
<fsummary>Invoked when a transport connection has been lost.</fsummary>
<type>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
-<v>State = NewState = state()</v>
+<v>SvcName = <seealso marker="diameter#service_name">diameter:service_name()</seealso></v>
+<v>Peer = <seealso marker="#peer">peer()</seealso></v>
+<v>State = NewState = <seealso marker="#state">state()</seealso></v>
</type>
<desc>
<p>
@@ -248,21 +250,17 @@ call to <seealso marker="#peer_up">peer_up/3</seealso>.</p>
-> {ok, Peer} | {Peer, NewState} | false</name>
<fsummary>Select a target peer for an outgoing request.</fsummary>
<type>
-<v>Candidates = [peer()]</v>
-<v>Peer = peer() | false</v>
-<v>SvcName = service_name()</v>
-<v>State = NewState = state()</v>
+<v>Candidates = [<seealso marker="#peer">peer()</seealso>]</v>
+<v>Peer = <seealso marker="#peer">peer()</seealso> | false</v>
+<v>SvcName = <seealso marker="diameter#service_name">diameter:service_name()</seealso></v>
+<v>State = NewState = <seealso marker="#state">state()</seealso></v>
</type>
<desc>
<p>
Invoked as a consequence of a call to <seealso
marker="diameter#call">diameter:call/4</seealso> to select a destination
-peer for an outgoing request, the return value indicating the selected peer.
-A new application state can also be returned but only if the Diameter
-application in question was
-configured with the option <c>call_mutates_state</c> set to
-<c>true</c>, as documented for <seealso
-marker="diameter#start_service">diameter:start_service/2</seealso>.</p>
+peer for an outgoing request, the return value indicating the selected
+peer.</p>
<p>
The candidate peers list will only include those
@@ -282,7 +280,8 @@ equivalent when callback state is mutable, as are
<c>{ok, Peer}</c> and <c>{Peer, State}</c>.
Returning a peer as <c>false</c> causes <c>{error, no_connection}</c>
to be returned from <seealso marker="diameter#call">diameter:call/4</seealso>.
-Returning a peer() from an initial pick_peer/4 callback will result in a
+Returning a <seealso marker="#peer">peer()</seealso> from an initial
+pick_peer/4 callback will result in a
<seealso marker="#prepare_request">prepare_request/3</seealso> callback
followed by either <seealso
marker="#handle_answer">handle_answer/4</seealso>
@@ -301,6 +300,19 @@ marker="#pick_peer">pick_peer/4</seealso> will be, since a
retransmission to an alternate peer is abandoned if an answer is
received from a previously selected peer.</p>
+<note>
+<p>
+<c>{Peer, NewState}</c> and its equivalents can only be returned if
+the Diameter application in question was
+configured with the <seealso
+marker="diameter#application_opt">diameter:application_opt()</seealso>
+<c>{call_mutates_state, true}</c>.
+Otherwise, the <c>State</c> argument is always
+the intial value as configured on the application, not any subsequent
+value returned by a <seealso marker="#peer_up">peer_up/3</seealso>
+or <seealso marker="#peer_down">peer_down/3</seealso> callback.</p>
+</note>
+
<marker id="prepare_request"/>
</desc>
@@ -310,10 +322,10 @@ received from a previously selected peer.</p>
<name>Mod:prepare_request(Packet, SvcName, Peer) -> Action</name>
<fsummary>Return a request for encoding and transport.</fsummary>
<type>
-<v>Packet = packet()</v>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
-<v>Action = {send, packet() | message()} | {discard, Reason} | discard</v>
+<v>Packet = <seealso marker="#packet">packet()</seealso></v>
+<v>SvcName = <seealso marker="diameter#service_name">diameter:service_name()</seealso></v>
+<v>Peer = <seealso marker="#peer">peer()</seealso></v>
+<v>Action = {send, <seealso marker="#packet">packet()</seealso> | <seealso marker="#message">message()</seealso>} | {discard, Reason} | discard</v>
</type>
<desc>
<p>
@@ -325,14 +337,14 @@ callback need not be limited to this usage.
Many implementations may simply want to return <c>{send, Packet}</c></p>
<p>
-A returned packet() should set the request to be encoded in its
+A returned <seealso marker="#packet">packet()</seealso> should set the request to be encoded in its
<c>msg</c> field and can set the <c>transport_data</c> field in order
to pass information to the transport module.
Extra arguments passed to <seealso
marker="diameter#call">diameter:call/4</seealso> can be used to
communicate transport data to the callback.
-A returned packet() can also set the <c>header</c> field to a
-<c>diameter_header</c> record in order to specify values that should
+A returned <seealso marker="#packet">packet()</seealso> can also set the <c>header</c> field to a
+<c>#diameter_header{}</c> record in order to specify values that should
be preserved in the outgoing request, although this should typically
not be necessary and allows the callback to set header values
inappropriately.
@@ -355,10 +367,10 @@ discarded}</c>.</p>
<name>Mod:prepare_retransmit(Packet, SvcName, Peer) -> Result</name>
<fsummary>Return a request for encoding and retransmission.</fsummary>
<type>
-<v>Packet = packet()</v>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
-<v>Result = {send, packet() | message()} | {discard, Reason} | discard</v>
+<v>Packet = <seealso marker="#packet">packet()</seealso></v>
+<v>SvcName = <seealso marker="diameter#service_name">diameter:service_name()</seealso></v>
+<v>Peer = <seealso marker="#peer">peer()</seealso></v>
+<v>Result = {send, <seealso marker="#packet">packet()</seealso> | <seealso marker="#message">message()</seealso>} | {discard, Reason} | discard</v>
</type>
<desc>
<p>
@@ -366,7 +378,7 @@ Invoked to return a request for encoding and retransmission.
Has the same role as <seealso
marker="#prepare_request">prepare_request/3</seealso> in the case that
a peer connection is lost an an alternate peer selected but the
-argument packet() is as returned by the initial
+argument <seealso marker="#packet">packet()</seealso> is as returned by the initial
<c>prepare_request/3</c>.</p>
<p>
@@ -385,10 +397,10 @@ discarded}</c>.</p>
<name>Mod:handle_answer(Packet, Request, SvcName, Peer) -> Result</name>
<fsummary>Receive an answer message from a peer.</fsummary>
<type>
-<v>Packet = packet()</v>
-<v>Request = message()</v>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
+<v>Packet = <seealso marker="#packet">packet()</seealso></v>
+<v>Request = <seealso marker="#message">message()</seealso></v>
+<v>SvcName = <seealso marker="diameter#service_name">diameter:service_name()</seealso></v>
+<v>Peer = <seealso marker="#peer">peer()</seealso></v>
<v>Result = term()</v>
</type>
<desc>
@@ -401,7 +413,7 @@ specified.</p>
<p>
The decoded answer record is in the <c>msg</c> field of the argument
-packet(),
+<seealso marker="#packet">packet()</seealso>,
the undecoded binary in the <c>packet</c> field.
<c>Request</c> is the outgoing request message as was returned from
<seealso marker="#prepare_request">prepare_request/3</seealso> or
@@ -438,9 +450,9 @@ marker="diameter#start_service">diameter:start_service/2</seealso>.</p>
<fsummary>Return an error from a outgoing request.</fsummary>
<type>
<v>Reason = timeout | failover | term()</v>
-<v>Request = message()</v>
-<v>SvcName = service_name()</v>
-<v>Peer = peer()</v>
+<v>Request = <seealso marker="#message">message()</seealso></v>
+<v>SvcName = <seealso marker="diameter#service_name">diameter:service_name()</seealso></v>
+<v>Peer = <seealso marker="#peer">peer()</seealso></v>
<v>Result = term()</v>
</type>
<desc>
@@ -469,14 +481,14 @@ callback returned false.</p>
<name>Mod:handle_request(Packet, SvcName, Peer) -> Action</name>
<fsummary>Receive an incoming request.</fsummary>
<type>
-<v>Packet = packet()</v>
+<v>Packet = <seealso marker="#packet">packet()</seealso></v>
<v>SvcName = term()</v>
-<v>Peer = peer()</v>
-<v>Action = Reply | {relay, Opts} | discard | {eval, Action, PostF}</v>
-<v>Reply = {reply, message()}
+<v>Peer = <seealso marker="#peer">peer()</seealso></v>
+<v>Action = Reply | {relay, [Opt]} | discard | {eval, Action, PostF}</v>
+<v>Reply = {reply, <seealso marker="#message">message()</seealso>}
| {protocol_error, 3000..3999}</v>
-<v>Opts = diameter:call_opts()</v>
-<v>PostF = diameter:evaluable()</v>
+<v>Opt = <seealso marker="diameter#call_opt">diameter:call_opt()</seealso></v>
+<v>PostF = <seealso marker="diameter#evaluable">diameter:evaluable()</seealso></v>
</type>
<desc>
<p>
@@ -492,13 +504,13 @@ itself as defining either the application in question or the Relay
application.</p>
<p>
-The argument packet() has the following signature.</p>
+The argument <seealso marker="#packet">packet()</seealso> has the following signature.</p>
<code>
#diameter_packet{header = #diameter_header{},
avps = [#diameter_avp{}],
msg = record() | undefined,
- errors = ['Unsigned32'() | {'Unsigned32'(), #diameter_avp{}}],
+ errors = [<seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso> | {<seealso marker="diameter_dict#DATA_TYPES">Unsigned32()</seealso>, #diameter_avp{}}],
bin = binary(),
transport_data = term()}
</code>
@@ -515,8 +527,8 @@ The <c>errors</c> field specifies any Result-Code's identifying errors
that were encountered in decoding the request.
In this case diameter will set both Result-Code and
Failed-AVP AVP's in a returned
-answer message() before sending it to the peer:
-the returned message() need only set any other required AVP's.
+answer <seealso marker="#message">message()</seealso> before sending it to the peer:
+the returned <seealso marker="#message">message()</seealso> need only set any other required AVP's.
Note that the errors detected by diameter are all of the 5xxx series
(Permanent Failures).
The <c>errors</c> list is empty if the request has been received in
@@ -526,7 +538,7 @@ the relay application.</p>
The <c>transport_data</c> field contains an arbitrary term passed into
diameter from the transport module in question, or the atom
<c>undefined</c> if the transport specified no data.
-The term is preserved in the packet() containing any answer message
+The term is preserved in the <seealso marker="#packet">packet()</seealso> containing any answer message
sent back to the transport process unless another value is explicitly
specified.</p>
@@ -535,7 +547,7 @@ The semantics of each of the possible return values are as follows.</p>
<taglist>
-<tag><c>{reply, message()}</c></tag>
+<tag><c>{reply, <seealso marker="#message">message()</seealso>}</c></tag>
<item>
<p>
Send the specified answer message to the peer.</p>
@@ -579,7 +591,7 @@ header of the relayed request.</p>
The returned <c>Opts</c> should not specify <c>detach</c>.
A subsequent <seealso marker="#handle_answer">handle_answer/4</seealso>
callback for the relayed request must return its first
-argument, the <c>diameter_packet</c> record containing the answer
+argument, the <c>#diameter_packet{}</c> record containing the answer
message.
Note that the <c>extra</c> option can be specified to supply arguments
that can distinguish the relay case from others if so desired.
diff --git a/lib/diameter/doc/src/diameter_compile.xml b/lib/diameter/doc/src/diameter_compile.xml
index 60e08d41da..7a6ca48798 100644
--- a/lib/diameter/doc/src/diameter_compile.xml
+++ b/lib/diameter/doc/src/diameter_compile.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,7 +31,7 @@ supplied.
<description>
<p>
-The diameterc utility is used to transform diameter
+The diameterc utility is used to compile diameter
<seealso marker="diameter_dict">dictionary files</seealso>
into Erlang source.
The resulting source implements the interface diameter requires
@@ -47,7 +47,7 @@ to encode and decode the dictionary's messages and AVP's.</p>
<tag><![CDATA[diameterc [<options>] <file>]]></tag>
<item>
<p>
-Transforms a single dictionary file. Valid options are as follows.</p>
+Compiles a single dictionary file. Valid options are as follows.</p>
<!-- Leave -h/d/v undocumented, except for the usage message from the
utility itself. -->
@@ -64,47 +64,36 @@ Defaults to the current working directory.</p>
<item>
<p>
Specifies a directory to add to the code path.
-Use to point at beam files corresponding to dictionaries
-inherited by the one being compiled using <c>@inherits</c> or
-<c>--inherits</c>.
-Inheritance is a beam dependency, not an erl/hrl dependency.</p>
+Use to point at beam files compiled from inherited dictionaries,
+<c>@inherits</c> in a dictionary file creating a beam dependency, not
+an erl/hrl dependency.</p>
<p>
Multiple <c>-i</c> options can be specified.</p>
</item>
<tag><![CDATA[-E]]></tag>
-<item>
-<p>
-Supresses erl generation.</p>
-</item>
-
<tag><![CDATA[-H]]></tag>
<item>
<p>
-Supresses hrl generation.</p>
+Supresses erl and hrl generation, respectively.</p>
</item>
<tag><![CDATA[--name <name>]]></tag>
-<item>
-<p>
-Set <c>@name</c> in the dictionary file.
-Overrides any setting in the file itself.</p>
-</item>
-
<tag><![CDATA[--prefix <prefix>]]></tag>
<item>
<p>
-Set <c>@prefix</c> in the dictionary file.
+Set <c>@name</c> and <c>@prefix</c> in the dictionary,
+respectively.
Overrides any setting in the file itself.</p>
</item>
<tag><![CDATA[--inherits <dict>]]></tag>
<item>
<p>
-Append an <c>@inherits</c> to the dictionary file.
-Specifying <c>'-'</c> as the dictionary has the effect of clearing
-any previous inherits, effectively ignoring previous inherits.</p>
+Append an <c>@inherits</c> to the dictionary before compiling.
+Specifying <c>'-'</c> as the dictionary has the effect of clearing any
+previous inherits, causing them to be ignored.</p>
<p>
Multiple <c>--inherits</c> options can be specified.</p>
@@ -130,16 +119,6 @@ Returns 0 on success, non-zero on failure.</p>
<!-- ===================================================================== -->
<section>
-<title>BUGS</title>
-
-<p>
-The identification of errors in the source file is poor.</p>
-
-</section>
-
-<!-- ===================================================================== -->
-
-<section>
<title>SEE ALSO</title>
<p>
diff --git a/lib/diameter/doc/src/diameter_dict.xml b/lib/diameter/doc/src/diameter_dict.xml
index cc638dbc18..98adebf145 100644
--- a/lib/diameter/doc/src/diameter_dict.xml
+++ b/lib/diameter/doc/src/diameter_dict.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -275,10 +275,12 @@ Location-Information 350 Grouped MV
Requested-Information 353 Enumerated V
</code>
+<warning>
<p>
-Note that the P flag has been deprecated by the Diameter Maintenance
-and Extensions Working Group of the IETF: diameter will set the P flag
-to 0 as mandated by the current draft standard.</p>
+The P flag has been deprecated by the Diameter Maintenance
+and Extensions Working Group of the IETF and should be omitted
+to conform to the current draft standard.</p>
+</warning>
</item>
diff --git a/lib/diameter/doc/src/diameter_examples.xml b/lib/diameter/doc/src/diameter_examples.xml
index 966d1f1eee..1fd7755695 100644
--- a/lib/diameter/doc/src/diameter_examples.xml
+++ b/lib/diameter/doc/src/diameter_examples.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
@@ -37,7 +37,6 @@ under the License.
<!-- ===================================================================== -->
<p>
-To be written.
Example code can be found in the diameter application's
<c>examples</c> subdirectory.</p>
diff --git a/lib/diameter/doc/src/diameter_intro.xml b/lib/diameter/doc/src/diameter_intro.xml
index ef08002a8b..bc2afbd453 100644
--- a/lib/diameter/doc/src/diameter_intro.xml
+++ b/lib/diameter/doc/src/diameter_intro.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
@@ -44,11 +44,11 @@ agent.
This chapter provides a short overview of the application.</p>
<p>
-A Diameter peer is implemented by configuring a <em>service</em> and
+A Diameter node is implemented by configuring a <em>service</em> and
one or more <em>transports</em> using the interface module
<seealso marker="diameter">diameter</seealso>.
The service configuration defines the Diameter applications to be
-supported by the peer and, typically, the capabilities that it should
+supported by the node and, typically, the capabilities that it should
send to remote peers at capabilities exchange upon the establishment
of transport connections.
A transport is configured on a service and provides protocol-specific
@@ -57,16 +57,17 @@ diameter and implemented by a transport module.
The diameter application provides two transport modules: <seealso
marker="diameter_tcp">diameter_tcp</seealso> and <seealso
marker="diameter_sctp">diameter_sctp</seealso> for transport over TCP
-(using <c>gen_tcp</c>) and SCTP (using <c>gen_sctp</c>) respectively.
+(using <seealso marker="kernel:gen_tcp">gen_tcp</seealso>) and SCTP
+(using <seealso marker="kernel:gen_sctp">gen_sctp</seealso>) respectively.
Other transports can be provided by any module that implements
diameter's <seealso marker="diameter_transport">transport
interface</seealso>.</p>
<p>
-While a service typically implements a single Diameter peer (as
+While a service typically implements a single Diameter node (as
identified by an Origin-Host AVP), transports can themselves be
-associated with capabilities AVP's so that a single service be used to
-implement more than one Diameter peer.</p>
+associated with capabilities AVP's so that a single service can be
+used to implement more than one Diameter node.</p>
<p>
Each Diameter application defined on a service is configured with a
@@ -76,19 +77,19 @@ diameter communicates the connectivity of remote peers, requests peer
selection for outgoing requests, and communicates the reception of
incoming Diameter request and answer messages.
An application using diameter implements these application callback
-modules to provide the functionality of the Diameter peer(s) it
+modules to provide the functionality of the Diameter node(s) it
implements.</p>
<p>
-Each Diameter application is also configured with one or more
-dictionary modules
+Each Diameter application is also configured with a
+dictionary module
that provide encode/decode functionality for outgoing/incoming
-Diameter messages.
-A module is generated from a <seealso
+Diameter messages belonging to the application.
+A dictionary module is generated from a <seealso
marker="diameter_dict">specification file</seealso> using the <seealso
marker="diameterc">diameterc</seealso> utility.
Dictionaries for the RFC 3588 Diameter Common Messages, Base
-Accounting and Relay applications are provided by the diameter
+Accounting and Relay applications are provided with the diameter
application.</p>
</chapter>
diff --git a/lib/diameter/doc/src/diameter_sctp.xml b/lib/diameter/doc/src/diameter_sctp.xml
index c1e839b8e1..955169349c 100644
--- a/lib/diameter/doc/src/diameter_sctp.xml
+++ b/lib/diameter/doc/src/diameter_sctp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -58,11 +58,11 @@ and implements the behaviour documented in
<fsummary>Start a transport process.</fsummary>
<type>
<v>Type = connect | accept</v>
-<v>Ref = reference()</v>
+<v>Ref = <seealso marker="diameter#transport_ref">diameter:transport_ref()</seealso></v>
<v>Svc = #diameter_service{}</v>
-<v>Opt = {raddr, ip_address()} | {rport, integer()} | term()</v>
+<v>Opt = {raddr, <seealso marker="kernel:inet#type-ip_address">inet:ip_address()</seealso>} | {rport, integer()} | term()</v>
<v>Pid = pid()</v>
-<v>LAddr = ip_address()</v>
+<v>LAddr = <seealso marker="kernel:inet#type-ip_address">inet:ip_address()</seealso></v>
<v>Reason = term()</v>
</type>
<desc>
@@ -75,12 +75,13 @@ marker="diameter_transport#start">diameter_transport(3)</seealso>.</p>
The only diameter_sctp-specific argument is the options list.
Options <c>raddr</c> and <c>rport</c> specify the remote address
and port for a connecting transport and not valid for a listening
-transport.
-The former is required while latter defaults to 3868 if unspecified.
+transport: the former is required while latter defaults to 3868 if
+unspecified.
More than one <c>raddr</c> option can be specified, in which case the
connecting transport in question attempts each in sequence until
an association is established.
-Remaining options are any accepted by gen_sctp:open/1, with the exception
+Remaining options are any accepted by <seealso
+marker="kernel:gen_sctp#open-1">gen_sctp:open/1</seealso>, with the exception
of options <c>mode</c>, <c>binary</c>, <c>list</c>, <c>active</c>
and <c>sctp_events</c>.
Note that options <c>ip</c> and <c>port</c> specify the local address
@@ -88,30 +89,45 @@ and port respectively.</p>
<p>
Multiple <c>ip</c> options can be specified for a multihomed peer.
-If none are specified then the values of Host-IP-Address
-on the service are used. (In particular, one of these must be specified.)
+If none are specified then the values of <c>Host-IP-Address</c>
+in the <c>#diameter_service{}</c> record are used.
+(In particular, one of these must be specified.)
Option <c>port</c> defaults to 3868 for a listening transport and 0 for a
connecting transport.</p>
+<warning>
+<p>
+An insufficiently large receive buffer may result in a peer having to
+resend incoming messages: set the <seealso
+marker="kernel:inet">inet(3)</seealso> option <c>recbuf</c> to increase
+the buffer size.</p>
+
+<p>
+An insufficiently large send buffer may result in outgoing messages
+being discarded: set the <seealso
+marker="kernel:inet">inet(3)</seealso> option <c>sndbuf</c> to increase
+the buffer size.</p>
+</warning>
+
<p>
diameter_sctp uses the <c>transport_data</c> field of
-the <c>diameter_packet</c> record to communicate the stream on which an
+the <c>#diameter_packet{}</c> record to communicate the stream on which an
inbound message has been received, or on which an outbound message
should be sent: the value will be of the form <c>{stream, Id}</c>
on an inbound message passed to a <seealso
marker="diameter_app#handle_request">handle_request</seealso> or <seealso
marker="diameter_app#handle_answer">handle_answer</seealso> callback.
-For an outbound message, either <c>undefined</c> (explicitly of
-by specifying the outbound message as a <c>binary()</c>) or a tuple
+For an outbound message, either <c>undefined</c> (explicitly or
+by receiving the outbound message as a <c>binary()</c>) or a tuple
should be set in the return value of <seealso
marker="diameter_app#handle_request">handle_request</seealso>
(typically by retaining the value passed into this function)
or <seealso
marker="diameter_app#prepare_request">prepare_request</seealso>.
-The value <c>undefined</c> uses a "next outbound stream" id and then
-increments this modulo the total number outbound streams. That
-is, successive values of <c>undefined</c> cycle through all outbound
-streams.</p>
+The value <c>undefined</c> uses a "next outbound stream" id and
+increments this modulo the total number outbound streams.
+That is, successive values of <c>undefined</c> cycle through all
+outbound streams.</p>
<!-- TODO: Some way of getting at the number of available outbound -->
<!-- streams. -->
@@ -128,7 +144,9 @@ streams.</p>
<title>SEE ALSO</title>
<p>
-<seealso marker="diameter_transport">diameter_transport(3)</seealso></p>
+<seealso marker="diameter_transport">diameter_transport(3)</seealso>,
+<seealso marker="kernel:gen_sctp">gen_sctp(3)</seealso>,
+<seealso marker="kernel:inet">inet(3)</seealso></p>
</section>
diff --git a/lib/diameter/doc/src/diameter_tcp.xml b/lib/diameter/doc/src/diameter_tcp.xml
index e6b53383c0..3ffcebfd90 100644
--- a/lib/diameter/doc/src/diameter_tcp.xml
+++ b/lib/diameter/doc/src/diameter_tcp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -38,8 +38,9 @@ under the License.
<description>
<p>
-This module implements diameter transport over TCP using gen_tcp.
-It can be specified as the value of a transport_module option to
+This module implements diameter transport over TCP using <seealso
+marker="kernel:gen_tcp">gen_tcp</seealso>.
+It can be specified as the value of a <c>transport_module</c> option to
<seealso
marker="diameter#add_transport">diameter:add_transport/2</seealso>
and implements the behaviour documented in
@@ -65,17 +66,17 @@ before configuring TLS capability on diameter transports.</p>
<fsummary>Start a transport process.</fsummary>
<type>
<v>Type = connect | accept</v>
-<v>Ref = reference()</v>
+<v>Ref = <seealso marker="diameter#transport_ref">diameter:transport_ref()</seealso></v>
<v>Svc = #diameter_service{}</v>
-<v>Opt = OwnOpt | SslOpt | OtherOpt</v>
+<v>Opt = OwnOpt | SslOpt | TcpOpt</v>
<v>Pid = pid()</v>
-<v>LAddr = ip_address()</v>
+<v>LAddr = <seealso marker="kernel:inet#type-ip_address">inet:ip_address()</seealso></v>
<v>Reason = term()</v>
-<v>OwnOpt = {raddr, ip_address()}
+<v>OwnOpt = {raddr, <seealso marker="kernel:inet#type-ip_address">inet:ip_address()</seealso>}
| {rport, integer()}
| {port, integer()}</v>
<v>SslOpt = {ssl_options, true | list()}</v>
-<v>OtherOpt = term()</v>
+<v>TcpOpt = term()</v>
</type>
<desc>
@@ -89,25 +90,32 @@ Options <c>raddr</c> and <c>rport</c> specify the remote address
and port for a connecting transport and are not valid for a listening
transport.
Option <c>ssl_options</c> must be specified for a transport
-that must be able to support TLS: a value of <c>true</c> results in a
+that should support TLS: a value of <c>true</c> results in a
TLS handshake immediately upon connection establishment while
-list() specifies options to be passed to ssl:connect/2 of ssl:ssl_accept/2
+<c>list()</c> specifies options to be passed to <seealso
+marker="ssl:ssl#connect-2">ssl:connect/2</seealso> or
+<seealso marker="ssl:ssl#ssl_accept-2">ssl:ssl_accept/2</seealso>
after capabilities exchange if TLS is negotiated.
-Remaining options are any accepted by ssl:connect/3 or gen_tcp:connect/3 for
-a connecting transport, or ssl:listen/3 or gen_tcp:listen/2 for
+Remaining options are any accepted by <seealso
+marker="ssl:ssl#connect-3">ssl:connect/3</seealso> or <seealso
+marker="kernel:gen_tcp#connect-3">gen_tcp:connect/3</seealso> for
+a connecting transport, or <seealso
+marker="ssl:ssl#listen-2">ssl:listen/2</seealso> or <seealso
+marker="kernel:gen_tcp#listen-2">gen_tcp:listen/2</seealso> for
a listening transport, depending on whether or not <c>{ssl_options, true}</c>
has been specified.
-Options <c>binary</c>, <c>packet</c> and <c>active</c> cannot be specified.
+Options <c>binary</c>,
+<c>packet</c> and <c>active</c> cannot be specified.
Also, option <c>port</c> can be specified for a listening transport
to specify the local listening port, the default being the standardized
3868 if unspecified.
-Note that option <c>ip</c> specifies the local address.</p>
+Note that the option <c>ip</c> specifies the local address.</p>
<p>
An <c>ssl_options</c> list must be specified if and only if
-the transport in question has specified an Inband-Security-Id
-AVP with value TLS on the relevant call to
-<seealso
+the transport in question has set <c>Inband-Security-Id</c> to
+1 (<c>TLS</c>), as
+specified to either <seealso
marker="diameter#start_service">start_service/2</seealso> or
<seealso
marker="diameter#add_transport">add_transport/2</seealso>,
@@ -122,9 +130,9 @@ that will not be forthcoming, which will eventually cause the RFC 3539
watchdog to take down the connection.</p>
<p>
-If the service specifies more than one Host-IP-Address and
-option <c>ip</c> is unspecified then then the
-first of the service's addresses is used as the local address.</p>
+If the <c>#diameter_service{}</c> record has more than one
+<c>Host-IP-Address</c> and option <c>ip</c> is unspecified then the
+first of the these addresses is used as the local address.</p>
<p>
The returned local address list has length one.</p>
@@ -142,7 +150,10 @@ The returned local address list has length one.</p>
<p>
<seealso marker="diameter">diameter(3)</seealso>,
-<seealso marker="diameter_transport">diameter_transport(3)</seealso></p>
+<seealso marker="diameter_transport">diameter_transport(3)</seealso>,
+<seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso>,
+<seealso marker="kernel:inet">inet(3)</seealso>,
+<seealso marker="ssl:ssl">ssl(3)</seealso></p>
</section>
diff --git a/lib/diameter/doc/src/diameter_transport.xml b/lib/diameter/doc/src/diameter_transport.xml
index 087a90b099..9ab750e560 100644
--- a/lib/diameter/doc/src/diameter_transport.xml
+++ b/lib/diameter/doc/src/diameter_transport.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -59,11 +59,11 @@ parent).</p>
<fsummary>Start a transport process.</fsummary>
<type>
<v>Type = connect | accept</v>
-<v>Ref = reference()</v>
+<v>Ref = <seealso marker="diameter#transport_ref">diameter:transport_ref()</seealso></v>
<v>Svc = #diameter_service{}</v>
<v>Opts = term()</v>
<v>Pid = pid()</v>
-<v>LAddrs = [ip_address()]</v>
+<v>LAddrs = [<seealso marker="kernel:inet#type-ip_address">inet:ip_address()</seealso>]</v>
<v>Reason = term()</v>
</type>
<desc>
@@ -93,16 +93,19 @@ It should not link to the parent.
It should exit if its transport connection with its peer is lost.</p>
<p>
-Transport processes for a given service are started sequentially.</p>
-
-<p>
-The start function should use the 'Host-IP-Address' list on the service,
-namely <c>Svc#diameter_service.host_ip_address</c>, and/or
+The capabilities in the <c>#diameter_service{}</c> record are as
+passed to <seealso
+marker="diameter#start_service">diameter:start_service/2</seealso> and
+<seealso
+marker="diameter#add_transport">diameter:add_transport/2</seealso>,
+values passed to the latter overriding those passed to the former.
+The start function should use the <c>Host-IP-Address</c> list and/or
<c>Opts</c> to select an appropriate list of local IP addresses,
-and should return this list if different from the service addresses.
-The returned list is used to populate 'Host-IP-Address' AVPs in outgoing
-capabilities exchange messages, the service addresses being used
-otherwise.</p>
+and should return this list if different from the
+<c>#diameter_service{}</c> addresses.
+The returned list is used to populate <c>Host-IP-Address</c> AVPs in
+outgoing capabilities exchange messages, the
+<c>#diameter_service{}</c> addresses being used otherwise.</p>
<marker id="MESSAGES"/>
</desc>
@@ -129,9 +132,10 @@ diameter.</p>
<item>
<p>
An outbound Diameter message.
-Packet can be either <c>binary()</c> (the message to be sent)
-or a <c>#diameter_packet{}</c> whose <c>transport_data</c> field
-containes a value other than undefined.</p>
+<c>Packet</c> can be either binary() (the message to be sent)
+or a <c>#diameter_packet{}</c> record whose <c>transport_data</c>
+field contains a value other than undefined and whose <c>bin</c> field
+contains the binary to send.</p>
</item>
<tag><c>{diameter, {close, Pid}}</c></tag>
@@ -140,7 +144,7 @@ containes a value other than undefined.</p>
A request to close the transport connection.
The transport process should terminate after closing the
connection.
-Pid is the pid() of the parent process.</p>
+<c>Pid</c> is the pid() of the parent process.</p>
</item>
<tag><c>{diameter, {tls, Ref, Type, Bool}}</c></tag>
@@ -148,17 +152,17 @@ Pid is the pid() of the parent process.</p>
<p>
Indication of whether or not capabilities exchange has selected
inband security using TLS.
-Ref is a reference() that must be included in the
+<c>Ref</c> is a reference() that must be included in the
<c>{diameter, {tls, Ref}}</c> reply message to the transport's
parent process (see below).
-Type is either <c>connect</c> or <c>accept</c> depending on
+<c>Type</c> is either <c>connect</c> or <c>accept</c> depending on
whether the process has been started for a connecting or listening
transport respectively.
-Bool is a boolean() indicating whether or not the transport connection
-should be upgraded to TLS.</p>
+<c>Bool</c> is a boolean() indicating whether or not the transport
+connection should be upgraded to TLS.</p>
<p>
-If TLS is requested (Bool = true) then a connecting process should
+If TLS is requested (<c>Bool=true</c>) then a connecting process should
initiate a TLS handshake with the peer and an accepting process should
prepare to accept a handshake.
A successful handshake should be followed by a <c>{diameter, {tls, Ref}}</c>
@@ -182,18 +186,18 @@ to its parent.</p>
<tag><c>{diameter, {self(), connected}}</c></tag>
<item>
<p>
-Inform the parent that the transport process with Type = accept has
+Inform the parent that the transport process with <c>Type=accept</c> has
established a connection with the peer.
-Not sent if the transport process has Type = connect.</p>
+Not sent if the transport process has <c>Type=connect</c>.</p>
</item>
<tag><c>{diameter, {self(), connected, Remote}}</c></tag>
<item>
<p>
-Inform the parent that the transport process with Type = connect
+Inform the parent that the transport process with <c>Type=connect</c>
has established a connection with a peer.
-Not sent if the transport process has Type = accept.
-Remote is an arbitrary term that uniquely identifies the remote
+Not sent if the transport process has <c>Type=accept</c>.
+<c>Remote</c> is an arbitrary term that uniquely identifies the remote
endpoint to which the transport has connected.</p>
</item>
@@ -201,14 +205,14 @@ endpoint to which the transport has connected.</p>
<item>
<p>
An inbound Diameter message.
-Packet can be either <c>binary()</c> (the message to be sent)
-or <c>#diameter_packet{}</c>
-whose <c>packet</c> field contains a <c>binary()</c>.
-Any value (other than undefined) set in
+<c>Packet</c> can be either binary() (the received message)
+or a <c>#diameter_packet{}</c> record
+whose <c>bin</c> field contains the received binary().
+Any value (other than <c>undefined</c>) set in
the <c>transport_data</c> field will be passed back with a
corresponding answer message in the case that the inbound message is a
request unless the sender sets another value.
-How the <c>transport_data</c> is used/interpreted is up to the
+How <c>transport_data</c> is used/interpreted is up to the
transport module.</p>
</item>
@@ -216,7 +220,7 @@ transport module.</p>
<item>
<p>
Acknowledgment of a successful TLS handshake.
-Ref is the reference() received in the
+<c>Ref</c> is the reference() received in the
<c>{diameter, {tls, Ref, Type, Bool}}</c> message in response
to which the reply is sent.
A transport must exit if a handshake is not successful.</p>
diff --git a/lib/diameter/doc/src/diameter_using.xml b/lib/diameter/doc/src/diameter_using.xml
index 809b76bdf3..7d9c0cd492 100644
--- a/lib/diameter/doc/src/diameter_using.xml
+++ b/lib/diameter/doc/src/diameter_using.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
-<year>2011</year>
+<year>2011</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
@@ -23,7 +23,7 @@ under the License.
</legalnotice>
-<title>Using the stack</title>
+<title>Usage</title>
<prepared></prepared>
<responsible></responsible>
<docno></docno>
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index 6e364a3200..e57958ac09 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -36,6 +36,106 @@ first.</p>
<!-- ===================================================================== -->
+<section><title>Diameter 1.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix broken Result-Code setting and Destination-Host/Realm
+ extraction.</p>
+ <p>
+ Result-Code was assumed to have arity 1 when setting this
+ value in an answer to a request containing AVP decode
+ errors. Destination-Host/Realm were only correctly
+ extracted from messages in the common application.</p>
+ <p>
+ Own Id: OTP-10202</p>
+ </item>
+ <item>
+ <p>
+ Handle insufficient capabilities configuration more
+ gracefully.</p>
+ <p>
+ A transport that does not have sufficient capabilities
+ configuration in order to encode CER/CEA will now emit an
+ error report noting the configuration error and exit
+ instead of failing. The error is not detected at
+ diameter:add_transport/2 since there is no requirement
+ that a service be configured before its transports.</p>
+ <p>
+ Own Id: OTP-10203</p>
+ </item>
+ <item>
+ <p>
+ Ensure a failing peer_up/down callback does not affect
+ transport connections to other peers.</p>
+ <p>
+ Such a failure would previously have taken down all of a
+ service's connections.</p>
+ <p>
+ Own Id: OTP-10215</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Statistics related to Diameter messages can be retrieved
+ using diameter:service_info/2.</p>
+ <p>
+ Both Diameter and socket-level statistics are available,
+ for both incoming and outgoing messages.</p>
+ <p>
+ Own Id: OTP-9608</p>
+ </item>
+ <item>
+ <p>
+ Allow multiple transport_module/config to
+ diameter:add_transport/2.</p>
+ <p>
+ Multiple values are attempted in sequence until one
+ results in an established connection. This provides a way
+ for a connecting transport to specify configuration in
+ order of preference. (For example, SCTP before TCP.)</p>
+ <p>
+ Own Id: OTP-9885</p>
+ </item>
+ <item>
+ <p>
+ Add events for state transitions in the RFC 3539 watchdog
+ state machine.</p>
+ <p>
+ The watchdog state is also available through
+ diameter:service_info/2.</p>
+ <p>
+ Own Id: OTP-10212</p>
+ </item>
+ <item>
+ <p>
+ Add diameter:service_info(SvcName, connections).</p>
+ <p>
+ This provides an alternative to
+ diameter:service_info(SvcName, transport) that presents
+ information per established connection instead of per
+ transport reference.</p>
+ <p>
+ Own Id: OTP-10213</p>
+ </item>
+ <item>
+ <p>
+ Assorted documentation corrections/improvements.</p>
+ <p>
+ Own Id: OTP-10216</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Diameter 1.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/diameter/examples/code/peer.erl b/lib/diameter/examples/code/peer.erl
index b07cd32b98..8fdeba57bf 100644
--- a/lib/diameter/examples/code/peer.erl
+++ b/lib/diameter/examples/code/peer.erl
@@ -117,6 +117,11 @@ server(T) ->
%%
%% Return config for a connecting transport.
+client({all, LA, RA, RP}) ->
+ [[M,{K,C}], T]
+ = [client({P, LA, RA, RP}) || P <- [sctp,tcp]],
+ [M, {K,C,2000} | T];
+
client({T, LA, RA, RP}) ->
[{transport_module, tmod(T)},
{transport_config, [{ip, addr(LA)},
diff --git a/lib/diameter/include/diameter.hrl b/lib/diameter/include/diameter.hrl
index 4273262015..beb577afaf 100644
--- a/lib/diameter/include/diameter.hrl
+++ b/lib/diameter/include/diameter.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -139,7 +139,6 @@
init_state, %% option 'state', initial callback state
id, %% 32-bit unsigned application identifier = Dict:id()
mutable = false, %% boolean(), do traffic callbacks modify state?
- answer_errors = report}). %% | callback | discard
- %% how to handle containing errors?
+ options = [{answer_errors, report}]}). %% | callback | discard
-endif. %% -ifdef(diameter_hrl).
diff --git a/lib/diameter/src/.gitignore b/lib/diameter/src/.gitignore
index feeb378fd8..cc06720fd1 100644
--- a/lib/diameter/src/.gitignore
+++ b/lib/diameter/src/.gitignore
@@ -1,2 +1,3 @@
/depend.mk
+/otp.plt
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index de2eca0279..99c343275b 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2011. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. 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
@@ -181,6 +181,31 @@ clean:
rm -f $(TARGET_FILES) gen/*
rm -f depend.mk
+realclean: clean
+ rm -f ../ebin/*
+# Not $(EBIN) just to be a bit paranoid
+
+PLT = ./otp.plt
+
+plt:
+ dialyzer --build_plt \
+ --apps erts stdlib kernel \
+ xmerl ssl public_key crypto \
+ compiler syntax_tools runtime_tools \
+ --output_plt $(PLT) \
+ --verbose
+
+dialyze: opt $(PLT)
+ dialyzer --plt $(PLT) \
+ --verbose \
+ -Wno_improper_lists \
+ $(EBIN)/diameter_gen_base_rfc3588.$(EMULATOR) \
+ $(patsubst %, $(EBIN)/%.$(EMULATOR), \
+ $(notdir $(RT_MODULES) $(CT_MODULES)))
+# Omit all but the common dictionary module since these
+# (diameter_gen_relay in particular) generate warning depending on how
+# much of the included diameter_gen.hrl they use.
+
# ----------------------------------------------------
# Release targets
# ----------------------------------------------------
@@ -245,10 +270,11 @@ depend.mk: depend.sed $(MODULES:%=%.erl) Makefile
-include depend.mk
-.PHONY: app clean depend dict info release_subdir
+.PHONY: app clean realclean depend dict info release_subdir
.PHONY: debug opt release_docs_spec release_spec
.PHONY: $(TARGET_DIRS:%/=%) $(TARGET_DIRS:%/=release_src_%)
.PHONY: $(EXAMPLE_DIRS:%/=release_examples_%)
+.PHONY: plt dialyze
# Keep intermediate files.
.SECONDARY: $(DICT_ERLS) $(DICT_HRLS) gen/$(DICT_YRL:%=%.erl)
diff --git a/lib/diameter/src/base/diameter.appup.src b/lib/diameter/src/base/diameter.appup.src
index 2ebdad598f..9b2a7d18ab 100644
--- a/lib/diameter/src/base/diameter.appup.src
+++ b/lib/diameter/src/base/diameter.appup.src
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -22,13 +22,28 @@
[
{"0.9", [{restart_application, diameter}]},
{"0.10", [{restart_application, diameter}]},
- {"1.0", [{update, diameter_service},
- {update, diameter_watchdog}]}
+ {"1.0", [{restart_application, diameter}]},
+ {"1.1", [%% new code
+ {add_module, diameter_transport},
+ %% modified code
+ {load, diameter_sctp},
+ {load, diameter_stats},
+ {load, diameter_service},
+ {load, diameter_config},
+ {load, diameter_codec},
+ {load, diameter_watchdog},
+ {load, diameter_peer},
+ {load, diameter_peer_fsm},
+ {load, diameter},
+ %% unmodified but including modified diameter.hrl
+ {load, diameter_callback},
+ {load, diameter_capx},
+ {load, diameter_types}]}
],
[
{"0.9", [{restart_application, diameter}]},
{"0.10", [{restart_application, diameter}]},
- {"1.0", [{update, diameter_watchdog},
- {update, diameter_service}]}
+ {"1.0", [{restart_application, diameter}]},
+ {"1.1", [{restart_application, diameter}]}
]
}.
diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl
index 336f0c1f2d..4f90b741ae 100644
--- a/lib/diameter/src/base/diameter.erl
+++ b/lib/diameter/src/base/diameter.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -312,6 +312,7 @@ call(SvcName, App, Message) ->
-type transport_opt()
:: {transport_module, atom()}
| {transport_config, any()}
+ | {transport_config, any(), non_neg_integer() | infinity}
| {applications, [app_alias()]}
| {capabilities, [capability()]}
| {capabilities_cb, evaluable()}
@@ -322,10 +323,11 @@ call(SvcName, App, Message) ->
%% Predicate passed to remove_transport/2
-type transport_pred()
- :: fun((reference(), connect|listen, list()) -> boolean())
- | fun((reference(), list()) -> boolean())
+ :: fun((transport_ref(), connect|listen, list()) -> boolean())
+ | fun((transport_ref(), list()) -> boolean())
| fun((list()) -> boolean())
- | reference()
+ | transport_ref()
+ | boolean()
| list()
| {connect|listen, transport_pred()}
| {atom(), atom(), list()}.
diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl
index fe1212b7e0..421e280422 100644
--- a/lib/diameter/src/base/diameter_codec.erl
+++ b/lib/diameter/src/base/diameter_codec.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -63,9 +63,9 @@ encode(Mod, #diameter_packet{} = Pkt) ->
e(Mod, Pkt)
catch
error: Reason ->
- %% Be verbose rather than letting the emulator truncate the
- %% error report.
- X = {Reason, ?STACK},
+ %% Be verbose since a crash report may be truncated and
+ %% encode errors are self-inflicted.
+ X = {?MODULE, encode, {Reason, ?STACK}},
diameter_lib:error_report(X, {?MODULE, encode, [Mod, Pkt]}),
exit(X)
end;
@@ -91,7 +91,8 @@ e(_, #diameter_packet{msg = [#diameter_header{} = Hdr | As]} = Pkt) ->
Flags = make_flags(0, Hdr),
- Pkt#diameter_packet{bin = <<Vsn:8, Length:24,
+ Pkt#diameter_packet{header = Hdr,
+ bin = <<Vsn:8, Length:24,
Flags:8, Code:24,
Aid:32,
Hid:32,
diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl
index 9253af0de2..e47f63f814 100644
--- a/lib/diameter/src/base/diameter_config.erl
+++ b/lib/diameter/src/base/diameter_config.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -519,6 +519,7 @@ rm(SvcName, L) ->
Refs = lists:map(fun(#transport{ref = R}) -> R end, L),
case stop_transport(SvcName, Refs) of
ok ->
+ diameter_stats:flush(Refs),
lists:foreach(fun delete_object/1, L);
{error, _} = No ->
No
@@ -600,7 +601,7 @@ app_acc({application, Opts}, Acc) ->
module = init_mod(Mod),
init_state = ModS,
mutable = init_mutable(M),
- answer_errors = init_answers(A)}
+ options = [{answer_errors, init_answers(A)}]}
| Acc];
app_acc(_, Acc) ->
Acc.
diff --git a/lib/diameter/src/base/diameter_peer.erl b/lib/diameter/src/base/diameter_peer.erl
index 3e78c4caef..46b2ba9465 100644
--- a/lib/diameter/src/base/diameter_peer.erl
+++ b/lib/diameter/src/base/diameter_peer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -27,12 +27,15 @@
up/2]).
%% ... and the stack.
--export([start/3,
+-export([start/1,
send/2,
close/1,
abort/1,
notify/2]).
+%% Old interface only called from old code.
+-export([start/3]). %% < diameter-1.2 (R15B02)
+
%% Server start.
-export([start_link/0]).
@@ -57,6 +60,11 @@
%% Server state.
-record(state, {id = now()}).
+%% Default transport_module/config.
+-define(DEFAULT_TMOD, diameter_tcp).
+-define(DEFAULT_TCFG, []).
+-define(DEFAULT_TTMO, infinity).
+
%%% ---------------------------------------------------------------------------
%%% # notify/2
%%% ---------------------------------------------------------------------------
@@ -68,9 +76,119 @@ notify(SvcName, T) ->
%%% # start/3
%%% ---------------------------------------------------------------------------
-start(T, Opts, #diameter_service{} = Svc) ->
- {Mod, Cfg} = split_transport(Opts),
- apply(Mod, start, [T, Svc, Cfg]).
+%% From old code: make is restart.
+start(_T, _Opts, #diameter_service{}) ->
+ {error, restart}.
+
+%%% ---------------------------------------------------------------------------
+%%% # start/1
+%%% ---------------------------------------------------------------------------
+
+-spec start({T, [Opt], #diameter_service{}})
+ -> {TPid, [Addr], Tmo, Data}
+ | {error, [term()]}
+ when T :: {connect|accept, diameter:transport_ref()},
+ Opt :: diameter:transport_opt(),
+ TPid :: pid(),
+ Addr :: inet:ip_address(),
+ Tmo :: non_neg_integer() | infinity,
+ Data :: {{T, Mod, Cfg}, [Mod], [{T, [Mod], Cfg}], [Err]},
+ Mod :: module(),
+ Cfg :: term(),
+ Err :: term()
+ ; ({#diameter_service{}, Tmo, Data})
+ -> {TPid, [Addr], Tmo, Data}
+ | {error, [term()]}
+ when TPid :: pid(),
+ Addr :: inet:ip_address(),
+ Tmo :: non_neg_integer() | infinity,
+ Data :: {{T, Mod, Cfg}, [Mod], [{T, [Mod], Cfg}], [Err]},
+ T :: {connect|accept, diameter:transport_ref()},
+ Mod :: module(),
+ Cfg :: term(),
+ Err :: term().
+
+%% Initial start.
+start({T, Opts, #diameter_service{} = Svc}) ->
+ start(T, Svc, pair(Opts, [], []), []);
+
+%% Subsequent start.
+start({#diameter_service{} = Svc, Tmo, {{T, _, Cfg}, Ms, Rest, Errs}}) ->
+ start(T, Ms, Cfg, Svc, Tmo, Rest, Errs).
+
+%% pair/3
+%%
+%% Pair transport modules with config.
+
+%% Another transport_module: accumulate it.
+pair([{transport_module, M} | Rest], Mods, Acc) ->
+ pair(Rest, [M|Mods], Acc);
+
+%% Another transport_config: accumulate another tuple.
+pair([{transport_config = T, C} | Rest], Mods, Acc) ->
+ pair([{T, C, ?DEFAULT_TTMO} | Rest], Mods, Acc);
+pair([{transport_config, C, Tmo} | Rest], Mods, Acc) ->
+ pair(Rest, [], acc({Mods, C, Tmo}, Acc));
+
+pair([_ | Rest], Mods, Acc) ->
+ pair(Rest, Mods, Acc);
+
+%% No transport_module or transport_config: defaults.
+pair([], [], []) ->
+ [{[?DEFAULT_TMOD], ?DEFAULT_TCFG, ?DEFAULT_TTMO}];
+
+%% One transport_module, one transport_config.
+pair([], [M], [{[], Cfg, Tmo}]) ->
+ [{[M], Cfg, Tmo}];
+
+%% Trailing transport_module: default transport_config.
+pair([], [_|_] = Mods, Acc) ->
+ lists:reverse(acc({Mods, ?DEFAULT_TCFG, ?DEFAULT_TTMO}, Acc));
+
+pair([], [], Acc) ->
+ lists:reverse(def(Acc)).
+
+%% acc/2
+
+acc(T, Acc) ->
+ [T | def(Acc)].
+
+%% def/1
+%%
+%% Default module of previous pair if none were specified.
+
+def([{[], Cfg, Tmo} | Acc]) ->
+ [{[?DEFAULT_TMOD], Cfg, Tmo} | Acc];
+def(Acc) ->
+ Acc.
+
+%% start/4
+
+start(T, Svc, [{Ms, Cfg, Tmo} | Rest], Errs) ->
+ start(T, Ms, Cfg, Svc, Tmo, Rest, Errs);
+
+start(_, _, [], Errs) ->
+ {error, Errs}.
+
+%% start/7
+
+start(T, [], _, Svc, _, Rest, Errs) ->
+ start(T, Svc, Rest, Errs);
+
+start(T, [M|Ms], Cfg, Svc, Tmo, Rest, Errs) ->
+ case start(M, [T, Svc, Cfg]) of
+ {ok, TPid} ->
+ {TPid, [], Tmo, {{T, M, Cfg}, Ms, Rest, Errs}};
+ {ok, TPid, [_|_] = Addrs} ->
+ {TPid, Addrs, Tmo, {{T, M, Cfg}, Ms, Rest, Errs}};
+ E ->
+ start(T, Ms, Cfg, Svc, Tmo, Rest, [E|Errs])
+ end.
+
+%% start/2
+
+start(Mod, Args) ->
+ apply(Mod, start, Args).
%%% ---------------------------------------------------------------------------
%%% # up/[12]
@@ -204,21 +322,6 @@ bang(undefined = No, _) ->
bang(Pid, T) ->
Pid ! T.
-%% split_transport/1
-%%
-%% Split options into transport module, transport config and
-%% remaining options.
-
-split_transport(Opts) ->
- {[M,C], _} = proplists:split(Opts, [transport_module,
- transport_config]),
- {value(M, diameter_tcp), value(C, [])}.
-
-value([{_,V}], _) ->
- V;
-value([], V) ->
- V.
-
%% call/1
call(Request) ->
diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl
index 99644814d2..302540e76b 100644
--- a/lib/diameter/src/base/diameter_peer_fsm.erl
+++ b/lib/diameter/src/base/diameter_peer_fsm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -54,6 +54,12 @@
-define(NO_INBAND_SECURITY, 0).
-define(TLS, 1).
+%% Keys in process dictionary.
+-define(CB_KEY, cb). %% capabilities callback
+-define(DWA_KEY, dwa). %% outgoing DWA
+-define(Q_KEY, q). %% transport start queue
+-define(START_KEY, start). %% start of connected transport
+
%% A 2xxx series Result-Code. Not necessarily 2001.
-define(IS_SUCCESS(N), 2 == (N) div 1000).
@@ -115,16 +121,20 @@
%%% Output: Pid
%%% ---------------------------------------------------------------------------
+-spec start(T, [Opt], #diameter_service{})
+ -> pid()
+ when T :: {connect|accept, diameter:transport_ref()},
+ Opt :: diameter:transport_opt().
+
%% diameter_config requires a non-empty list of applications on the
%% service but diameter_service then constrains the list to any
%% specified on the transport in question. Check here that the list is
%% still non-empty.
-start({_, Ref} = Type, Opts, #diameter_service{applications = Apps} = Svc) ->
+start({_,_} = Type, Opts, #diameter_service{applications = Apps} = Svc) ->
[] /= Apps orelse ?ERROR({no_apps, Type, Opts}),
T = {self(), Type, Opts, Svc},
{ok, Pid} = diameter_peer_fsm_sup:start_child(T),
- diameter_stats:reg(Pid, Ref),
Pid.
start_link(T) ->
@@ -143,18 +153,18 @@ init(T) ->
proc_lib:init_ack({ok, self()}),
gen_server:enter_loop(?MODULE, [], i(T)).
-i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc0}) ->
- putr(dwa, dwa(Caps)),
+i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc}) ->
+ putr(?DWA_KEY, dwa(Caps)),
{M, Ref} = T,
+ diameter_stats:reg(Ref),
{[Ts], Rest} = proplists:split(Opts, [capabilities_cb]),
- putr(capabilities_cb, {Ref, [F || {_,F} <- Ts]}),
- {ok, TPid, Svc} = start_transport(T, Rest, Svc0),
- erlang:monitor(process, TPid),
+ putr(?CB_KEY, {Ref, [F || {_,F} <- Ts]}),
erlang:monitor(process, WPid),
+ {TPid, Addrs} = start_transport(T, Rest, Svc),
#state{parent = WPid,
transport = TPid,
mode = M,
- service = Svc}.
+ service = svc(Svc, Addrs)}.
%% The transport returns its local ip addresses so that different
%% transports on the same service can use different local addresses.
%% The local addresses are put into Host-IP-Address avps here when
@@ -164,18 +174,56 @@ i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc0}) ->
%% watchdog start (start/2) succeeds regardless so as not to crash the
%% service.
-start_transport(T, Opts, Svc) ->
- case diameter_peer:start(T, Opts, Svc) of
- {ok, TPid} ->
- {ok, TPid, Svc};
- {ok, TPid, [_|_] = Addrs} ->
- #diameter_service{capabilities = Caps0} = Svc,
- Caps = Caps0#diameter_caps{host_ip_address = Addrs},
- {ok, TPid, Svc#diameter_service{capabilities = Caps}};
+start_transport(T, Opts, #diameter_service{capabilities = Caps} = Svc) ->
+ Addrs0 = Caps#diameter_caps.host_ip_address,
+ start_transport(Addrs0, {T, Opts, Svc}).
+
+start_transport(Addrs0, T) ->
+ case diameter_peer:start(T) of
+ {TPid, Addrs, Tmo, Data} ->
+ erlang:monitor(process, TPid),
+ q_next(TPid, Addrs0, Tmo, Data),
+ {TPid, addrs(Addrs, Addrs0)};
No ->
exit({shutdown, No})
end.
+addrs([], Addrs0) ->
+ Addrs0;
+addrs(Addrs, _) ->
+ Addrs.
+
+svc(Svc, []) ->
+ Svc;
+svc(Svc, Addrs) ->
+ readdr(Svc, Addrs).
+
+readdr(#diameter_service{capabilities = Caps0} = Svc, Addrs) ->
+ Caps = Caps0#diameter_caps{host_ip_address = Addrs},
+ Svc#diameter_service{capabilities = Caps}.
+
+%% The 4-tuple Data returned from diameter_peer:start/1 identifies the
+%% transport module/config use to start the transport process in
+%% question as well as any alternates to try if a connection isn't
+%% established within Tmo.
+q_next(TPid, Addrs0, Tmo, {_,_,_,_} = Data) ->
+ send_after(Tmo, {connection_timeout, TPid}),
+ putr(?Q_KEY, {Addrs0, Tmo, Data}).
+
+%% Connection has been established: retain the started
+%% pid/module/config in the process dictionary. This is a part of the
+%% interface defined by this module, so that the transport pid can be
+%% found when constructing service_info (in order to extract further
+%% information from it).
+keep_transport(TPid) ->
+ {_, _, {{_,_,_} = T, _, _, _}} = eraser(?Q_KEY),
+ putr(?START_KEY, {TPid, T}).
+
+send_after(infinity, _) ->
+ ok;
+send_after(Tmo, T) ->
+ erlang:send_after(Tmo, self(), T).
+
%% handle_call/3
handle_call(_, _, State) ->
@@ -202,14 +250,27 @@ handle_info(T, #state{} = State) ->
?LOG(stop, T),
x(T, State)
catch
+ exit: {diameter_codec, encode, _} = Reason ->
+ close_wd(Reason, State#state.parent),
+ ?LOG(stop, Reason),
+ %% diameter_codec:encode/2 emits an error report. Only
+ %% indicate the probable reason here.
+ diameter_lib:info_report(probable_configuration_error,
+ insufficient_capabilities),
+ {stop, {shutdown, Reason}, State};
{?MODULE, Tag, Reason} ->
?LOG(Tag, {Reason, T}),
{stop, {shutdown, Reason}, State}
end.
-%% The form of the exception caught here is historical. It's
+%% The form of the throw caught here is historical. It's
%% significant that it's not a 2-tuple, as in ?FAILURE(Reason),
%% since these are caught elsewhere.
+%% Note that there's no guarantee that the service and transport
+%% capabilities are good enough to build a CER/CEA that can be
+%% succesfully encoded. It's not checked at diameter:add_transport/2
+%% since this can be called before creating the service.
+
x(Reason, #state{} = S) ->
close_wd(Reason, S),
{stop, {shutdown, Reason}, S}.
@@ -240,25 +301,48 @@ eraser(Key) ->
%% Connection to peer.
transition({diameter, {TPid, connected, Remote}},
- #state{state = PS,
+ #state{transport = TPid,
+ state = PS,
mode = M}
= S) ->
'Wait-Conn-Ack' = PS, %% assert
connect = M, %%
- send_CER(S#state{mode = {M, Remote},
- transport = TPid});
+ keep_transport(TPid),
+ send_CER(S#state{mode = {M, Remote}});
%% Connection from peer.
transition({diameter, {TPid, connected}},
- #state{state = PS,
+ #state{transport = TPid,
+ state = PS,
mode = M,
parent = Pid}
= S) ->
'Wait-Conn-Ack' = PS, %% assert
accept = M, %%
+ keep_transport(TPid),
Pid ! {accepted, self()},
- start_timer(S#state{state = recv_CER,
- transport = TPid});
+ start_timer(S#state{state = recv_CER});
+
+%% Connection established after receiving a connection_timeout
+%% message. This may be followed by an incoming message which arrived
+%% before the transport was killed and this can't be distinguished
+%% from one from the transport that's been started to replace it.
+transition({diameter, {_, connected}}, _) ->
+ {stop, connection_timeout};
+transition({diameter, {_, connected, _}}, _) ->
+ {stop, connection_timeout};
+
+%% Connection has timed out: start an alternate.
+transition({connection_timeout = T, TPid},
+ #state{transport = TPid,
+ state = 'Wait-Conn-Ack'}
+ = S) ->
+ exit(TPid, {shutdown, T}),
+ start_next(S);
+
+%% Connect timeout after connection or alternate start: ignore.
+transition({connection_timeout, _}, _) ->
+ ok;
%% Incoming message from the transport.
transition({diameter, {recv, Pkt}}, S) ->
@@ -305,14 +389,21 @@ transition({resolve_port, _Pid} = T, #state{transport = TPid}) ->
TPid ! T,
ok;
-%% Parent or transport has died.
-transition({'DOWN', _, process, P, _},
- #state{parent = Pid,
- transport = TPid})
- when P == Pid;
- P == TPid ->
+%% Parent has died.
+transition({'DOWN', _, process, WPid, _},
+ #state{parent = WPid}) ->
stop;
+%% Transport has died before connection timeout.
+transition({'DOWN', _, process, TPid, _},
+ #state{transport = TPid}
+ = S) ->
+ start_next(S);
+
+%% Transport has died after connection timeout.
+transition({'DOWN', _, process, _, _}, _) ->
+ ok;
+
%% State query.
transition({state, Pid}, #state{state = S, transport = TPid}) ->
Pid ! {self(), [S, TPid]},
@@ -320,6 +411,19 @@ transition({state, Pid}, #state{state = S, transport = TPid}) ->
%% Crash on anything unexpected.
+%% start_next/1
+
+start_next(#state{service = Svc0} = S) ->
+ case getr(?Q_KEY) of
+ {Addrs0, Tmo, Data} ->
+ Svc = readdr(Svc0, Addrs0),
+ {TPid, Addrs} = start_transport(Addrs0, {Svc, Tmo, Data}),
+ S#state{transport = TPid,
+ service = svc(Svc, Addrs)};
+ undefined ->
+ stop
+ end.
+
%% send_CER/1
send_CER(#state{mode = {connect, Remote},
@@ -649,7 +753,7 @@ rc([RC|_]) ->
%% answer/2
answer('DWR', _) ->
- getr(dwa);
+ getr(?DWA_KEY);
answer(Name, #state{service = #diameter_service{capabilities = Caps}}) ->
a(Name, Caps).
@@ -749,15 +853,15 @@ caps(#state{service = Svc}) ->
%% caps_cb/1
caps_cb(Caps) ->
- {Ref, Ts} = eraser(capabilities_cb),
- ccb(Ts, [Ref, Caps]).
+ {Ref, Ts} = eraser(?CB_KEY),
+ caps_cb(Ts, [Ref, Caps]).
-ccb([], _) ->
+caps_cb([], _) ->
ok;
-ccb([F | Rest], T) ->
+caps_cb([F | Rest], T) ->
case diameter_lib:eval([F|T]) of
ok ->
- ccb(Rest, T);
+ caps_cb(Rest, T);
N when ?IS_SUCCESS(N) -> %% 2xxx result code: accept immediately
N;
Res ->
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index 3dfdcee2b2..725cccda1e 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -43,8 +43,7 @@
subscriptions/0,
services/0,
services/1,
- whois/1,
- flush_stats/1]).
+ whois/1]).
%% test/debug
-export([call_module/3,
@@ -65,13 +64,33 @@
-include_lib("diameter/include/diameter.hrl").
-include("diameter_internal.hrl").
+%% The "old" states maintained in this module historically.
-define(STATE_UP, up).
-define(STATE_DOWN, down).
+-type op_state() :: ?STATE_UP
+ | ?STATE_DOWN.
+
+%% The RFC 3539 watchdog states that are now maintained, albeit
+%% along with the old up/down. okay = up, else down.
+-define(WD_INITIAL, initial).
+-define(WD_OKAY, okay).
+-define(WD_SUSPECT, suspect).
+-define(WD_DOWN, down).
+-define(WD_REOPEN, reopen).
+
+-type wd_state() :: ?WD_INITIAL
+ | ?WD_OKAY
+ | ?WD_SUSPECT
+ | ?WD_DOWN
+ | ?WD_REOPEN.
+
-define(DEFAULT_TC, 30000). %% RFC 3588 ch 2.1
-define(DEFAULT_TIMEOUT, 5000). %% for outgoing requests
-define(RESTART_TC, 1000). %% if restart was this recent
+-define(RELAY, ?DIAMETER_DICT_RELAY).
+
%% Used to be able to swap this with anything else dict-like but now
%% rely on the fact that a service's #state{} record does not change
%% in storing in it ?STATE table and not always going through the
@@ -117,7 +136,8 @@
type :: match(connect | accept),
ref :: match(reference()), %% key into diameter_config
options :: match([diameter:transport_opt()]),%% from start_transport
- op_state = ?STATE_DOWN :: match(?STATE_DOWN | ?STATE_UP),
+ op_state = {?STATE_DOWN, ?WD_INITIAL}
+ :: match(op_state() | {op_state(), wd_state()}),
started = now(), %% at process start
conn = false :: match(boolean() | pid())}).
%% true at accept, pid() at connection_up (connT key)
@@ -388,15 +408,6 @@ whois(SvcName) ->
undefined
end.
-%%% ---------------------------------------------------------------------------
-%%% # flush_stats/1
-%%%
-%%% Output: list of {{SvcName, Alias, Counter}, Value}
-%%% ---------------------------------------------------------------------------
-
-flush_stats(TPid) ->
- diameter_stats:flush(TPid).
-
%% ===========================================================================
%% ===========================================================================
@@ -516,6 +527,34 @@ transition({reconnect, Pid}, S) ->
reconnect(Pid, S),
ok;
+%% Watchdog is sending notification of a state transition. Note that
+%% the connection_up/down messages are pre-date this message and are
+%% still used. A 'watchdog' message will follow these and communicate
+%% the same state as was set in handling connection_up/down.
+transition({watchdog, Pid, {TPid, From, To}}, #state{service_name = SvcName,
+ peerT = PeerT}) ->
+ #peer{ref = Ref, type = T, options = Opts, op_state = {OS,_}}
+ = P
+ = fetch(PeerT, Pid),
+ insert(PeerT, P#peer{op_state = {OS, To}}),
+ send_event(SvcName, {watchdog, Ref, TPid, {From, To}, {T, Opts}}),
+ ok;
+%% Death of a peer process results in the removal of it's peer and any
+%% associated conn record when 'DOWN' is received (after this) but the
+%% states will be {?STATE_UP, ?WD_DOWN} for a short time. (No real
+%% problem since ?WD_* is only used in service_info.) We set ?WD_OKAY
+%% as a consequence of connection_up since we know a watchdog is
+%% coming. We can't set anything at connection_down since we don't
+%% know if the subsequent watchdog message will be ?WD_DOWN or
+%% ?WD_SUSPECT. We don't (yet) set ?STATE_* as a consequence of a
+%% watchdog message since this requires changing some of the matching
+%% on ?STATE_*.
+%%
+%% Death of a conn process results in connection_down followed by
+%% watchdog ?WD_DOWN. The latter doesn't result in the conn record
+%% being deleted since 'DOWN' from death of its peer doesn't (yet)
+%% deal with the record having been removed.
+
%% Monitor process has died. Just die with a reason that tells
%% diameter_config about the happening. If a cleaner shutdown is
%% required then someone should stop us.
@@ -879,7 +918,14 @@ accepted(Pid, _TPid, #state{peerT = PeerT} = S) ->
fetch(Tid, Key) ->
[T] = ets:lookup(Tid, Key),
- T.
+ case T of
+ #peer{op_state = ?STATE_UP} = P ->
+ P#peer{op_state = {?STATE_UP, ?WD_OKAY}};
+ #peer{op_state = ?STATE_DOWN} = P ->
+ P#peer{op_state = {?STATE_DOWN, ?WD_DOWN}};
+ _ ->
+ T
+ end.
%%% ---------------------------------------------------------------------------
%%% # connection_up/3
@@ -925,12 +971,12 @@ connection_up(T, P, C, #state{peerT = PeerT,
service
= #diameter_service{applications = Apps}}
= S) ->
- #peer{conn = TPid, op_state = ?STATE_DOWN}
+ #peer{conn = TPid, op_state = {?STATE_DOWN, _}}
= P,
#conn{apps = SApps, caps = Caps}
= C,
- insert(PeerT, P#peer{op_state = ?STATE_UP}),
+ insert(PeerT, P#peer{op_state = {?STATE_UP, ?WD_OKAY}}),
request_peer_up(TPid),
report_status(up, P, C, S, T),
@@ -945,27 +991,35 @@ ilp({Id, Alias}, {TC, SA}, LDict) ->
init_conn(Id, Alias, TC, SA),
?Dict:append(Alias, TC, LDict).
-init_conn(Id, Alias, TC, {SvcName, Apps}) ->
+init_conn(Id, Alias, {TPid, _} = TC, {SvcName, Apps}) ->
#diameter_app{module = ModX,
id = Id} %% assert
= find_app(Alias, Apps),
- peer_cb({ModX, peer_up, [SvcName, TC]}, Alias).
+ peer_cb({ModX, peer_up, [SvcName, TC]}, Alias)
+ orelse exit(TPid, kill). %% fake transport failure
+
+%% find_app/2
find_app(Alias, Apps) ->
- lists:keyfind(Alias, #diameter_app.alias, Apps).
+ case lists:keyfind(Alias, #diameter_app.alias, Apps) of
+ #diameter_app{options = E} = A when is_atom(E) -> %% upgrade
+ A#diameter_app{options = [{answer_errors, E}]};
+ A ->
+ A
+ end.
-%% A failing peer callback brings down the service. In the case of
-%% peer_up we could just kill the transport and emit an error but for
-%% peer_down we have no way to cleanup any state change that peer_up
-%% may have introduced.
+%% Don't bring down the service (and all associated connections)
+%% regardless of what happens.
peer_cb(MFA, Alias) ->
try state_cb(MFA, Alias) of
ModS ->
- mod_state(Alias, ModS)
+ mod_state(Alias, ModS),
+ true
catch
- E: Reason ->
- ?ERROR({E, Reason, MFA, ?STACK})
+ E:R ->
+ diameter_lib:error_report({failure, {E, R, Alias, ?STACK}}, MFA),
+ false
end.
%%% ---------------------------------------------------------------------------
@@ -979,22 +1033,22 @@ peer_cb(MFA, Alias) ->
connection_down(Pid, #state{peerT = PeerT,
connT = ConnT}
= S) ->
- #peer{op_state = ?STATE_UP, %% assert
+ #peer{op_state = {?STATE_UP, WS}, %% assert
conn = TPid}
= P
= fetch(PeerT, Pid),
C = fetch(ConnT, TPid),
- insert(PeerT, P#peer{op_state = ?STATE_DOWN}),
+ insert(PeerT, P#peer{op_state = {?STATE_DOWN, WS}}),
connection_down(P,C,S).
%% connection_down/3
-connection_down(#peer{op_state = ?STATE_DOWN}, _, S) ->
+connection_down(#peer{op_state = {?STATE_DOWN, _}}, _, S) ->
S;
connection_down(#peer{conn = TPid,
- op_state = ?STATE_UP}
+ op_state = {?STATE_UP, _}}
= P,
#conn{caps = Caps,
apps = SApps}
@@ -1043,7 +1097,7 @@ peer_down(Pid, Reason, #state{peerT = PeerT} = S) ->
%% Send an event at connection establishment failure.
closed({shutdown, {close, _TPid, Reason}},
- #peer{op_state = ?STATE_DOWN,
+ #peer{op_state = {?STATE_DOWN, _},
ref = Ref,
type = Type,
options = Opts},
@@ -1352,7 +1406,7 @@ send_request(Pkt, TPid, Caps, App, Opts, Caller, SvcName) ->
#diameter_app{alias = Alias,
dictionary = Dict,
module = ModX,
- answer_errors = AE}
+ options = [{answer_errors, AE} | _]}
= App,
EPkt = encode(Dict, Pkt),
@@ -1935,6 +1989,12 @@ is_loop(Code, Vid, OH, Avps) ->
%%
%% Send a locally originating reply.
+%% Skip the setting of Result-Code and Failed-AVP's below.
+reply([Msg], Dict, TPid, Pkt)
+ when is_list(Msg);
+ is_tuple(Msg) ->
+ reply(Msg, Dict, TPid, Pkt#diameter_packet{errors = []});
+
%% No errors or a diameter_header/avp list.
reply(Msg, Dict, TPid, #diameter_packet{errors = Es,
transport_data = TD}
@@ -1942,7 +2002,7 @@ reply(Msg, Dict, TPid, #diameter_packet{errors = Es,
when [] == Es;
is_record(hd(Msg), diameter_header) ->
Pkt = diameter_codec:encode(Dict, make_answer_packet(Msg, ReqPkt)),
- incr(send, Pkt, TPid), %% count result codes in sent answers
+ incr(send, Pkt, Dict, TPid), %% count result codes in sent answers
send(TPid, Pkt#diameter_packet{transport_data = TD});
%% Or not: set Result-Code and Failed-AVP AVP's.
@@ -1983,7 +2043,10 @@ rc(RC) ->
rc(Rec, RC, Failed, Dict)
when is_integer(RC) ->
- set(Rec, [{'Result-Code', RC} | failed_avp(Rec, Failed, Dict)], Dict).
+ set(Rec,
+ lists:append([rc(Rec, {'Result-Code', RC}, Dict),
+ failed_avp(Rec, Failed, Dict)]),
+ Dict).
%% Reply as name and tuple list ...
set([_|_] = Ans, Avps, _) ->
@@ -1993,6 +2056,22 @@ set([_|_] = Ans, Avps, _) ->
set(Rec, Avps, Dict) ->
Dict:'#set-'(Avps, Rec).
+%% rc/3
+%%
+%% Turn the result code into a list if its optional and only set it if
+%% the arity is 1 or {0,1}. In other cases (which probably shouldn't
+%% exist in practise) we can't know what's appropriate.
+
+rc([MsgName | _], {'Result-Code' = K, RC} = T, Dict) ->
+ case Dict:avp_arity(MsgName, 'Result-Code') of
+ 1 -> [T];
+ {0,1} -> [{K, [RC]}];
+ _ -> []
+ end;
+
+rc(Rec, T, Dict) ->
+ rc([Dict:rec2msg(element(1, Rec))], T, Dict).
+
%% failed_avp/3
failed_avp(_, [] = No, _) ->
@@ -2200,44 +2279,39 @@ handle_answer(SvcName, _, {error, Req, Reason}) ->
handle_answer(SvcName,
AnswerErrors,
{answer, #request{dictionary = Dict} = Req, Pkt}) ->
- a(examine(diameter_codec:decode(Dict, Pkt)),
- SvcName,
- AnswerErrors,
- Req).
+ answer(examine(diameter_codec:decode(Dict, Pkt)),
+ SvcName,
+ AnswerErrors,
+ Req).
%% We don't really need to do a full decode if we're a relay and will
%% just resend with a new hop by hop identifier, but might a proxy
%% want to examine the answer?
-a(#diameter_packet{errors = []}
- = Pkt,
- SvcName,
- AE,
- #request{transport = TPid,
- caps = Caps,
- packet = P}
- = Req) ->
+answer(Pkt, SvcName, AE, #request{transport = TPid,
+ dictionary = Dict}
+ = Req) ->
try
- incr(in, Pkt, TPid)
+ incr(recv, Pkt, Dict, TPid)
of
- _ ->
- cb(Req, handle_answer, [Pkt, msg(P), SvcName, {TPid, Caps}])
+ _ -> a(Pkt, SvcName, AE, Req)
catch
exit: {invalid_error_bit, _} = E ->
- e(Pkt#diameter_packet{errors = [E]}, SvcName, AE, Req)
- end;
-
-a(#diameter_packet{} = Pkt, SvcName, AE, Req) ->
- e(Pkt, SvcName, AE, Req).
+ a(Pkt#diameter_packet{errors = [E]}, SvcName, AE, Req)
+ end.
-e(Pkt, SvcName, callback, #request{transport = TPid,
- caps = Caps,
- packet = Pkt}
- = Req) ->
- cb(Req, handle_answer, [Pkt, msg(Pkt), SvcName, {TPid, Caps}]);
-e(Pkt, SvcName, report, Req) ->
+a(#diameter_packet{errors = Es} = Pkt, SvcName, AE, #request{transport = TPid,
+ caps = Caps,
+ packet = P}
+ = Req)
+ when [] == Es;
+ callback == AE ->
+ cb(Req, handle_answer, [Pkt, msg(P), SvcName, {TPid, Caps}]);
+
+a(Pkt, SvcName, report, Req) ->
x(errors, handle_answer, [SvcName, Req, Pkt]);
-e(Pkt, SvcName, discard, Req) ->
+
+a(Pkt, SvcName, discard, Req) ->
x({errors, handle_answer, [SvcName, Req, Pkt]}).
%% Note that we don't check that the application id in the answer's
@@ -2249,17 +2323,19 @@ e(Pkt, SvcName, discard, Req) ->
%% Increment a stats counter for an incoming or outgoing message.
%% TODO: fix
-incr(_, #diameter_packet{msg = undefined}, _) ->
+incr(_, #diameter_packet{msg = undefined}, _, _) ->
ok;
-incr(Dir, Pkt, TPid)
- when is_pid(TPid) ->
+incr(recv = D, #diameter_packet{header = H, errors = [_|_]}, _, TPid) ->
+ incr(TPid, {diameter_codec:msg_id(H), D, error});
+
+incr(Dir, Pkt, Dict, TPid) ->
#diameter_packet{header = #diameter_header{is_error = E}
= Hdr,
msg = Rec}
= Pkt,
- RC = int(get_avp_value(?BASE, 'Result-Code', Rec)),
+ RC = int(get_avp_value(Dict, 'Result-Code', Rec)),
PE = is_protocol_error(RC),
%% Check that the E bit is set only for 3xxx result codes.
@@ -2267,15 +2343,21 @@ incr(Dir, Pkt, TPid)
orelse (E andalso PE)
orelse x({invalid_error_bit, RC}, answer, [Dir, Pkt]),
- Ctr = rc_counter(Rec, RC),
- is_tuple(Ctr)
- andalso incr(TPid, {diameter_codec:msg_id(Hdr), Dir, Ctr}).
+ irc(TPid, Hdr, Dir, rc_counter(Dict, Rec, RC)).
+
+irc(_, _, _, undefined) ->
+ false;
+
+irc(TPid, Hdr, Dir, Ctr) ->
+ incr(TPid, {diameter_codec:msg_id(Hdr), Dir, Ctr}).
%% incr/2
incr(TPid, Counter) ->
diameter_stats:incr(Counter, TPid, 1).
+%% error_counter/2
+
%% RFC 3588, 7.6:
%%
%% All Diameter answer messages defined in vendor-specific
@@ -2285,26 +2367,27 @@ incr(TPid, Counter) ->
%% Maintain statistics assuming one or the other, not both, which is
%% surely the intent of the RFC.
-rc_counter(_, RC)
- when is_integer(RC) ->
- {'Result-Code', RC};
-rc_counter(Rec, _) ->
- rcc(get_avp_value(?BASE, 'Experimental-Result', Rec)).
+rc_counter(Dict, Rec, undefined) ->
+ er(get_avp_value(Dict, 'Experimental-Result', Rec));
+rc_counter(_, _, RC) ->
+ {'Result-Code', RC}.
%% Outgoing answers may be in any of the forms messages can be sent
%% in. Incoming messages will be records. We're assuming here that the
%% arity of the result code AVP's is 0 or 1.
-rcc([{_,_,RC} = T])
- when is_integer(RC) ->
+er([{_,_,N} = T | _])
+ when is_integer(N) ->
T;
-rcc({_,_,RC} = T)
- when is_integer(RC) ->
+er({_,_,N} = T)
+ when is_integer(N) ->
T;
-rcc(_) ->
+er(_) ->
undefined.
-int([N])
+%% Extract the first good looking integer. There's no guarantee
+%% that what we're looking for has arity 1.
+int([N|_])
when is_integer(N) ->
N;
int(N)
@@ -2349,8 +2432,11 @@ rt(#request{packet = #diameter_packet{msg = undefined}}, _) ->
false; %% TODO: Not what we should do.
%% ... or not.
-rt(#request{packet = #diameter_packet{msg = Msg}} = Req, S) ->
- find_transport(get_destination(Msg), Req, S).
+rt(#request{packet = #diameter_packet{msg = Msg},
+ dictionary = Dict}
+ = Req,
+ S) ->
+ find_transport(get_destination(Dict, Msg), Req, S).
%%% ---------------------------------------------------------------------------
%%% # report_status/5
@@ -2462,12 +2548,12 @@ find_transport({alias, Alias}, Msg, Opts, #state{service = Svc} = S) ->
find_transport(#diameter_app{} = App, Msg, Opts, S) ->
ft(App, Msg, Opts, S).
-ft(#diameter_app{module = Mod} = App, Msg, Opts, S) ->
+ft(#diameter_app{module = Mod, dictionary = Dict} = App, Msg, Opts, S) ->
#options{filter = Filter,
extra = Xtra}
= Opts,
pick_peer(App#diameter_app{module = Mod ++ Xtra},
- get_destination(Msg),
+ get_destination(Dict, Msg),
Filter,
S);
ft(false = No, _, _, _) ->
@@ -2503,11 +2589,11 @@ find_transport([_,_] = RH,
Filter,
S).
-%% get_destination/1
+%% get_destination/2
-get_destination(Msg) ->
- [str(get_avp_value(?BASE, 'Destination-Realm', Msg)),
- str(get_avp_value(?BASE, 'Destination-Host', Msg))].
+get_destination(Dict, Msg) ->
+ [str(get_avp_value(Dict, 'Destination-Realm', Msg)),
+ str(get_avp_value(Dict, 'Destination-Host', Msg))].
%% This is not entirely correct. The avp could have an arity 1, in
%% which case an empty list is a DiameterIdentity of length 0 rather
@@ -2531,6 +2617,15 @@ str(T) ->
%% question. The third form allows messages to be sent as is, without
%% a dictionary, which is needed in the case of relay agents, for one.
+%% Messages will be header/avps list as a relay and the only AVP's we
+%% look for are in the common dictionary. This is required since the
+%% relay dictionary doesn't inherit the common dictionary (which maybe
+%% it should).
+get_avp_value(?RELAY, Name, Msg) ->
+ get_avp_value(?BASE, Name, Msg);
+
+%% Message sent as a header/avps list, probably a relay case but not
+%% necessarily.
get_avp_value(Dict, Name, [#diameter_header{} | Avps]) ->
try
{Code, _, VId} = Dict:avp_header(Name),
@@ -2544,6 +2639,7 @@ get_avp_value(Dict, Name, [#diameter_header{} | Avps]) ->
undefined
end;
+%% Outgoing message as a name/values list.
get_avp_value(_, Name, [_MsgName | Avps]) ->
case lists:keyfind(Name, 1, Avps) of
{_, V} ->
@@ -2552,6 +2648,11 @@ get_avp_value(_, Name, [_MsgName | Avps]) ->
undefined
end;
+%% Record might be an answer message in the common dictionary.
+get_avp_value(Dict, Name, Rec)
+ when Dict /= ?BASE, element(1, Rec) == 'diameter_base_answer-message' ->
+ get_avp_value(?BASE, Name, Rec);
+
%% Message is typically a record but not necessarily: diameter:call/4
%% can be passed an arbitrary term.
get_avp_value(Dict, Name, Rec) ->
@@ -2746,20 +2847,45 @@ transports(#state{peerT = PeerT}) ->
'Vendor-Specific-Application-Id',
'Firmware-Revision']).
+%% The config returned by diameter:service_info(SvcName, all).
-define(ALL_INFO, [capabilities,
applications,
transport,
- pending,
- statistics]).
+ pending]).
+
+%% The rest.
+-define(OTHER_INFO, [connections,
+ name,
+ peers,
+ statistics]).
-service_info(Items, S)
- when is_list(Items) ->
- [{complete(I), service_info(I,S)} || I <- Items];
service_info(Item, S)
when is_atom(Item) ->
- service_info(Item, S, true).
+ case tagged_info(Item, S) of
+ {_, T} -> T;
+ undefined = No -> No
+ end;
-service_info(Item, #state{service = Svc} = S, Complete) ->
+service_info(Items, S) ->
+ tagged_info(Items, S).
+
+tagged_info(Item, S)
+ when is_atom(Item) ->
+ case complete(Item) of
+ {value, I} ->
+ {I, complete_info(I,S)};
+ false ->
+ undefined
+ end;
+
+tagged_info(Items, S)
+ when is_list(Items) ->
+ [T || I <- Items, T <- [tagged_info(I,S)], T /= undefined, T /= []];
+
+tagged_info(_, _) ->
+ undefined.
+
+complete_info(Item, #state{service = Svc} = S) ->
case Item of
name ->
S#state.service_name;
@@ -2803,84 +2929,160 @@ service_info(Item, #state{service = Svc} = S, Complete) ->
applications -> info_apps(S);
transport -> info_transport(S);
pending -> info_pending(S);
- statistics -> info_stats(S);
- keys -> ?ALL_INFO ++ ?CAP_INFO; %% mostly for test
+ keys -> ?ALL_INFO ++ ?CAP_INFO ++ ?OTHER_INFO;
all -> service_info(?ALL_INFO, S);
- _ when Complete -> service_info(complete(Item), S, false);
- _ -> undefined
+ statistics -> info_stats(S);
+ connections -> info_connections(S);
+ peers -> info_peers(S)
end.
+complete(I)
+ when I == keys;
+ I == all ->
+ {value, I};
complete(Pre) ->
P = atom_to_list(Pre),
- case [I || I <- [name | ?ALL_INFO] ++ ?CAP_INFO,
+ case [I || I <- ?ALL_INFO ++ ?CAP_INFO ++ ?OTHER_INFO,
lists:prefix(P, atom_to_list(I))]
of
- [I] -> I;
- _ -> Pre
+ [I] -> {value, I};
+ _ -> false
end.
+%% info_stats/1
+
info_stats(#state{peerT = PeerT}) ->
- Peers = ets:select(PeerT, [{#peer{ref = '$1', conn = '$2', _ = '_'},
- [{'is_pid', '$2'}],
- [['$1', '$2']]}]),
- diameter_stats:read(lists:append(Peers)).
-%% TODO: include peer identities in return value
-
-info_transport(#state{peerT = PeerT, connT = ConnT}) ->
- dict:fold(fun it/3,
+ MatchSpec = [{#peer{ref = '$1', conn = '$2', _ = '_'},
+ [{'is_pid', '$2'}],
+ [['$1', '$2']]}],
+ diameter_stats:read(lists:append(ets:select(PeerT, MatchSpec))).
+
+%% info_transport/1
+%%
+%% One entry per configured transport. Statistics for each entry are
+%% the accumulated values for the ref and associated peer pids.
+
+info_transport(S) ->
+ PeerD = peer_dict(S, config_dict(S)),
+ RefsD = dict:map(fun(_, Ls) -> [P || L <- Ls, {peer, {P,_}} <- L] end,
+ PeerD),
+ Refs = lists:append(dict:fold(fun(R, Ps, A) -> [[R|Ps] | A] end,
+ [],
+ RefsD)),
+ Stats = diameter_stats:read(Refs),
+ dict:fold(fun(R, Ls, A) ->
+ Ps = dict:fetch(R, RefsD),
+ [[{ref, R} | transport(Ls)] ++ [stats([R|Ps], Stats)]
+ | A]
+ end,
[],
- ets:foldl(fun(T,A) -> it_acc(ConnT, A, T) end,
- dict:new(),
- PeerT)).
-
-it(Ref, [[{type, connect} | _] = L], Acc) ->
- [[{ref, Ref} | L] | Acc];
-it(Ref, [[{type, accept}, {options, Opts} | _] | _] = L, Acc) ->
- [[{ref, Ref},
- {type, listen},
- {options, Opts},
- {accept, [lists:nthtail(2,A) || A <- L]}]
- | Acc].
-%% Each entry has the same Opts. (TODO)
-
-it_acc(ConnT, Acc, #peer{pid = Pid,
- type = Type,
- ref = Ref,
- options = Opts,
- op_state = OS,
- started = T,
- conn = TPid}) ->
+ PeerD).
+
+%% Only a config entry for a listening transport: use it.
+transport([[{type, listen}, _] = L]) ->
+ L ++ [{accept, []}];
+
+%% Only one config or peer entry for a connecting transport: use it.
+transport([[{type, connect} | _] = L]) ->
+ L;
+
+%% Peer entries: discard config. Note that the peer entries have
+%% length at least 3.
+transport([[_,_] | L]) ->
+ transport(L);
+
+%% Possibly many peer entries for a listening transport. Note that all
+%% have the same options by construction, which is not terribly space
+%% efficient. (TODO: all entries for the same Ref should share options.)
+transport([[{type, accept}, {options, Opts} | _] | _] = Ls) ->
+ [{type, listen},
+ {options, Opts},
+ {accept, [lists:nthtail(2,L) || L <- Ls]}].
+
+peer_dict(#state{peerT = PeerT, connT = ConnT}, Dict0) ->
+ ets:foldl(fun(T,A) -> peer_acc(ConnT, A, T) end, Dict0, PeerT).
+
+peer_acc(ConnT, Acc, #peer{pid = Pid,
+ type = Type,
+ ref = Ref,
+ options = Opts,
+ op_state = OS,
+ started = T,
+ conn = TPid}) ->
+ WS = wd_state(OS),
dict:append(Ref,
[{type, Type},
{options, Opts},
- {watchdog, {Pid, T, OS}}
- | info_conn(ConnT, TPid)],
+ {watchdog, {Pid, T, WS}}
+ | info_conn(ConnT, TPid, WS /= ?WD_DOWN)],
Acc).
-info_conn(ConnT, TPid) ->
- info_conn(ets:lookup(ConnT, TPid)).
+info_conn(ConnT, TPid, true)
+ when is_pid(TPid) ->
+ info_conn(ets:lookup(ConnT, TPid));
+info_conn(_, _, _) ->
+ [].
+
+%% The point of extracting the config here is so that 'transport' info
+%% has one entry for each transport ref, the peer table only
+%% containing entries that have a living watchdog.
+
+config_dict(#state{service_name = SvcName}) ->
+ lists:foldl(fun config_acc/2,
+ dict:new(),
+ diameter_config:lookup(SvcName)).
+
+config_acc({Ref, T, Opts}, Dict)
+ when T == listen;
+ T == connect ->
+ dict:store(Ref, [[{type, T}, {options, Opts}]], Dict);
+config_acc(_, Dict) ->
+ Dict.
+
+wd_state({_,S}) ->
+ S;
+wd_state(?STATE_UP) ->
+ ?WD_OKAY;
+wd_state(?STATE_DOWN) ->
+ ?WD_DOWN.
info_conn([#conn{pid = Pid, apps = SApps, caps = Caps, started = T}]) ->
[{peer, {Pid, T}},
{apps, SApps},
- {caps, info_caps(Caps)}];
+ {caps, info_caps(Caps)}
+ | try [{port, info_port(Pid)}] catch _:_ -> [] end];
info_conn([] = No) ->
No.
+%% Extract information that the processes involved are expected to
+%% "publish" in their process dictionaries. Simple but backhanded.
+info_port(Pid) ->
+ {_, PD} = process_info(Pid, dictionary),
+ {_, T} = lists:keyfind({diameter_peer_fsm, start}, 1, PD),
+ {TPid, {_Type, TMod, _Cfg}} = T,
+ {_, TD} = process_info(TPid, dictionary),
+ {_, Data} = lists:keyfind({TMod, info}, 1, TD),
+ [{owner, TPid},
+ {module, TMod}
+ | try TMod:info(Data) catch _:_ -> [] end].
+
+%% Use the fields names from diameter_caps instead of
+%% diameter_base_CER to distinguish between the 2-tuple values
+%% compared to the single capabilities values. Note also that the
+%% returned list is tagged 'caps' rather than 'capabilities' to
+%% emphasize the difference.
info_caps(#diameter_caps{} = C) ->
lists:zip(record_info(fields, diameter_caps), tl(tuple_to_list(C))).
info_apps(#state{service = #diameter_service{applications = Apps}}) ->
lists:map(fun mk_app/1, Apps).
-mk_app(#diameter_app{alias = Alias,
- dictionary = Dict,
- module = ModX,
- id = Id}) ->
- [{alias, Alias},
- {dictionary, Dict},
- {module, ModX},
- {id, Id}].
+mk_app(#diameter_app{} = A) ->
+ lists:zip(record_info(fields, diameter_app), tl(tuple_to_list(A))).
+
+%% info_pending/1
+%%
+%% One entry for each outgoing request whose answer is outstanding.
info_pending(#state{} = S) ->
MatchSpec = [{{'$1',
@@ -2895,3 +3097,57 @@ info_pending(#state{} = S) ->
{{from, '$3'}}]}}]}],
ets:select(?REQUEST_TABLE, MatchSpec).
+
+%% info_connections/1
+%%
+%% One entry per transport connection. Statistics for each entry are
+%% for the peer pid only.
+
+info_connections(S) ->
+ ConnL = conn_list(S),
+ Stats = diameter_stats:read([P || L <- ConnL, {peer, {P,_}} <- L]),
+ [L ++ [stats([P], Stats)] || L <- ConnL, {peer, {P,_}} <- L].
+
+conn_list(S) ->
+ lists:append(dict:fold(fun conn_acc/3, [], peer_dict(S, dict:new()))).
+
+conn_acc(Ref, Peers, Acc) ->
+ [[[{ref, Ref} | L] || L <- Peers, lists:keymember(peer, 1, L)]
+ | Acc].
+
+stats(Refs, Stats) ->
+ {statistics, dict:to_list(lists:foldl(fun(R,D) ->
+ stats_acc(R, D, Stats)
+ end,
+ dict:new(),
+ Refs))}.
+
+stats_acc(Ref, Dict, Stats) ->
+ lists:foldl(fun({C,N}, D) -> dict:update_counter(C, N, D) end,
+ Dict,
+ proplists:get_value(Ref, Stats, [])).
+
+%% info_peers/1
+%%
+%% One entry per peer Origin-Host. Statistics for each entry are
+%% accumulated values for all peer pids.
+
+info_peers(S) ->
+ {PeerD, RefD} = lists:foldl(fun peer_acc/2,
+ {dict:new(), dict:new()},
+ conn_list(S)),
+ Refs = lists:append(dict:fold(fun(_, Rs, A) -> [Rs|A] end,
+ [],
+ RefD)),
+ Stats = diameter_stats:read(Refs),
+ dict:fold(fun(OH, Cs, A) ->
+ Rs = dict:fetch(OH, RefD),
+ [{OH, [{connections, Cs}, stats(Rs, Stats)]} | A]
+ end,
+ [],
+ PeerD).
+
+peer_acc(Peer, {PeerD, RefD}) ->
+ [{TPid, _}, [{origin_host, {_, OH}} | _]]
+ = [proplists:get_value(K, Peer) || K <- [peer, caps]],
+ {dict:append(OH, Peer, PeerD), dict:append(OH, TPid, RefD)}.
diff --git a/lib/diameter/src/base/diameter_stats.erl b/lib/diameter/src/base/diameter_stats.erl
index 71479afa95..70727d068e 100644
--- a/lib/diameter/src/base/diameter_stats.erl
+++ b/lib/diameter/src/base/diameter_stats.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -22,14 +22,13 @@
%%
-module(diameter_stats).
--compile({no_auto_import, [monitor/2]}).
-behaviour(gen_server).
--export([reg/1, reg/2,
- incr/1, incr/2, incr/3,
+-export([reg/2, reg/1,
+ incr/3, incr/1,
read/1,
- flush/0, flush/1]).
+ flush/1]).
%% supervisor callback
-export([start_link/0]).
@@ -48,123 +47,105 @@
-include("diameter_internal.hrl").
-%% ets table containing stats. reg(Pid, Ref) inserts a {Pid, Ref},
-%% incr(Counter, X, N) updates the counter keyed at {Counter, X}, and
-%% Pid death causes counters keyed on {Counter, Pid} to be deleted and
-%% added to those keyed on {Counter, Ref}.
+%% ets table containing 2-tuple stats. reg(Pid, Ref) inserts a {Pid,
+%% Ref}, incr(Counter, X, N) updates the counter keyed at {Counter,
+%% X}, and Pid death causes counters keyed on {Counter, Pid} to be
+%% deleted and added to those keyed on {Counter, Ref}.
-define(TABLE, ?MODULE).
%% Name of registered server.
-define(SERVER, ?MODULE).
-%% Entries in the table.
--define(REC(Key, Value), {Key, Value}).
-
%% Server state.
-record(state, {id = now()}).
-type counter() :: any().
--type contrib() :: any().
-
-%%% ---------------------------------------------------------------------------
-%%% # reg(Pid, Contrib)
-%%%
-%%% Description: Register a process as a contributor of statistics
-%%% associated with a specified term. Statistics can be
-%%% contributed by specifying either Pid or Contrib as
-%%% the second argument to incr/3. Statistics contributed
-%%% by Pid are folded into the corresponding entry for
-%%% Contrib when the process dies.
-%%%
-%%% Contrib can be any term but should not be a pid
-%%% passed as the first argument to reg/2. Subsequent
-%%% registrations for the same Pid overwrite the association
-%%% ---------------------------------------------------------------------------
-
--spec reg(pid(), contrib())
- -> true.
+-type ref() :: any().
+
+%% ---------------------------------------------------------------------------
+%% # reg(Pid, Ref)
+%%
+%% Register a process as a contributor of statistics associated with a
+%% specified term. Statistics can be contributed by specifying either
+%% Pid or Ref as the second argument to incr/3. Statistics contributed
+%% by Pid are folded into the corresponding entry for Ref when the
+%% process dies.
+%% ---------------------------------------------------------------------------
+
+-spec reg(pid(), ref())
+ -> boolean().
-reg(Pid, Contrib)
+reg(Pid, Ref)
when is_pid(Pid) ->
- call({reg, Pid, Contrib}).
+ call({reg, Pid, Ref}).
--spec reg(contrib())
+-spec reg(ref())
-> true.
reg(Ref) ->
reg(self(), Ref).
-%%% ---------------------------------------------------------------------------
-%%% # incr(Counter, Contrib, N)
-%%%
-%%% Description: Increment a counter for the specified contributor.
-%%%
-%%% Contrib will typically be an argument passed to reg/2
-%%% but there's nothing that requires this. In particular,
-%%% if Contrib is a pid that hasn't been registered then
-%%% counters are unaffected by the death of the process.
-%%% ---------------------------------------------------------------------------
-
--spec incr(counter(), contrib(), integer())
- -> integer().
+%% ---------------------------------------------------------------------------
+%% # incr(Counter, Ref, N)
+%%
+%% Increment a counter for the specified contributor.
+%%
+%% Ref will typically be an argument passed to reg/2 but there's
+%% nothing that requires this. Only registered pids can contribute
+%% counters however, otherwise incr/3 is a no-op.
+%% ---------------------------------------------------------------------------
-incr(Ctr, Contrib, N) ->
- update_counter({Ctr, Contrib}, N).
+-spec incr(counter(), ref(), integer())
+ -> integer() | false.
-incr(Ctr, N)
+incr(Ctr, Ref, N)
when is_integer(N) ->
- incr(Ctr, self(), N);
-
-incr(Ctr, Contrib) ->
- incr(Ctr, Contrib, 1).
+ update_counter({Ctr, Ref}, N).
incr(Ctr) ->
incr(Ctr, self(), 1).
-%%% ---------------------------------------------------------------------------
-%%% # read(Contribs)
-%%%
-%%% Description: Retrieve counters for the specified contributors.
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # read(Refs)
+%%
+%% Retrieve counters for the specified contributors.
+%% ---------------------------------------------------------------------------
--spec read([contrib()])
- -> [{contrib(), [{counter(), integer()}]}].
+-spec read([ref()])
+ -> [{ref(), [{counter(), integer()}]}].
-read(Contribs) ->
- lists:foldl(fun(?REC({T,C}, N), D) -> orddict:append(C, {T,N}, D) end,
+read(Refs) ->
+ read(Refs, false).
+
+read(Refs, B) ->
+ MatchSpec = [{{{'_', '$1'}, '_'},
+ [?ORCOND([{'=:=', '$1', {const, R}}
+ || R <- Refs])],
+ ['$_']}],
+ L = ets:select(?TABLE, MatchSpec),
+ B andalso delete(L),
+ lists:foldl(fun({{C,R}, N}, D) -> orddict:append(R, {C,N}, D) end,
orddict:new(),
- ets:select(?TABLE, [{?REC({'_', '$1'}, '_'),
- [?ORCOND([{'=:=', '$1', {const, C}}
- || C <- Contribs])],
- ['$_']}])).
-
-%%% ---------------------------------------------------------------------------
-%%% # flush(Contrib)
-%%%
-%%% Description: Retrieve and delete statistics for the specified
-%%% contributor.
-%%%
-%%% If Contrib is a pid registered with reg/2 then statistics
-%%% for both and its associated contributor are retrieved.
-%%% ---------------------------------------------------------------------------
-
--spec flush(contrib())
- -> [{counter(), integer()}].
+ L).
+
+%% ---------------------------------------------------------------------------
+%% # flush(Refs)
+%%
+%% Retrieve and delete statistics for the specified contributors.
+%% ---------------------------------------------------------------------------
+
+-spec flush([ref()])
+ -> [{ref(), {counter(), integer()}}].
-flush(Contrib) ->
+flush(Refs) ->
try
- call({flush, Contrib})
+ call({flush, Refs})
catch
exit: _ ->
[]
end.
-flush() ->
- flush(self()).
-
-%%% ---------------------------------------------------------
-%%% EXPORTED INTERNAL FUNCTIONS
-%%% ---------------------------------------------------------
+%% ===========================================================================
start_link() ->
ServerName = {local, ?SERVER},
@@ -179,18 +160,16 @@ state() ->
uptime() ->
call(uptime).
-%%% ----------------------------------------------------------
-%%% # init(_)
-%%%
-%%% Output: {ok, State}
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # init/1
+%% ----------------------------------------------------------
init([]) ->
ets:new(?TABLE, [named_table, ordered_set, public]),
{ok, #state{}}.
%% ----------------------------------------------------------
-%% handle_call(Request, From, State)
+%% # handle_call/3
%% ----------------------------------------------------------
handle_call(state, _, State) ->
@@ -199,31 +178,31 @@ handle_call(state, _, State) ->
handle_call(uptime, _, #state{id = Time} = State) ->
{reply, diameter_lib:now_diff(Time), State};
-handle_call({reg, Pid, Contrib}, _From, State) ->
- monitor(not ets:member(?TABLE, Pid), Pid),
- {reply, insert(?REC(Pid, Contrib)), State};
+handle_call({incr, T}, _, State) ->
+ {reply, update_counter(T), State};
-handle_call({flush, Contrib}, _From, State) ->
- {reply, fetch(Contrib), State};
+handle_call({reg, Pid, Ref}, _From, State) ->
+ B = ets:insert_new(?TABLE, {Pid, Ref}),
+ B andalso erlang:monitor(process, Pid),
+ {reply, B, State};
+
+handle_call({flush, Refs}, _From, State) ->
+ {reply, read(Refs, true), State};
handle_call(Req, From, State) ->
?UNEXPECTED([Req, From]),
{reply, nok, State}.
%% ----------------------------------------------------------
-%% handle_cast(Request, State)
+%% # handle_cast/2
%% ----------------------------------------------------------
-handle_cast({incr, Rec}, State) ->
- update_counter(Rec),
- {noreply, State};
-
handle_cast(Msg, State) ->
?UNEXPECTED([Msg]),
{noreply, State}.
%% ----------------------------------------------------------
-%% handle_info(Request, State)
+%% # handle_info/2
%% ----------------------------------------------------------
handle_info({'DOWN', _MRef, process, Pid, _}, State) ->
@@ -235,91 +214,62 @@ handle_info(Info, State) ->
{noreply, State}.
%% ----------------------------------------------------------
-%% terminate(Reason, State)
+%% # terminate/2
%% ----------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%% ----------------------------------------------------------
-%% code_change(OldVsn, State, Extra)
+%% # code_change/3
%% ----------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
-%%% ---------------------------------------------------------
-%%% INTERNAL FUNCTIONS
-%%% ---------------------------------------------------------
-
-%% monitor/2
-
-monitor(true, Pid) ->
- erlang:monitor(process, Pid);
-monitor(false = No, _) ->
- No.
+%% ===========================================================================
%% down/1
down(Pid) ->
- L = ets:match_object(?TABLE, ?REC({'_', Pid}, '_')),
- [?REC(_, Ref) = T] = lookup(Pid),
+ down(lookup(Pid), ets:match_object(?TABLE, {{'_', Pid}, '_'})).
+
+down([{_, Ref} = T], L) ->
fold(Ref, L),
- delete_object(T),
+ delete([T|L]);
+down([], L) -> %% flushed
delete(L).
-%% Fold Pid-based entries into Ref-based ones.
+%% Fold pid-based entries into ref-based ones.
fold(Ref, L) ->
- lists:foreach(fun(?REC({K, _}, V)) -> update_counter({{K, Ref}, V}) end,
- L).
-
-delete(Objs) ->
- lists:foreach(fun delete_object/1, Objs).
-
-%% fetch/1
-
-fetch(X) ->
- MatchSpec = [{?REC({'_', '$1'}, '_'),
- [?ORCOND([{'==', '$1', {const, T}} || T <- [X | ref(X)]])],
- ['$_']}],
- L = ets:select(?TABLE, MatchSpec),
- delete(L),
- D = lists:foldl(fun sum/2, dict:new(), L),
- dict:to_list(D).
-
-sum({{Ctr, _}, N}, Dict) ->
- dict:update(Ctr, fun(V) -> V+N end, N, Dict).
-
-ref(Pid)
- when is_pid(Pid) ->
- ets:select(?TABLE, [{?REC(Pid, '$1'), [], ['$1']}]);
-ref(_) ->
- [].
+ lists:foreach(fun({{K, _}, V}) -> update_counter({{K, Ref}, V}) end, L).
%% update_counter/2
%%
-%% From an arbitrary request process. Cast to the server process to
-%% insert a new element if the counter doesn't exists so that two
-%% processes don't do so simultaneously.
+%% From an arbitrary process. Call to the server process to insert a
+%% new element if the counter doesn't exists so that two processes
+%% don't insert simultaneously.
update_counter(Key, N) ->
try
ets:update_counter(?TABLE, Key, N)
catch
error: badarg ->
- cast({incr, ?REC(Key, N)})
+ call({incr, {Key, N}})
end.
%% update_counter/1
%%
-%% From the server process.
+%% From the server process, when update_counter/2 failed due to a
+%% non-existent entry.
-update_counter(?REC(Key, N) = T) ->
+update_counter({{_Ctr, Ref} = Key, N} = T) ->
try
ets:update_counter(?TABLE, Key, N)
catch
error: badarg ->
- insert(T)
+ (not is_pid(Ref) orelse ets:member(?TABLE, Ref))
+ andalso begin insert(T), N end
end.
insert(T) ->
@@ -328,13 +278,8 @@ insert(T) ->
lookup(Key) ->
ets:lookup(?TABLE, Key).
-delete_object(T) ->
- ets:delete_object(?TABLE, T).
-
-%% cast/1
-
-cast(Msg) ->
- gen_server:cast(?SERVER, Msg).
+delete(Objs) ->
+ lists:foreach(fun({K,_}) -> ets:delete(?TABLE, K) end, Objs).
%% call/1
diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl
index fb22fd8275..d7474e5c56 100644
--- a/lib/diameter/src/base/diameter_watchdog.erl
+++ b/lib/diameter/src/base/diameter_watchdog.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -54,7 +54,7 @@
%% number of DWAs received during reopen
%% end PCB
parent = self() :: pid(),
- transport :: pid(),
+ transport :: pid() | undefined,
tref :: reference(), %% reference for current watchdog timer
message_data}). %% term passed into diameter_service with message
@@ -64,6 +64,13 @@
%% that a failed capabilities exchange produces the desired exit
%% reason.
+-spec start(Type, {RecvData, [Opt], SvcName, #diameter_service{}})
+ -> {reference(), pid()}
+ when Type :: {connect|accept, diameter:transport_ref()},
+ RecvData :: term(),
+ Opt :: diameter:transport_opt(),
+ SvcName :: diameter:service_name().
+
start({_,_} = Type, T) ->
Ref = make_ref(),
{ok, Pid} = diameter_watchdog_sup:start_child({Ref, {Type, self(), T}}),
@@ -102,7 +109,7 @@ i({_, Pid, _} = T) -> %% from old code
erlang:monitor(process, Pid),
make_state(T).
-make_state({T, Pid, {ConnT,
+make_state({T, Pid, {RecvData,
Opts,
SvcName,
#diameter_service{applications = Apps,
@@ -116,7 +123,7 @@ make_state({T, Pid, {ConnT,
tw = proplists:get_value(watchdog_timer,
Opts,
?DEFAULT_TW_INIT),
- message_data = {ConnT, SvcName, Apps}}.
+ message_data = {RecvData, SvcName, Apps}}.
%% handle_call/3
@@ -134,14 +141,36 @@ handle_info(T, State) ->
case transition(T, State) of
ok ->
{noreply, State};
- #watchdog{status = X} = S ->
- ?LOGC(X =/= State#watchdog.status, transition, X),
+ #watchdog{} = S ->
+ event(State, S),
{noreply, S};
stop ->
?LOG(stop, T),
+ event(State, State#watchdog{status = down}),
{stop, {shutdown, T}, State}
end.
+event(#watchdog{status = T}, #watchdog{status = T}) ->
+ ok;
+
+event(#watchdog{transport = undefined}, #watchdog{transport = undefined}) ->
+ ok;
+
+event(#watchdog{status = From, transport = F, parent = Pid},
+ #watchdog{status = To, transport = T}) ->
+ E = {tpid(F,T), From, To},
+ notify(Pid, E),
+ ?LOG(transition, {self(), E}).
+
+tpid(_, Pid)
+ when is_pid(Pid) ->
+ Pid;
+tpid(Pid, _) ->
+ Pid.
+
+notify(Pid, E) ->
+ Pid ! {watchdog, self(), E}.
+
%% terminate/2
terminate(_, _) ->
@@ -251,8 +280,8 @@ transition({'DOWN', _, process, TPid, _},
status = initial}) ->
stop;
-transition({'DOWN', _, process, Pid, _},
- #watchdog{transport = Pid}
+transition({'DOWN', _, process, TPid, _},
+ #watchdog{transport = TPid}
= S) ->
failover(S),
close(S),
@@ -385,7 +414,7 @@ recv(Name, Pkt, S) ->
rcv(Name, Pkt, S),
NS
catch
- throw: {?MODULE, throwaway, #watchdog{} = NS} ->
+ {?MODULE, throwaway, #watchdog{} = NS} ->
NS
end.
diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk
index 7a700a6d53..01f9284881 100644
--- a/lib/diameter/src/modules.mk
+++ b/lib/diameter/src/modules.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2011. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. 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
@@ -58,6 +58,7 @@ RT_MODULES = \
transport/diameter_tcp_sup \
transport/diameter_sctp \
transport/diameter_sctp_sup \
+ transport/diameter_transport \
transport/diameter_transport_sup
# Handwritten (compile time) modules not included in the app file.
diff --git a/lib/diameter/src/transport/diameter_etcp.erl b/lib/diameter/src/transport/diameter_etcp.erl
index d925d62545..cd62cf34fa 100644
--- a/lib/diameter/src/transport/diameter_etcp.erl
+++ b/lib/diameter/src/transport/diameter_etcp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -36,7 +36,9 @@
send/2,
close/1,
setopts/2,
- port/1]).
+ sockname/1,
+ peername/1,
+ getstat/1]).
%% child start
-export([start_link/1]).
@@ -113,10 +115,20 @@ close(Pid) ->
setopts(_, _) ->
ok.
-%% port/1
+%% sockname/1
-port(_) ->
- 3868. %% We have no local port: fake it.
+sockname(_) ->
+ {error, ?MODULE}.
+
+%% peername/1
+
+peername(_) ->
+ {error, ?MODULE}.
+
+%% getstat/1
+
+getstat(_) ->
+ {error, ?MODULE}.
%% start_link/1
diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl
index 68b0342cd5..79b8b851fb 100644
--- a/lib/diameter/src/transport/diameter_sctp.erl
+++ b/lib/diameter/src/transport/diameter_sctp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -37,12 +37,18 @@
code_change/3,
terminate/2]).
+-export([info/1]). %% service_info callback
+
-export([ports/0,
ports/1]).
-include_lib("kernel/include/inet_sctp.hrl").
-include_lib("diameter/include/diameter.hrl").
+%% Keys into process dictionary.
+-define(INFO_KEY, info).
+-define(REF_KEY, ref).
+
-define(ERROR(T), erlang:error({T, ?MODULE, ?LINE})).
%% The default port for a listener.
@@ -62,6 +68,7 @@
-record(transport,
{parent :: pid(),
mode :: {accept, pid()}
+ | accept
| {connect, {list(inet:ip_address()), uint(), list()}}
%% {RAs, RP, Errors}
| connect,
@@ -134,6 +141,24 @@ start_link(T) ->
diameter_lib:spawn_opts(server, [])).
%% ---------------------------------------------------------------------------
+%% # info/1
+%% ---------------------------------------------------------------------------
+
+info({gen_sctp, Sock}) ->
+ lists:flatmap(fun(K) -> info(K, Sock) end,
+ [{socket, sockname},
+ {peer, peername},
+ {statistics, getstat}]).
+
+info({K,F}, Sock) ->
+ case inet:F(Sock) of
+ {ok, V} ->
+ [{K,V}];
+ _ ->
+ []
+ end.
+
+%% ---------------------------------------------------------------------------
%% # init/1
%% ---------------------------------------------------------------------------
@@ -157,7 +182,7 @@ i({connect, Pid, Opts, Addrs, Ref}) ->
RAs = [diameter_lib:ipaddr(A) || {raddr, A} <- As],
[RP] = [P || {rport, P} <- Ps] ++ [P || P <- [?DEFAULT_PORT], [] == Ps],
{LAs, Sock} = open(Addrs, Rest, 0),
- putr(ref, Ref),
+ putr(?REF_KEY, Ref),
proc_lib:init_ack({ok, self(), LAs}),
erlang:monitor(process, Pid),
#transport{parent = Pid,
@@ -169,7 +194,7 @@ i({connect, _, _, _} = T) -> %% from old code
%% An accepting transport spawned by diameter.
i({accept, Pid, LPid, Sock, Ref})
when is_pid(Pid) ->
- putr(ref, Ref),
+ putr(?REF_KEY, Ref),
proc_lib:init_ack({ok, self()}),
erlang:monitor(process, Pid),
erlang:monitor(process, LPid),
@@ -181,7 +206,7 @@ i({accept, _, _, _} = T) -> %% from old code
%% An accepting transport spawned at association establishment.
i({accept, Ref, LPid, Sock, Id}) ->
- putr(ref, Ref),
+ putr(?REF_KEY, Ref),
proc_lib:init_ack({ok, self()}),
MRef = erlang:monitor(process, LPid),
%% Wait for a signal that the transport has been started before
@@ -325,6 +350,11 @@ terminate(_, #transport{assoc_id = undefined}) ->
ok;
terminate(_, #transport{socket = Sock,
+ mode = accept,
+ assoc_id = Id}) ->
+ close(Sock, Id);
+
+terminate(_, #transport{socket = Sock,
mode = {accept, _},
assoc_id = Id}) ->
close(Sock, Id);
@@ -356,13 +386,16 @@ start_timer(S) ->
%% Incoming message from SCTP.
l({sctp, Sock, _RA, _RP, Data} = Msg, #listener{socket = Sock} = S) ->
- setopts(Sock),
- case find(Data, S) of
+ Id = assoc_id(Data),
+
+ try find(Id, Data, S) of
{TPid, NewS} ->
- TPid ! Msg,
+ TPid ! {peeloff, peeloff(Sock, Id, TPid), Msg},
NewS;
false ->
S
+ after
+ setopts(Sock)
end;
%% Transport is asking message to be sent. See send/3 for why the send
@@ -430,15 +463,19 @@ t(T,S) ->
%% transition/2
-%% Incoming message.
-transition({sctp, Sock, _RA, _RP, Data}, #transport{socket = Sock,
- mode = {accept, _}}
- = S) ->
- recv(Data, S);
+%% Listening process is transfering ownership of an association.
+transition({peeloff, Sock, {sctp, LSock, _RA, _RP, _Data} = Msg},
+ #transport{mode = {accept, _},
+ socket = LSock}
+ = S) ->
+ transition(Msg, S#transport{socket = Sock});
-transition({sctp, Sock, _RA, _RP, Data}, #transport{socket = Sock} = S) ->
+%% Incoming message.
+transition({sctp, _Sock, _RA, _RP, Data}, #transport{socket = Sock} = S) ->
setopts(Sock),
recv(Data, S);
+%% Don't match on Sock since in R15B01 it can be the listening socket
+%% in the (peeled-off) accept case, which is likely a bug.
%% Outgoing message.
transition({diameter, {send, Msg}}, S) ->
@@ -456,13 +493,18 @@ transition({diameter, {close, Pid}}, #transport{parent = Pid}) ->
transition({diameter, {tls, _Ref, _Type, _Bool}}, _) ->
stop;
+%% Parent process has died.
+transition({'DOWN', _, process, Pid, _}, #transport{parent = Pid}) ->
+ stop;
+
%% Listener process has died.
transition({'DOWN', _, process, Pid, _}, #transport{mode = {accept, Pid}}) ->
stop;
-%% Parent process has died.
-transition({'DOWN', _, process, Pid, _}, #transport{parent = Pid}) ->
- stop;
+%% Ditto but we have ownership of the association. It might be that
+%% we'll go down anyway though.
+transition({'DOWN', _, process, _Pid, _}, #transport{mode = accept}) ->
+ ok;
%% Request for the local port number.
transition({resolve_port, Pid}, #transport{socket = Sock})
@@ -521,14 +563,6 @@ send(Bin, #transport{streams = {_, OS},
%% send/3
-%% Messages have to be sent from the controlling process, which is
-%% probably a bug. Sending from here causes an inet_reply, Sock,
-%% Status} message to be sent to the controlling process while
-%% gen_sctp:send/4 here hangs.
-send(StreamId, Bin, #transport{assoc_id = AId,
- mode = {accept, LPid}}) ->
- LPid ! {send, AId, StreamId, Bin};
-
send(StreamId, Bin, #transport{socket = Sock,
assoc_id = AId}) ->
send(Sock, AId, StreamId, Bin).
@@ -554,10 +588,9 @@ recv({_, #sctp_assoc_change{state = comm_up,
mode = {T, _},
socket = Sock}
= S) ->
- Ref = getr(ref),
+ Ref = getr(?REF_KEY),
is_reference(Ref) %% started in new code
- andalso
- (true = diameter_reg:add_new({?MODULE, T, {Ref, {Id, Sock}}})),
+ andalso publish(T, Ref, Id, Sock),
up(S#transport{assoc_id = Id,
streams = {IS, OS}});
@@ -599,6 +632,10 @@ recv({_, #sctp_paddr_change{}}, _) ->
recv({_, #sctp_pdapi_event{}}, _) ->
ok.
+publish(T, Ref, Id, Sock) ->
+ true = diameter_reg:add_new({?MODULE, T, {Ref, {Id, Sock}}}),
+ putr(?INFO_KEY, {gen_sctp, Sock}). %% for info/1
+
%% up/1
up(#transport{parent = Pid,
@@ -608,21 +645,15 @@ up(#transport{parent = Pid,
S#transport{mode = C};
up(#transport{parent = Pid,
- mode = {accept, _}}
+ mode = {accept = A, _}}
= S) ->
diameter_peer:up(Pid),
- S.
+ S#transport{mode = A}.
-%% find/2
-
-find({[#sctp_sndrcvinfo{assoc_id = Id}], _}
- = Data,
- #listener{tmap = T}
- = S) ->
- f(ets:lookup(T, Id), Data, S);
+%% find/3
-find({_, Rec} = Data, #listener{tmap = T} = S) ->
- f(ets:lookup(T, assoc_id(Rec)), Data, S).
+find(Id, Data, #listener{tmap = T} = S) ->
+ f(ets:lookup(T, Id), Data, S).
%% New association and a transport waiting for one: use it.
f([],
@@ -663,17 +694,29 @@ f([], _, _) ->
%% assoc_id/1
-assoc_id(#sctp_shutdown_event{assoc_id = Id}) ->
+assoc_id({[#sctp_sndrcvinfo{assoc_id = Id}], _}) ->
Id;
-assoc_id(#sctp_assoc_change{assoc_id = Id}) ->
+assoc_id({_, Rec}) ->
+ id(Rec).
+
+id(#sctp_shutdown_event{assoc_id = Id}) ->
+ Id;
+id(#sctp_assoc_change{assoc_id = Id}) ->
Id;
-assoc_id(#sctp_sndrcvinfo{assoc_id = Id}) ->
+id(#sctp_sndrcvinfo{assoc_id = Id}) ->
Id;
-assoc_id(#sctp_paddr_change{assoc_id = Id}) ->
+id(#sctp_paddr_change{assoc_id = Id}) ->
Id;
-assoc_id(#sctp_adaptation_event{assoc_id = Id}) ->
+id(#sctp_adaptation_event{assoc_id = Id}) ->
Id.
+%% peeloff/3
+
+peeloff(LSock, Id, TPid) ->
+ {ok, Sock} = gen_sctp:peeloff(LSock, Id),
+ ok = gen_sctp:controlling_process(Sock, TPid),
+ Sock.
+
%% connect/4
connect(_, [], _, Reasons) ->
diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl
index 78dbda6888..f3fbbee609 100644
--- a/lib/diameter/src/transport/diameter_tcp.erl
+++ b/lib/diameter/src/transport/diameter_tcp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -37,11 +37,17 @@
code_change/3,
terminate/2]).
+-export([info/1]). %% service_info callback
+
-export([ports/0,
ports/1]).
-include_lib("diameter/include/diameter.hrl").
+%% Keys into process dictionary.
+-define(INFO_KEY, info).
+-define(REF_KEY, ref).
+
-define(ERROR(T), erlang:error({T, ?MODULE, ?LINE})).
-define(DEFAULT_PORT, 3868). %% RFC 3588, ch 2.1
@@ -74,7 +80,7 @@
%% Accepting/connecting transport process state.
-record(transport,
- {socket :: inet:socket(), %% accept or connect socket
+ {socket :: inet:socket() | ssl:sslsock(), %% accept or connect socket
parent :: pid(), %% of process that started us
module :: module(), %% gen_tcp-like module
frag = <<>> :: binary() | {tref(), frag()}, %% message fragment
@@ -111,6 +117,33 @@ start_link(T) ->
diameter_lib:spawn_opts(server, [])).
%% ---------------------------------------------------------------------------
+%% # info/1
+%% ---------------------------------------------------------------------------
+
+info({Mod, Sock}) ->
+ lists:flatmap(fun(K) -> info(Mod, K, Sock) end,
+ [{socket, fun sockname/2},
+ {peer, fun peername/2},
+ {statistics, fun getstat/2}
+ | ssl_info(Mod, Sock)]).
+
+info(Mod, {K,F}, Sock) ->
+ case F(Mod, Sock) of
+ {ok, V} ->
+ [{K,V}];
+ _ ->
+ []
+ end.
+
+ssl_info(ssl = M, Sock) ->
+ [{M, ssl_info(Sock)}];
+ssl_info(_, _) ->
+ [].
+
+ssl_info(Sock) ->
+ [{peercert, C} || {ok, C} <- [ssl:peercert(Sock)]].
+
+%% ---------------------------------------------------------------------------
%% # init/1
%% ---------------------------------------------------------------------------
@@ -133,7 +166,7 @@ i({T, Ref, Mod, Pid, Opts, Addrs})
MPid ! {stop, self()}, %% tell the monitor to die
M = if SslOpts -> ssl; true -> Mod end,
setopts(M, Sock),
- putr(ref, Ref),
+ putr(?REF_KEY, Ref),
#transport{parent = Pid,
module = M,
socket = Sock,
@@ -191,7 +224,7 @@ i(accept = T, Ref, Mod, Pid, Opts, Addrs) ->
{LAddr, LSock} = listener(Ref, {Mod, Opts, Addrs}),
proc_lib:init_ack({ok, self(), [LAddr]}),
Sock = ok(accept(Mod, LSock)),
- true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}),
+ publish(Mod, T, Ref, Sock),
diameter_peer:up(Pid),
Sock;
@@ -202,10 +235,14 @@ i(connect = T, Ref, Mod, Pid, Opts, Addrs) ->
RPort = get_port(RP),
proc_lib:init_ack({ok, self(), [LAddr]}),
Sock = ok(connect(Mod, RAddr, RPort, gen_opts(LAddr, Rest))),
- true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}),
+ publish(Mod, T, Ref, Sock),
diameter_peer:up(Pid, {RAddr, RPort}),
Sock.
+publish(Mod, T, Ref, Sock) ->
+ true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}),
+ putr(?INFO_KEY, {Mod, Sock}). %% for info/1
+
ok({ok, T}) ->
T;
ok(No) ->
@@ -521,7 +558,7 @@ tls_handshake(Type, true, #transport{socket = Sock,
ssl = Opts}
= S) ->
{ok, SSock} = tls(Type, Sock, [{cb_info, ?TCP_CB(M)} | Opts]),
- Ref = getr(ref),
+ Ref = getr(?REF_KEY),
is_reference(Ref) %% started in new code
andalso
(true = diameter_reg:add_new({?MODULE, Type, {Ref, SSock}})),
@@ -696,12 +733,32 @@ setopts(M, Sock) ->
portnr(gen_tcp, Sock) ->
inet:port(Sock);
-portnr(ssl, Sock) ->
- case ssl:sockname(Sock) of
+portnr(M, Sock) ->
+ case M:sockname(Sock) of
{ok, {_Addr, PortNr}} ->
{ok, PortNr};
{error, _} = No ->
No
- end;
-portnr(M, Sock) ->
- M:port(Sock).
+ end.
+
+%% sockname/2
+
+sockname(gen_tcp, Sock) ->
+ inet:sockname(Sock);
+sockname(M, Sock) ->
+ M:sockname(Sock).
+
+%% peername/2
+
+peername(gen_tcp, Sock) ->
+ inet:peername(Sock);
+peername(M, Sock) ->
+ M:peername(Sock).
+
+%% getstat/2
+
+getstat(gen_tcp, Sock) ->
+ inet:getstat(Sock);
+getstat(M, Sock) ->
+ M:getstat(Sock).
+%% Note that ssl:getstat/1 doesn't yet exist in R15B01.
diff --git a/lib/diameter/src/transport/diameter_transport.erl b/lib/diameter/src/transport/diameter_transport.erl
new file mode 100644
index 0000000000..ff4b6bbc6d
--- /dev/null
+++ b/lib/diameter/src/transport/diameter_transport.erl
@@ -0,0 +1,55 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(diameter_transport).
+
+%%
+%% This module implements a transport start function that
+%% evaluates its config argument.
+%%
+
+%% Transport start functions
+-export([start/3,
+ select/3,
+ eval/3]).
+
+%% start/3
+
+%% Call a start function in this module ...
+start(T, Svc, {F,A}) ->
+ start(T, Svc, {?MODULE, F, [A]});
+
+%% ... or some other.
+start(T, Svc, F) ->
+ diameter_lib:eval([F, T, Svc]).
+
+%% select/3
+%%
+%% A start function that whose config argument is expected to return a
+%% new start function.
+
+select(T, Svc, F) ->
+ start(T, Svc, diameter_lib:eval([F, T, Svc])).
+
+%% eval/3
+%%
+%% A start function that simply evaluates its config argument.
+
+eval(_, _, F) ->
+ diameter_lib:eval(F).
diff --git a/lib/diameter/test/Makefile b/lib/diameter/test/Makefile
index 1659330a91..e3a57553c1 100644
--- a/lib/diameter/test/Makefile
+++ b/lib/diameter/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2011. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. 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
@@ -67,8 +67,13 @@ ERL_COMPILE_FLAGS += +warn_export_vars \
# Targets
# ----------------------------------------------------
+# Require success ...
all: opt
+# ... or not.
+any: opt
+ $(MAKE) -i $(SUITES)
+
run: $(SUITES)
debug opt: $(TARGET_FILES)
@@ -113,7 +118,7 @@ help:
@echo " Echo some relevant variables."
@echo ========================================
-.PHONY: all run clean debug docs help info opt realclean
+.PHONY: all any run clean debug docs help info opt realclean
# ----------------------------------------------------
# Special Targets
diff --git a/lib/diameter/test/diameter_capx_SUITE.erl b/lib/diameter/test/diameter_capx_SUITE.erl
index 54a161d606..ae128b8203 100644
--- a/lib/diameter/test/diameter_capx_SUITE.erl
+++ b/lib/diameter/test/diameter_capx_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -93,12 +93,12 @@
-define(fail(T), erlang:error({T, process_info(self(), messages)})).
--define(TIMEOUT, 2000).
+-define(TIMEOUT, 10000).
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() -> [start,
start_services,
diff --git a/lib/diameter/test/diameter_compiler_SUITE.erl b/lib/diameter/test/diameter_compiler_SUITE.erl
index 3b4c9706e0..4b792b5426 100644
--- a/lib/diameter/test/diameter_compiler_SUITE.erl
+++ b/lib/diameter/test/diameter_compiler_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -339,7 +339,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 5}}].
+ [{timetrap, {minutes, 2}}].
all() ->
[format,
diff --git a/lib/diameter/test/diameter_ct.erl b/lib/diameter/test/diameter_ct.erl
index f8ee3dc1d7..ded50bf6c5 100644
--- a/lib/diameter/test/diameter_ct.erl
+++ b/lib/diameter/test/diameter_ct.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -25,15 +25,14 @@
-export([run/1]).
-%% ct:run_test/1 is currently documented as returning a list of test
-%% results ... but no. Instead it returns 'ok' regardless of whether
-%% or not the suite in question has failed testcases.
+%% The makefile looks for signs of failure so ignore the ct:run_test/1
+%% return value.
run([Suite]) ->
Start = info(),
- ok = ct:run_test([{suite, Suite},
- {logdir, "./log"},
- {auto_compile, false}]),
+ ct:run_test([{suite, Suite},
+ {logdir, "./log"},
+ {auto_compile, false}]),
info(Start , info()).
info() ->
diff --git a/lib/diameter/test/diameter_dict_SUITE.erl b/lib/diameter/test/diameter_dict_SUITE.erl
index 5cf8506d3f..3cc65c0257 100644
--- a/lib/diameter/test/diameter_dict_SUITE.erl
+++ b/lib/diameter/test/diameter_dict_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -48,7 +48,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[{group, all},
diff --git a/lib/diameter/test/diameter_failover_SUITE.erl b/lib/diameter/test/diameter_failover_SUITE.erl
index 53398dd93e..ed31670031 100644
--- a/lib/diameter/test/diameter_failover_SUITE.erl
+++ b/lib/diameter/test/diameter_failover_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -101,7 +101,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[start,
diff --git a/lib/diameter/test/diameter_gen_sctp_SUITE.erl b/lib/diameter/test/diameter_gen_sctp_SUITE.erl
index 7f435a6b7a..2fde7b9fdb 100644
--- a/lib/diameter/test/diameter_gen_sctp_SUITE.erl
+++ b/lib/diameter/test/diameter_gen_sctp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -175,7 +175,8 @@ send(Sock, Id) ->
send_from_multiple_clients(_) ->
{S, Rs} = T = send_from_multiple_clients(8, 1024),
- {false, [], _} = {?FOREVER < S,
+ Max = ?FOREVER*1000,
+ {false, [], _} = {Max < S,
Rs -- [OI || {O,_} = OI <- Rs, is_integer(O)],
T}.
@@ -223,6 +224,11 @@ send_from_multiple_clients(_) ->
%% {134,100},
%% {117,98},
%% {149,125}]}
+%%
+%% This turns out to have been due to SCTP resends as a consequence of
+%% the listener having an insufficient recbuf. Increasing the size
+%% solves the problem.
+%%
send_from_multiple_clients(N, Sz)
when is_integer(N), 0 < N, is_integer(Sz), 0 < Sz ->
@@ -341,8 +347,15 @@ receive_what_was_sent(_Config) ->
%% open/0
open() ->
- gen_sctp:open([{ip, ?ADDR}, {port, 0}, {active, true}, binary]).
+ open([]).
+
+%% open/1
+open(Opts) ->
+ gen_sctp:open([{ip, ?ADDR}, {port, 0}, {active, true}, binary,
+ {recbuf, 1 bsl 16}, {sndbuf, 1 bsl 16}
+ | Opts]).
+
%% assoc/1
assoc(Sock) ->
diff --git a/lib/diameter/test/diameter_reg_SUITE.erl b/lib/diameter/test/diameter_reg_SUITE.erl
index ec6a0ca731..4939019f7a 100644
--- a/lib/diameter/test/diameter_reg_SUITE.erl
+++ b/lib/diameter/test/diameter_reg_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -43,7 +43,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[{group, all},
diff --git a/lib/diameter/test/diameter_relay_SUITE.erl b/lib/diameter/test/diameter_relay_SUITE.erl
index 70e1866791..f10d82bdf8 100644
--- a/lib/diameter/test/diameter_relay_SUITE.erl
+++ b/lib/diameter/test/diameter_relay_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -48,6 +48,7 @@
send_loop/1,
send_timeout_1/1,
send_timeout_2/1,
+ info/1,
disconnect/1,
stop_services/1,
stop/1]).
@@ -112,7 +113,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[start,
@@ -136,7 +137,8 @@ tc() ->
send4,
send_loop,
send_timeout_1,
- send_timeout_2].
+ send_timeout_2,
+ info].
%% ===========================================================================
%% start/stop testcases
@@ -224,6 +226,9 @@ send_timeout(Tmo) ->
{'Re-Auth-Request-Type', ?AUTHORIZE_ONLY}],
call(Req, [{filter, realm}, {timeout, Tmo}]).
+info(_Config) ->
+ [] = ?util:info().
+
%% ===========================================================================
realm(Host) ->
diff --git a/lib/diameter/test/diameter_stats_SUITE.erl b/lib/diameter/test/diameter_stats_SUITE.erl
index e7807fd360..8b7d8cb1b6 100644
--- a/lib/diameter/test/diameter_stats_SUITE.erl
+++ b/lib/diameter/test/diameter_stats_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -30,16 +30,17 @@
end_per_suite/1]).
%% testcases
--export([an/1,
- twa/1]).
+-export([reg/1,
+ incr/1,
+ read/1,
+ flush/1]).
-define(stat, diameter_stats).
--define(util, diameter_util).
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[{group, all},
@@ -49,8 +50,10 @@ groups() ->
[{all, [], tc()}].
tc() ->
- [an,
- twa].
+ [reg,
+ incr,
+ read,
+ flush].
init_per_suite(Config) ->
ok = diameter:start(),
@@ -61,25 +64,62 @@ end_per_suite(_Config) ->
%% ===========================================================================
-an(_) ->
- Ref = {'_', make_ref()},
+reg(_) ->
+ Ref = '$1',
true = ?stat:reg(Ref),
- true = ?stat:reg(Ref), %% duplicate
- ok = ?stat:incr(x),
- ok = ?stat:incr(x, Ref),
- ok = ?stat:incr(y, 2),
- ok = ?stat:incr(y, Ref),
- %% Flushing a pid flushes even stats on the registered reference.
- [{x,2},{y,3}] = lists:sort(?stat:flush()),
- [] = ?stat:flush(Ref),
- [] = ?stat:flush().
-
-twa(_) ->
+ false = ?stat:reg(Ref). %% duplicate
+
+incr(_) ->
+ Ref = '_',
+ Ctr = x,
+ false = ?stat:incr(Ctr), %% not registered,
+ 1 = ?stat:incr(Ctr, Ref, 1), %% only pids need register
+ true = ?stat:reg(Ref),
+ spawn(fun() ->
+ true = ?stat:reg(Ref),
+ 2 = ?stat:incr(Ctr, self(), 2)
+ end),
+ ok = fold(Ctr, Ref, 3), %% folded
+ ?stat:flush([self(), Ref]).
+
+read(_) ->
+ Ref = make_ref(),
+ C1 = {a,b},
+ C2 = {b,a},
+ true = ?stat:reg(Ref),
+ 1 = ?stat:incr(C1),
+ 1 = ?stat:incr(C2),
+ 2 = ?stat:incr(C1),
+ 7 = ?stat:incr(C1, Ref, 7),
+ Self = self(),
+ [{Ref, [{C1,7}]}, {Self, [{C1,2}, {C2,1}]}]
+ = lists:sort(?stat:read([self(), Ref, make_ref()])),
+ [] = ?stat:read([]),
+ [] = ?stat:read([make_ref()]),
+ ?stat:flush([self(), Ref, make_ref()]).
+
+flush(_) ->
Ref = make_ref(),
- ok = ?stat:incr(x, 8),
- ok = ?stat:incr(x, Ref, 7),
- %% Flushing a reference doesn't affect registered pids.
- [{x,7}] = ?stat:flush(Ref),
- [] = ?stat:flush(Ref),
- [{x,8}] = ?stat:flush(),
- [] = ?stat:flush().
+ Ctr = '_',
+ true = ?stat:reg(Ref),
+ 1 = ?stat:incr(Ctr),
+ 3 = ?stat:incr(Ctr, self(), 2),
+ 2 = ?stat:incr(Ctr, Ref, 2),
+ Self = self(),
+ [{Self, [{Ctr, 3}]}] = ?stat:flush([self()]),
+ 1 = ?stat:incr(Ctr),
+ [{Ref, [{Ctr, 2}]}] = ?stat:flush([Ref]),
+ [{Self, [{Ctr, 1}]}] = ?stat:flush([self()]),
+ [] = ?stat:flush([self(), Ref]).
+
+%% ===========================================================================
+
+%% Keep incremented until a fold results in the specified value.
+fold(Ctr, Ref, N) ->
+ case ?stat:incr(Ctr, Ref, 0) of
+ N ->
+ ok;
+ M when M < N ->
+ erlang:yield(),
+ fold(Ctr, Ref, N)
+ end.
diff --git a/lib/diameter/test/diameter_sync_SUITE.erl b/lib/diameter/test/diameter_sync_SUITE.erl
index ab629fb1c1..457efab8ae 100644
--- a/lib/diameter/test/diameter_sync_SUITE.erl
+++ b/lib/diameter/test/diameter_sync_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -43,7 +43,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[{group, all},
diff --git a/lib/diameter/test/diameter_tls_SUITE.erl b/lib/diameter/test/diameter_tls_SUITE.erl
index 85b953dc1a..6cc34b20c5 100644
--- a/lib/diameter/test/diameter_tls_SUITE.erl
+++ b/lib/diameter/test/diameter_tls_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -127,7 +127,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[start_ssl,
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index 6eed8d3b5d..669918f757 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -37,6 +37,7 @@
add_transports/1,
result_codes/1,
send_ok/1,
+ send_nok/1,
send_arbitrary/1,
send_unknown/1,
send_unknown_mandatory/1,
@@ -89,6 +90,7 @@
-include("diameter.hrl").
-include("diameter_gen_base_rfc3588.hrl").
+-include("diameter_gen_base_accounting.hrl").
%% ===========================================================================
@@ -101,11 +103,17 @@
-define(REALM, "erlang.org").
-define(HOST(Host, Realm), Host ++ [$.|Realm]).
--define(APP_ALIAS, base).
-define(EXTRA, an_extra_argument).
+
+-define(BASE, ?DIAMETER_DICT_COMMON).
+-define(ACCT, ?DIAMETER_DICT_ACCOUNTING).
+
+%% Run tests cases in different encoding variants. Send outgoing
+%% messages as lists or records.
-define(ENCODINGS, [list, record]).
--define(DICT, ?DIAMETER_DICT_COMMON).
+%% Not really what we should be setting unless the message is sent in
+%% the common application but diameter doesn't care.
-define(APP_ID, ?DIAMETER_APP_ID_COMMON).
%% Config for diameter:start_service/2.
@@ -115,11 +123,12 @@
{'Host-IP-Address', [?ADDR]},
{'Vendor-Id', 12345},
{'Product-Name', "OTP/diameter"},
- {'Acct-Application-Id', [?DIAMETER_APP_ID_COMMON]},
- {application, [{alias, ?APP_ALIAS},
- {dictionary, ?DIAMETER_DICT_COMMON},
- {module, ?MODULE},
- {answer_errors, callback}]}]).
+ {'Auth-Application-Id', [?DIAMETER_APP_ID_COMMON]},
+ {'Acct-Application-Id', [?DIAMETER_APP_ID_ACCOUNTING]}
+ | [{application, [{dictionary, D},
+ {module, ?MODULE},
+ {answer_errors, callback}]}
+ || D <- [?BASE, ?ACCT]]]).
-define(SUCCESS,
?'DIAMETER_BASE_RESULT-CODE_DIAMETER_SUCCESS').
@@ -131,6 +140,8 @@
?'DIAMETER_BASE_RESULT-CODE_DIAMETER_APPLICATION_UNSUPPORTED').
-define(INVALID_HDR_BITS,
?'DIAMETER_BASE_RESULT-CODE_DIAMETER_INVALID_HDR_BITS').
+-define(INVALID_AVP_BITS,
+ ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_INVALID_AVP_BITS').
-define(AVP_UNSUPPORTED,
?'DIAMETER_BASE_RESULT-CODE_DIAMETER_AVP_UNSUPPORTED').
-define(UNSUPPORTED_VERSION,
@@ -154,24 +165,25 @@
-define(A, list_to_atom).
-define(L, atom_to_list).
--define(P(N), ?A("p_" ++ ?L(N))).
+
+-define(NAME(A,B), ?A(?L(A) ++ "," ++ ?L(B))).
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 10}}].
+ [{timetrap, {seconds, 60}}].
all() ->
[start, start_services, add_transports, result_codes]
- ++ [{group, E, P} || E <- ?ENCODINGS, P <- [[], [parallel]]]
+ ++ [{group, name([E]), P} || E <- ?ENCODINGS, P <- [[], [parallel]]]
++ [remove_transports, stop_services, stop].
groups() ->
Ts = tc(),
- [{E, [], Ts} || E <- ?ENCODINGS].
+ [{name([E]), [], Ts} || E <- ?ENCODINGS].
init_per_group(Name, Config) ->
- [{encode, Name} | Config].
+ [{group, Name} | Config].
end_per_group(_, _) ->
ok.
@@ -186,6 +198,7 @@ end_per_testcase(_, _) ->
%% established.
tc() ->
[send_ok,
+ send_nok,
send_arbitrary,
send_unknown,
send_unknown_mandatory,
@@ -257,7 +270,7 @@ capx(_, #diameter_caps{origin_host = {OH,DH}}) ->
%% Ensure that result codes have the expected values.
result_codes(_Config) ->
- {2001, 3001, 3002, 3003, 3004, 3007, 3008, 5001, 5011}
+ {2001, 3001, 3002, 3003, 3004, 3007, 3008, 3009, 5001, 5011}
= {?SUCCESS,
?COMMAND_UNSUPPORTED,
?UNABLE_TO_DELIVER,
@@ -265,6 +278,7 @@ result_codes(_Config) ->
?TOO_BUSY,
?APPLICATION_UNSUPPORTED,
?INVALID_HDR_BITS,
+ ?INVALID_AVP_BITS,
?AVP_UNSUPPORTED,
?UNSUPPORTED_VERSION}.
@@ -272,7 +286,16 @@ result_codes(_Config) ->
send_ok(Config) ->
Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
{'Accounting-Record-Number', 1}],
- #diameter_base_ACA{'Result-Code' = ?SUCCESS}
+
+ #diameter_base_accounting_ACA{'Result-Code' = ?SUCCESS}
+ = call(Config, Req).
+
+%% Send an accounting ACR that the server answers badly to.
+send_nok(Config) ->
+ Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
+ {'Accounting-Record-Number', 0}],
+
+ #'diameter_base_answer-message'{'Result-Code' = ?INVALID_AVP_BITS}
= call(Config, Req).
%% Send an ASR with an arbitrary AVP and expect success and the same
@@ -324,7 +347,7 @@ send_unsupported(Config) ->
#'diameter_base_answer-message'{'Result-Code' = ?COMMAND_UNSUPPORTED}
= call(Config, Req).
-%% Send an unsupported command and expect 3007.
+%% Send an unsupported application and expect 3007.
send_unsupported_app(Config) ->
Req = ['STR', {'Termination-Cause', ?BAD_ANSWER}],
#'diameter_base_answer-message'{'Result-Code' = ?APPLICATION_UNSUPPORTED}
@@ -516,34 +539,57 @@ call(Config, Req) ->
call(Config, Req, Opts) ->
Name = proplists:get_value(testcase, Config),
- Enc = proplists:get_value(encode, Config),
+ [Enc] = name(proplists:get_value(group, Config)),
diameter:call(?CLIENT,
- ?APP_ALIAS,
- msg(Req, Enc),
+ dict(Req),
+ req(Req, Enc),
[{extra, [Name]} | Opts]).
-msg([_|_] = L, list) ->
- L;
-msg([H|T], record) ->
- ?DICT:'#new-'(?DICT:msg2rec(H), T);
-msg(T, _) ->
+req(['ACR' = H | T], record) ->
+ ?ACCT:'#new-'(?ACCT:msg2rec(H), T);
+req([H|T], record) ->
+ ?BASE:'#new-'(?BASE:msg2rec(H), T);
+req(T, _) ->
T.
+dict(['ACR' | _]) ->
+ ?ACCT;
+dict(_) ->
+ ?BASE.
+
%% Set only values that aren't already.
set([H|T], Vs) ->
[H | Vs ++ T];
+set(#diameter_base_accounting_ACR{} = Rec, Vs) ->
+ set(Rec, Vs, ?ACCT);
set(Rec, Vs) ->
- lists:foldl(fun({F,_} = FV, A) -> set(?DICT:'#get-'(F, A), FV, A) end,
+ set(Rec, Vs, ?BASE).
+
+set(Rec, Vs, Dict) ->
+ lists:foldl(fun({F,_} = FV, A) ->
+ set(Dict, Dict:'#get-'(F, A), FV, A)
+ end,
Rec,
Vs).
-set(E, FV, Rec)
+set(Dict, E, FV, Rec)
when E == undefined;
E == [] ->
- ?DICT:'#set-'(FV, Rec);
-set(_, _, Rec) ->
+ Dict:'#set-'(FV, Rec);
+set(_, _, _, Rec) ->
Rec.
+%% Contruct and deconstruct names to work around group names being
+%% restricted to atoms. (Not really used yet.)
+
+name(Names)
+ when is_list(Names) ->
+ ?A(string:join([?L(A) || A <- Names], ","));
+
+name(A)
+ when is_atom(A) ->
+ [?A(S) || S <- string:tokens(?L(A), ",")].
+
%% ===========================================================================
%% diameter callbacks
@@ -584,14 +630,14 @@ prepare(Pkt, Caps, send_unsupported) ->
Req = prepare(Pkt, Caps),
#diameter_packet{bin = <<H:5/binary, _CmdCode:3/binary, T/binary>>}
= E
- = diameter_codec:encode(?DICT, Pkt#diameter_packet{msg = Req}),
+ = diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}),
E#diameter_packet{bin = <<H/binary, 42:24/integer, T/binary>>};
prepare(Pkt, Caps, send_unsupported_app) ->
Req = prepare(Pkt, Caps),
#diameter_packet{bin = <<H:8/binary, _ApplId:4/binary, T/binary>>}
= E
- = diameter_codec:encode(?DICT, Pkt#diameter_packet{msg = Req}),
+ = diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}),
E#diameter_packet{bin = <<H/binary, 42:32/integer, T/binary>>};
prepare(Pkt, Caps, send_error_bit) ->
@@ -612,7 +658,7 @@ prepare(Pkt, Caps, _Name) ->
prepare(Pkt, Caps).
prepare(#diameter_packet{msg = Req}, Caps)
- when is_record(Req, diameter_base_ACR);
+ when is_record(Req, diameter_base_accounting_ACR);
'ACR' == hd(Req) ->
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, DR}}
@@ -687,13 +733,16 @@ handle_error(Reason, _Req, ?CLIENT, _Peer, _Name) ->
%% Note that diameter will set Result-Code and Failed-AVPs if
%% #diameter_packet.errors is non-null.
-handle_request(Pkt, ?SERVER, {_Ref, Caps}) ->
- request(Pkt, Caps).
+handle_request(#diameter_packet{msg = M}, ?SERVER, {_Ref, Caps}) ->
+ request(M, Caps).
+
+request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 0},
+ _) ->
+ {protocol_error, ?INVALID_AVP_BITS};
-request(#diameter_packet{msg
- = #diameter_base_ACR{'Session-Id' = SId,
- 'Accounting-Record-Type' = RT,
- 'Accounting-Record-Number' = RN}},
+request(#diameter_base_accounting_ACR{'Session-Id' = SId,
+ 'Accounting-Record-Type' = RT,
+ 'Accounting-Record-Number' = RN},
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
{reply, ['ACA', {'Result-Code', ?SUCCESS},
@@ -703,8 +752,8 @@ request(#diameter_packet{msg
{'Accounting-Record-Type', RT},
{'Accounting-Record-Number', RN}]};
-request(#diameter_packet{msg = #diameter_base_ASR{'Session-Id' = SId,
- 'AVP' = Avps}},
+request(#diameter_base_ASR{'Session-Id' = SId,
+ 'AVP' = Avps},
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
{reply, #diameter_base_ASA{'Result-Code' = ?SUCCESS,
@@ -713,22 +762,22 @@ request(#diameter_packet{msg = #diameter_base_ASR{'Session-Id' = SId,
'Origin-Realm' = OR,
'AVP' = Avps}};
-request(#diameter_packet{msg = #diameter_base_STR{'Termination-Cause' = T}},
+request(#diameter_base_STR{'Termination-Cause' = T},
_Caps)
when T /= ?LOGOUT ->
discard;
-request(#diameter_packet{msg = #diameter_base_STR{'Destination-Realm'= R}},
+request(#diameter_base_STR{'Destination-Realm'= R},
#diameter_caps{origin_realm = {OR, _}})
when R /= undefined, R /= OR ->
{protocol_error, ?REALM_NOT_SERVED};
-request(#diameter_packet{msg = #diameter_base_STR{'Destination-Host'= [H]}},
+request(#diameter_base_STR{'Destination-Host'= [H]},
#diameter_caps{origin_host = {OH, _}})
when H /= OH ->
{protocol_error, ?UNABLE_TO_DELIVER};
-request(#diameter_packet{msg = #diameter_base_STR{'Session-Id' = SId}},
+request(#diameter_base_STR{'Session-Id' = SId},
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
{reply, #diameter_base_STA{'Result-Code' = ?SUCCESS,
@@ -736,6 +785,6 @@ request(#diameter_packet{msg = #diameter_base_STR{'Session-Id' = SId}},
'Origin-Host' = OH,
'Origin-Realm' = OR}};
-request(#diameter_packet{msg = #diameter_base_RAR{}}, _Caps) ->
+request(#diameter_base_RAR{}, _Caps) ->
receive after 2000 -> ok end,
{protocol_error, ?TOO_BUSY}.
diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl
index 0c42f955ad..890d24f6f8 100644
--- a/lib/diameter/test/diameter_util.erl
+++ b/lib/diameter/test/diameter_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -35,7 +35,8 @@
lport/3,
listen/2, listen/3,
connect/3, connect/4,
- disconnect/4]).
+ disconnect/4,
+ info/0]).
%% common_test-specific
-export([write_priv/3,
@@ -262,7 +263,10 @@ listen(SvcName, Prot) ->
listen(SvcName, Prot, []).
listen(SvcName, Prot, Opts) ->
- add_transport(SvcName, {listen, opts(Prot, listen) ++ Opts}).
+ SvcName = diameter:service_info(SvcName, name), %% assert
+ Ref = add_transport(SvcName, {listen, opts(Prot, listen) ++ Opts}),
+ true = transport(SvcName, Ref), %% assert
+ Ref.
%% ---------------------------------------------------------------------------
%% connect/2-3
@@ -275,15 +279,22 @@ connect(Client, Prot, LRef) ->
connect(Client, Prot, LRef, Opts) ->
[PortNr] = lport(Prot, LRef, 20),
- Ref = add_transport(Client, {connect, opts(Prot, PortNr) ++ Opts}),
+ Client = diameter:service_info(Client, name), %% assert
true = diameter:subscribe(Client),
+ Ref = add_transport(Client, {connect, opts(Prot, PortNr) ++ Opts}),
+ true = transport(Client, Ref), %% assert
+
ok = receive
{diameter_event, Client, {up, Ref, _, _, _}} -> ok
- after 2000 ->
+ after 10000 ->
{Client, Prot, PortNr, process_info(self(), messages)}
end,
Ref.
+transport(SvcName, Ref) ->
+ [Ref] == [R || [{ref, R} | _] <- diameter:service_info(SvcName, transport),
+ R == Ref].
+
%% ---------------------------------------------------------------------------
%% disconnect/4
%%
@@ -295,7 +306,7 @@ disconnect(Client, Ref, Server, LRef) ->
ok = diameter:remove_transport(Client, Ref),
ok = receive
{diameter_event, Server, {down, LRef, _, _}} -> ok
- after 2000 ->
+ after 10000 ->
{Client, Ref, Server, LRef, process_info(self(), messages)}
end.
@@ -320,3 +331,17 @@ opts(listen) ->
[];
opts(PortNr) ->
[{raddr, ?ADDR}, {rport, PortNr}].
+
+%% ---------------------------------------------------------------------------
+%% info/0
+
+info() ->
+ [_|_] = Svcs = diameter:services(), %% assert
+ run([[fun info/1, S] || S <- Svcs]).
+
+info(S) ->
+ [_|_] = Keys = diameter:service_info(S, keys),
+ [] = run([[fun info/2, K, S] || K <- Keys]).
+
+info(Key, SvcName) ->
+ [{Key, _}] = diameter:service_info(SvcName, [Key]).
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index f6dc786417..48e6596e72 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2011. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. 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
@@ -18,7 +18,7 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 1.1
+DIAMETER_VSN = 1.2
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)"
diff --git a/lib/edoc/doc/src/notes.xml b/lib/edoc/doc/src/notes.xml
index a74a19bc05..e01a6d6675 100644
--- a/lib/edoc/doc/src/notes.xml
+++ b/lib/edoc/doc/src/notes.xml
@@ -31,6 +31,25 @@
<p>This document describes the changes made to the EDoc
application.</p>
+<section><title>Edoc 0.7.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ List behaviour callbacks in Edoc when using -callback
+ attribute. (Thanks to Magnus Henoch.)</p>
+ <p>
+ Added special case for file names under Windows. (Thanks
+ to Beads Land-Trujillo.)</p>
+ <p>
+ Own Id: OTP-10174</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Edoc 0.7.9.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/edoc/src/edoc_data.erl b/lib/edoc/src/edoc_data.erl
index aad0b14371..624f9177a2 100644
--- a/lib/edoc/src/edoc_data.erl
+++ b/lib/edoc/src/edoc_data.erl
@@ -167,7 +167,10 @@ callbacks(Es, Module, Env, Opts) ->
case lists:any(fun (#entry{name = {behaviour_info, 1}}) -> true;
(_) -> false
end,
- Es) of
+ Es)
+ orelse
+ lists:keymember(callback, 1, Module#module.attributes)
+ of
true ->
try (Module#module.name):behaviour_info(callbacks) of
Fs ->
diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl
index 7fd8358add..90fb8a679c 100644
--- a/lib/edoc/src/edoc_lib.erl
+++ b/lib/edoc/src/edoc_lib.erl
@@ -469,6 +469,10 @@ uri_get("ftp:" ++ Path) ->
uri_get("//" ++ Path) ->
Msg = io_lib:format("cannot access network-path: '//~s'.", [Path]),
{error, Msg};
+uri_get([C, $:, $/ | _]=Path) when C >= $A, C =< $Z; C >= $a, C =< $z ->
+ uri_get_file(Path); % special case for Windows
+uri_get([C, $:, $\ | _]=Path) when C >= $A, C =< $Z; C >= $a, C =< $z ->
+ uri_get_file(Path); % special case for Windows
uri_get(URI) ->
case is_relative_uri(URI) of
true ->
diff --git a/lib/edoc/src/edoc_parser.yrl b/lib/edoc/src/edoc_parser.yrl
index 4d6428f75b..d7c1b1c045 100644
--- a/lib/edoc/src/edoc_parser.yrl
+++ b/lib/edoc/src/edoc_parser.yrl
@@ -100,9 +100,7 @@ ptype -> '[' utype ',' '...' ']' : #t_nonempty_list{type = '$2'}.
ptype -> utype_list:
if length(element(1, '$1')) == 1 ->
%% there must be exactly one utype in the list
- hd(element(1, '$1'));
- %% Replace last line when releasing next major release:
- %% #t_paren{type = hd(element(1, '$1'))};
+ #t_paren{type = hd(element(1, '$1'))};
length(element(1, '$1')) == 0 ->
return_error(element(2, '$1'), "syntax error before: ')'");
true ->
diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk
index b8f33894f1..2f403212c8 100644
--- a/lib/edoc/vsn.mk
+++ b/lib/edoc/vsn.mk
@@ -1 +1 @@
-EDOC_VSN = 0.7.9.1
+EDOC_VSN = 0.7.10
diff --git a/lib/erl_docgen/doc/src/Makefile b/lib/erl_docgen/doc/src/Makefile
index a254c4fb6d..8d8dd0dd55 100644
--- a/lib/erl_docgen/doc/src/Makefile
+++ b/lib/erl_docgen/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2011-2011. All Rights Reserved.
+# Copyright Ericsson AB 2011-2012. 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/lib/erl_docgen/doc/src/notes.xml b/lib/erl_docgen/doc/src/notes.xml
index 8ca11b1cf5..ee62de86b1 100644
--- a/lib/erl_docgen/doc/src/notes.xml
+++ b/lib/erl_docgen/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,7 +30,22 @@
</header>
<p>This document describes the changes made to the <em>erl_docgen</em> application.</p>
- <section><title>Erl_Docgen 0.3.1</title>
+ <section><title>Erl_Docgen 0.3.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> A bug regarding spaces in C function prototypes has
+ been fixed. (Thanks to Richard O'Keefe.) </p>
+ <p>
+ Own Id: OTP-10138</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Docgen 0.3.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/erl_docgen/priv/bin/Makefile b/lib/erl_docgen/priv/bin/Makefile
index 449cc6691c..d6d0ad2260 100644
--- a/lib/erl_docgen/priv/bin/Makefile
+++ b/lib/erl_docgen/priv/bin/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2010. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. 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/lib/erl_docgen/priv/css/Makefile b/lib/erl_docgen/priv/css/Makefile
index 0faca019f3..24abc4fa9c 100644
--- a/lib/erl_docgen/priv/css/Makefile
+++ b/lib/erl_docgen/priv/css/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. 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/lib/erl_docgen/priv/dtd/Makefile b/lib/erl_docgen/priv/dtd/Makefile
index 314374d34b..4538b7f8e6 100644
--- a/lib/erl_docgen/priv/dtd/Makefile
+++ b/lib/erl_docgen/priv/dtd/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2011. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. 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/lib/erl_docgen/priv/dtd_html_entities/Makefile b/lib/erl_docgen/priv/dtd_html_entities/Makefile
index 9cd3f38c4b..f8a10973b2 100644
--- a/lib/erl_docgen/priv/dtd_html_entities/Makefile
+++ b/lib/erl_docgen/priv/dtd_html_entities/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. 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/lib/erl_docgen/priv/dtd_man_entities/Makefile b/lib/erl_docgen/priv/dtd_man_entities/Makefile
index b6f29af6b4..da4998dcbc 100644
--- a/lib/erl_docgen/priv/dtd_man_entities/Makefile
+++ b/lib/erl_docgen/priv/dtd_man_entities/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. 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/lib/erl_docgen/priv/images/Makefile b/lib/erl_docgen/priv/images/Makefile
index 389f9f98ae..61016e927c 100644
--- a/lib/erl_docgen/priv/images/Makefile
+++ b/lib/erl_docgen/priv/images/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. 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/lib/erl_docgen/priv/js/flipmenu/Makefile b/lib/erl_docgen/priv/js/flipmenu/Makefile
index 682d186608..7933752532 100644
--- a/lib/erl_docgen/priv/js/flipmenu/Makefile
+++ b/lib/erl_docgen/priv/js/flipmenu/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. 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/lib/erl_docgen/priv/xsl/Makefile b/lib/erl_docgen/priv/xsl/Makefile
index ca7a4e8157..1510387d72 100644
--- a/lib/erl_docgen/priv/xsl/Makefile
+++ b/lib/erl_docgen/priv/xsl/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. 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/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl
index 7cf5465f90..4bc5abb364 100644
--- a/lib/erl_docgen/priv/xsl/db_html.xsl
+++ b/lib/erl_docgen/priv/xsl/db_html.xsl
@@ -1817,7 +1817,14 @@
<xsl:choose>
<xsl:when test="ancestor::cref">
- <a name="{substring-before(nametext, '(')}"><span class="bold_code"><xsl:value-of select="ret"/><xsl:text> </xsl:text><xsl:value-of select="nametext"/></span></a><br/>
+ <a name="{substring-before(nametext, '(')}">
+ <span class="bold_code">
+ <xsl:value-of select="ret"/>
+ <xsl:call-template name="maybe-space-after-ret">
+ <xsl:with-param name="s" select="ret"/>
+ </xsl:call-template>
+ <xsl:value-of select="nametext"/>
+ </span></a><br/>
</xsl:when>
<xsl:when test="ancestor::erlref">
<xsl:variable name="fname">
@@ -1845,6 +1852,18 @@
</xsl:template>
+ <xsl:template name="maybe-space-after-ret">
+ <xsl:param name="s"/>
+ <xsl:variable name="last_char"
+ select="substring($s, string-length($s), 1)"/>
+ <xsl:choose>
+ <xsl:when test="$last_char != '*'">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+
<!-- Type -->
<xsl:template match="type">
<xsl:param name="partnum"/>
diff --git a/lib/erl_docgen/priv/xsl/db_man.xsl b/lib/erl_docgen/priv/xsl/db_man.xsl
index 5234ba6bd0..33808859c7 100644
--- a/lib/erl_docgen/priv/xsl/db_man.xsl
+++ b/lib/erl_docgen/priv/xsl/db_man.xsl
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
- # Copyright Ericsson AB 2009-2011. All Rights Reserved.
+ # Copyright Ericsson AB 2009-2012. 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
@@ -758,10 +758,32 @@
<xsl:template name="name">
<xsl:text>&#10;.B&#10;</xsl:text>
- <xsl:apply-templates/>
+ <xsl:choose>
+ <xsl:when test="ancestor::cref">
+ <xsl:value-of select="ret"/>
+ <xsl:call-template name="maybe-space-after-ret">
+ <xsl:with-param name="s" select="ret"/>
+ </xsl:call-template>
+ <xsl:value-of select="nametext"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates/>
+ </xsl:otherwise>
+ </xsl:choose>
<xsl:text>&#10;.br</xsl:text>
</xsl:template>
+ <xsl:template name="maybe-space-after-ret">
+ <xsl:param name="s"/>
+ <xsl:variable name="last_char"
+ select="substring($s, string-length($s), 1)"/>
+ <xsl:choose>
+ <xsl:when test="$last_char != '*'">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
<!-- Type -->
<xsl:template match="type">
diff --git a/lib/erl_docgen/priv/xsl/db_pdf.xsl b/lib/erl_docgen/priv/xsl/db_pdf.xsl
index bf17406d84..da96052462 100644
--- a/lib/erl_docgen/priv/xsl/db_pdf.xsl
+++ b/lib/erl_docgen/priv/xsl/db_pdf.xsl
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
- # Copyright Ericsson AB 2009-2011. All Rights Reserved.
+ # Copyright Ericsson AB 2009-2012. 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
@@ -1424,7 +1424,13 @@
<xsl:param name="partnum"/>
<xsl:choose>
<xsl:when test="ancestor::cref">
- <fo:block id="{generate-id(nametext)}"><xsl:value-of select="ret"/><xsl:text></xsl:text><xsl:value-of select="nametext"/></fo:block>
+ <fo:block id="{generate-id(nametext)}">
+ <xsl:value-of select="ret"/>
+ <xsl:call-template name="maybe-space-after-ret">
+ <xsl:with-param name="s" select="ret"/>
+ </xsl:call-template>
+ <xsl:value-of select="nametext"/>
+ </fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block id="{generate-id(.)}"><xsl:value-of select="."/></fo:block>
@@ -1432,6 +1438,16 @@
</xsl:choose>
</xsl:template>
+ <xsl:template name="maybe-space-after-ret">
+ <xsl:param name="s"/>
+ <xsl:variable name="last_char"
+ select="substring($s, string-length($s), 1)"/>
+ <xsl:choose>
+ <xsl:when test="$last_char != '*'">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
<!-- Type -->
<xsl:template match="type">
diff --git a/lib/erl_docgen/src/Makefile b/lib/erl_docgen/src/Makefile
index 4d8f43bfed..6c8b438b25 100644
--- a/lib/erl_docgen/src/Makefile
+++ b/lib/erl_docgen/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/erl_docgen/src/docgen_edoc_xml_cb.erl b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
index 20daae8215..cbaa93a15d 100644
--- a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
+++ b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
@@ -338,7 +338,7 @@ otp_xmlify_e(#xmlElement{name=code} = E) -> % 4)
end;
otp_xmlify_e(#xmlElement{name=Tag} = E) % 5a
when Tag==h1; Tag==h2; Tag==h3; Tag==h4; Tag==h5 ->
- Content = text_only(E#xmlElement.content),
+ Content = text_and_a_name_only(E#xmlElement.content),
[E#xmlElement{name=b, content=Content}];
otp_xmlify_e(#xmlElement{name=Tag} = E) % 5b-c)
when Tag==center;
@@ -1161,6 +1161,18 @@ get_text(#xmlElement{content=[#xmlText{value=Text}]}) ->
get_text(#xmlElement{content=[E]}) ->
get_text(E).
+%% text_and_name_only(Es) -> Ts
+text_and_a_name_only([#xmlElement{
+ name = a,
+ attributes = [#xmlAttribute{name=name}]} = Name|Es]) ->
+ [Name|text_and_a_name_only(Es)];
+text_and_a_name_only([#xmlElement{content = Content}|Es]) ->
+ text_and_a_name_only(Content) ++ text_and_a_name_only(Es);
+text_and_a_name_only([#xmlText{} = E |Es]) ->
+ [E | text_and_a_name_only(Es)];
+text_and_a_name_only([]) ->
+ [].
+
%% text_only(Es) -> Ts
%% Takes a list of xmlElement and xmlText and return a lists of xmlText.
text_only([#xmlElement{content = Content}|Es]) ->
diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk
index bf10591f34..b05df254a6 100644
--- a/lib/erl_docgen/vsn.mk
+++ b/lib/erl_docgen/vsn.mk
@@ -1,2 +1,2 @@
-ERL_DOCGEN_VSN = 0.3.1
+ERL_DOCGEN_VSN = 0.3.2
diff --git a/lib/erl_interface/aclocal.m4 b/lib/erl_interface/aclocal.m4
index 339a15a2bb..b1cf1fe404 100644
--- a/lib/erl_interface/aclocal.m4
+++ b/lib/erl_interface/aclocal.m4
@@ -1,7 +1,7 @@
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1998-2011. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2012. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
@@ -59,6 +59,7 @@ AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only us
dnl Cross compilation variables
AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)])
@@ -606,6 +607,103 @@ ifelse([$5], , , [$5
fi
])
+dnl ----------------------------------------------------------------------
+dnl
+dnl AC_DOUBLE_MIDDLE_ENDIAN
+dnl
+dnl Checks whether doubles are represented in "middle-endian" format.
+dnl Sets ac_cv_double_middle_endian={no,yes,unknown} accordingly,
+dnl as well as DOUBLE_MIDDLE_ENDIAN.
+dnl
+dnl
+
+AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN],
+[AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian,
+[# It does not; compile a test program.
+AC_RUN_IFELSE(
+[AC_LANG_SOURCE([[#include <stdlib.h>
+
+int
+main(void)
+{
+ int i = 0;
+ int zero = 0;
+ int bigendian;
+ int zero_index = 0;
+
+ union
+ {
+ long int l;
+ char c[sizeof (long int)];
+ } u;
+
+ /* we'll use the one with 32-bit words */
+ union
+ {
+ double d;
+ unsigned int c[2];
+ } vint;
+
+ union
+ {
+ double d;
+ unsigned long c[2];
+ } vlong;
+
+ union
+ {
+ double d;
+ unsigned short c[2];
+ } vshort;
+
+
+ /* Are we little or big endian? From Harbison&Steele. */
+ u.l = 1;
+ bigendian = (u.c[sizeof (long int) - 1] == 1);
+
+ zero_index = bigendian ? 1 : 0;
+
+ vint.d = 1.0;
+ vlong.d = 1.0;
+ vshort.d = 1.0;
+
+ if (sizeof(unsigned int) == 4)
+ {
+ if (vint.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned long) == 4)
+ {
+ if (vlong.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned short) == 4)
+ {
+ if (vshort.c[zero_index] != 0)
+ zero = 1;
+ }
+
+ exit (zero);
+}
+]])],
+ [ac_cv_c_double_middle_endian=no],
+ [ac_cv_c_double_middle_endian=yes],
+ [ac_cv_c_double_middle=unknown])])
+case $ac_cv_c_double_middle_endian in
+ yes)
+ m4_default([$1],
+ [AC_DEFINE([DOUBLE_MIDDLE_ENDIAN], 1,
+ [Define to 1 if your processor stores the words in a double in
+ middle-endian format (like some ARMs).])]) ;;
+ no)
+ $2 ;;
+ *)
+ m4_default([$3],
+ [AC_MSG_WARN([unknown double endianness
+presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;;
+esac
+])# AC_C_DOUBLE_MIDDLE_ENDIAN
+
dnl ----------------------------------------------------------------------
dnl
@@ -1337,6 +1435,14 @@ if test "$ac_cv_c_bigendian" = "yes"; then
AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian])
fi
+case X$erl_xcomp_double_middle_endian in
+ X) ;;
+ Xyes|Xno|Xunknown) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;;
+ *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);;
+esac
+
+AC_C_DOUBLE_MIDDLE_ENDIAN
+
AC_ARG_ENABLE(native-ethr-impls,
AS_HELP_STRING([--disable-native-ethr-impls],
[disable native ethread implementations]),
diff --git a/lib/erl_interface/doc/src/Makefile b/lib/erl_interface/doc/src/Makefile
index 2bad8976b5..b8e638ed24 100644
--- a/lib/erl_interface/doc/src/Makefile
+++ b/lib/erl_interface/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+# Copyright Ericsson AB 1998-2012. 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/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml
index 48ac0be22d..6791c39c7d 100644
--- a/lib/erl_interface/doc/src/notes.xml
+++ b/lib/erl_interface/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,23 @@
</header>
<p>This document describes the changes made to the Erl_interface application.</p>
+<section><title>Erl_Interface 3.7.8</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Detect when middle endian doubles are used by a platform
+ and account for it when decoding floats. (Thanks to Mike
+ Sperber)</p>
+ <p>
+ Own Id: OTP-10209</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erl_Interface 3.7.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/erl_interface/src/Makefile.in b/lib/erl_interface/src/Makefile.in
index 661dbb68ac..cb41391fe9 100644
--- a/lib/erl_interface/src/Makefile.in
+++ b/lib/erl_interface/src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/erl_interface/test/Makefile b/lib/erl_interface/test/Makefile
index d11a138844..2b85dfc571 100644
--- a/lib/erl_interface/test/Makefile
+++ b/lib/erl_interface/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/erl_interface/test/all_SUITE_data/Makefile.src b/lib/erl_interface/test/all_SUITE_data/Makefile.src
index 39085def2d..d2f57c8149 100644
--- a/lib/erl_interface/test/all_SUITE_data/Makefile.src
+++ b/lib/erl_interface/test/all_SUITE_data/Makefile.src
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. 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/lib/erl_interface/test/all_SUITE_data/gccifier.c b/lib/erl_interface/test/all_SUITE_data/gccifier.c
index a1019f9a72..7e4ffc7281 100644
--- a/lib/erl_interface/test/all_SUITE_data/gccifier.c
+++ b/lib/erl_interface/test/all_SUITE_data/gccifier.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2012. 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/lib/erl_interface/test/all_SUITE_data/init_tc.erl b/lib/erl_interface/test/all_SUITE_data/init_tc.erl
index 60c965eda3..7e370c566a 100644
--- a/lib/erl_interface/test/all_SUITE_data/init_tc.erl
+++ b/lib/erl_interface/test/all_SUITE_data/init_tc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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/lib/erl_interface/test/ei_tmo_SUITE.erl b/lib/erl_interface/test/ei_tmo_SUITE.erl
index cc22cb7440..c33beecbe9 100644
--- a/lib/erl_interface/test/ei_tmo_SUITE.erl
+++ b/lib/erl_interface/test/ei_tmo_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. 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/lib/erl_interface/test/erl_match_SUITE.erl b/lib/erl_interface/test/erl_match_SUITE.erl
index edbb17a8c1..2cf4fafe82 100644
--- a/lib/erl_interface/test/erl_match_SUITE.erl
+++ b/lib/erl_interface/test/erl_match_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk
index 0e249d2483..4e401c874e 100644
--- a/lib/erl_interface/vsn.mk
+++ b/lib/erl_interface/vsn.mk
@@ -1 +1 @@
-EI_VSN = 3.7.7
+EI_VSN = 3.7.8
diff --git a/lib/et/doc/src/Makefile b/lib/et/doc/src/Makefile
index facd335449..249d45fa52 100644
--- a/lib/et/doc/src/Makefile
+++ b/lib/et/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2010. All Rights Reserved.
+# Copyright Ericsson AB 2002-2012. 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/lib/et/doc/src/et_intro.xml b/lib/et/doc/src/et_intro.xml
index 60da289721..f90a6d2b5c 100644
--- a/lib/et/doc/src/et_intro.xml
+++ b/lib/et/doc/src/et_intro.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2002</year><year>2010</year>
+ <year>2002</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/et/doc/src/notes.xml b/lib/et/doc/src/notes.xml
index 42790e0cdf..0b1f3eddce 100644
--- a/lib/et/doc/src/notes.xml
+++ b/lib/et/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2002</year><year>2011</year>
+ <year>2002</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -36,6 +36,21 @@
one section in this document. The title of each section is the
version number of <c>Event Tracer (ET)</c>.</p>
+<section><title>ET 1.4.4.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix typo in ET doc (Thanks to Ricardo Catalinas Jim�nez)</p>
+ <p>
+ Own Id: OTP-10119</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>ET 1.4.4.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/et/examples/Makefile b/lib/et/examples/Makefile
index 190d5e2d6a..3edbebc2e4 100644
--- a/lib/et/examples/Makefile
+++ b/lib/et/examples/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2010. All Rights Reserved.
+# Copyright Ericsson AB 2002-2012. 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/lib/et/src/Makefile b/lib/et/src/Makefile
index 7693571c5c..c68a3f4efd 100644
--- a/lib/et/src/Makefile
+++ b/lib/et/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2010. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/et/src/et_wx_contents_viewer.erl b/lib/et/src/et_wx_contents_viewer.erl
index 86f46f25d0..b559da8807 100644
--- a/lib/et/src/et_wx_contents_viewer.erl
+++ b/lib/et/src/et_wx_contents_viewer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2012. 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
@@ -245,12 +245,7 @@ handle_event(#wx{id = Id,
Data when is_record(Data, filter) ->
F = Data,
ChildState= S#state{active_filter = F#filter.name},
- case wx_object:start_link(?MODULE, [ChildState], []) of
- {ok, Pid} when S#state.parent_pid =/= self() ->
- unlink(Pid);
- _ ->
- ignore
- end;
+ wx_object:start_link(?MODULE, [ChildState], []);
{hide, Actors} ->
send_viewer_event(S, {delete_actors, Actors});
{show, Actors} ->
@@ -356,12 +351,7 @@ handle_event(#wx{event = #wxKey{rawCode = KeyCode}}, S) ->
case lists:keysearch(?DEFAULT_FILTER_NAME, #filter.name, S#state.filters) of
{value, F} when is_record(F, filter) ->
ChildState= S#state{active_filter = F#filter.name},
- case wx_object:start_link(?MODULE, [ChildState], []) of
- {ok, Pid} when S#state.parent_pid =/= self() ->
- unlink(Pid);
- _ ->
- ignore
- end;
+ wx_object:start_link(?MODULE, [ChildState], []);
false ->
ignore
end,
@@ -370,12 +360,7 @@ handle_event(#wx{event = #wxKey{rawCode = KeyCode}}, S) ->
case catch lists:nth(Int-$0, S#state.filters) of
F when is_record(F, filter) ->
ChildState= S#state{active_filter = F#filter.name},
- case wx_object:start_link(?MODULE, [ChildState], []) of
- {ok, Pid} when S#state.parent_pid =/= self() ->
- unlink(Pid);
- _ ->
- ignore
- end;
+ wx_object:start_link(?MODULE, [ChildState], []);
{'EXIT', _} ->
ignore
end,
diff --git a/lib/et/test/Makefile b/lib/et/test/Makefile
index 7b4db7b0c3..3817079b5f 100644
--- a/lib/et/test/Makefile
+++ b/lib/et/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2011. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. 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/lib/et/vsn.mk b/lib/et/vsn.mk
index 090d43d52f..167c9d9594 100644
--- a/lib/et/vsn.mk
+++ b/lib/et/vsn.mk
@@ -1 +1 @@
-ET_VSN = 1.4.4.1
+ET_VSN = 1.4.4.2
diff --git a/lib/eunit/doc/overview.edoc b/lib/eunit/doc/overview.edoc
index ad449cb6fc..b4af31ae6a 100644
--- a/lib/eunit/doc/overview.edoc
+++ b/lib/eunit/doc/overview.edoc
@@ -723,8 +723,12 @@ A <em>simple test object</em> is one of the following:
```fun some_function/0'''
```fun some_module:some_function/0'''
</li>
- <li>A pair of atoms `{ModuleName, FunctionName}', referring to the
- function `ModuleName:FunctionName/0'</li>
+ <li>A tuple `{test, ModuleName, FunctionName}', where `ModuleName' and
+ `FunctionName' are atoms, referring to the function
+ `ModuleName:FunctionName/0'</li>
+ <li>(Obsolete) A pair of atoms `{ModuleName, FunctionName}', equivalent to
+ `{test, ModuleName, FunctionName}' if nothing else matches first. This
+ might be removed in a future version.</li>
<li>A pair `{LineNumber, SimpleTest}', where `LineNumber' is a
nonnegative integer and `SimpleTest' is another simple test
object. `LineNumber' should indicate the source line of the test.
diff --git a/lib/eunit/doc/src/notes.xml b/lib/eunit/doc/src/notes.xml
index 34e8a47e3d..814580e228 100644
--- a/lib/eunit/doc/src/notes.xml
+++ b/lib/eunit/doc/src/notes.xml
@@ -32,6 +32,38 @@
</header>
<p>This document describes the changes made to the EUnit application.</p>
+<section><title>Eunit 2.2.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ New option 'no_tty' to silent the default tty report.</p>
+ <p>
+ Recognize the new stacktrace format introduced in R15,
+ adding location information. (Thanks to Klas Johansson.)</p>
+ <p>
+ Improve layout of error messages, printing the stack
+ trace before the error term.</p>
+ <p>
+ Heuristically detect and report bad return values from
+ generators and instantiators. E.g., "ok" will not be
+ interpreted as a module name, and a warning will be
+ printed.</p>
+ <p>
+ New test representation {test,M,F} for completeness along
+ with {generator,M,F}. Tuples {M,F} are deprecated.</p>
+ <p>
+ Use UTF-8 as encoding in Surefire output files. (Thanks
+ to Lukas Larsson.)</p>
+ <p>
+ Own Id: OTP-10173</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Eunit 2.2.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl
index db68d8ae60..fba840c3bd 100644
--- a/lib/eunit/include/eunit.hrl
+++ b/lib/eunit/include/eunit.hrl
@@ -25,11 +25,12 @@
%% will become undefined. NODEBUG also implies NOASSERT, unless testing
%% is enabled.
%%
-%% If including this file causes TEST to be defined, then NOASSERT will
-%% be undefined, even if it was previously defined and even if NODEBUG
-%% is defined. If both ASSERT and NOASSERT are defined before the file
-%% is included, then ASSERT takes precedence, and NOASSERT will become
-%% undefined regardless of TEST.
+%% Defining NOASSERT disables asserts. NODEBUG implies NOASSERT unless
+%% testing is enabled. If including this file causes TEST to be defined,
+%% then NOASSERT will be undefined, even if it was previously defined and
+%% even if NODEBUG is defined. If both ASSERT and NOASSERT are defined
+%% before the file is included, then ASSERT takes precedence, and NOASSERT
+%% will become undefined regardless of TEST.
%%
%% After including this file, EUNIT will be defined if and only if TEST
%% is defined.
@@ -127,9 +128,9 @@
current_function)))).
-endif.
--ifdef(NOASSERT).
%% The plain assert macro should be defined to do nothing if this file
%% is included when debugging/testing is turned off.
+-ifdef(NOASSERT).
-ifndef(assert).
-define(assert(BoolExpr),ok).
-endif.
diff --git a/lib/eunit/src/eunit.app.src b/lib/eunit/src/eunit.app.src
index 5e16dfa2ce..431abac98b 100644
--- a/lib/eunit/src/eunit.app.src
+++ b/lib/eunit/src/eunit.app.src
@@ -14,7 +14,6 @@
eunit_striptests,
eunit_surefire,
eunit_test,
- eunit_tests,
eunit_tty]},
{registered,[]},
{applications, [kernel,stdlib]},
diff --git a/lib/eunit/src/eunit.erl b/lib/eunit/src/eunit.erl
index 95857e83c8..51846d73b3 100644
--- a/lib/eunit/src/eunit.erl
+++ b/lib/eunit/src/eunit.erl
@@ -139,7 +139,7 @@ test(Tests, Options) ->
%% @private
%% @doc See {@link test/2}.
test(Server, Tests, Options) ->
- Listeners = [eunit_tty:start(Options) | listeners(Options)],
+ Listeners = listeners(Options),
Serial = eunit_serial:start(Listeners),
case eunit_server:start_test(Server, Serial, Tests, Options) of
{ok, Reference} -> test_run(Reference, Listeners);
@@ -194,7 +194,10 @@ submit(Server, T, Options) ->
eunit_server:start_test(Server, Dummy, T, Options).
listeners(Options) ->
- Ps = start_listeners(proplists:get_all_values(report, Options)),
+ %% note that eunit_tty must always run, because it sends the final
+ %% {result,...} message that the test_run() function is waiting for
+ Ls = [{eunit_tty, Options} | proplists:get_all_values(report, Options)],
+ Ps = start_listeners(Ls),
%% the event_log option is for debugging, to view the raw events
case proplists:get_value(event_log, Options) of
undefined ->
diff --git a/lib/eunit/src/eunit_data.erl b/lib/eunit/src/eunit_data.erl
index 392d378a0e..0350f9bf6e 100644
--- a/lib/eunit/src/eunit_data.erl
+++ b/lib/eunit/src/eunit_data.erl
@@ -83,6 +83,7 @@
%% SimpleTest = TestFunction | {Line::integer(), SimpleTest}
%%
%% TestFunction = () -> any()
+%% | {test, M::moduleName(), F::functionName()}
%% | {M::moduleName(), F::functionName()}.
%%
%% AbstractTestFunction = (X::any()) -> any()
@@ -95,7 +96,6 @@
%%
%% @type moduleName() = atom()
%% @type functionName() = atom()
-%% @type arity() = integer()
%% @type appName() = atom()
%% @type fileName() = string()
@@ -156,8 +156,9 @@ iter_prev(#iter{prev = [T | Ts]} = I) ->
%% @spec (tests()) -> none | {testItem(), tests()}
%% @type testItem() = #test{} | #group{}
%% @throws {bad_test, term()}
-%% | {generator_failed, exception()}
-%% | {no_such_function, eunit_lib:mfa()}
+%% | {generator_failed, {{M::atom(),F::atom(),A::integer()},
+%% exception()}}
+%% | {no_such_function, mfa()}
%% | {module_not_found, moduleName()}
%% | {application_not_found, appName()}
%% | {file_read_error, {Reason::atom(), Message::string(),
@@ -221,17 +222,27 @@ parse({foreachx, P, S1, C1, Ps} = T)
[] ->
{data, []}
end;
-parse({generator, F} = T) when is_function(F) ->
+parse({generator, F}) when is_function(F) ->
+ {module, M} = erlang:fun_info(F, module),
+ {name, N} = erlang:fun_info(F, name),
+ {arity, A} = erlang:fun_info(F, arity),
+ parse({generator, F, {M,N,A}});
+parse({generator, F, {M,N,A}} = T)
+ when is_function(F), is_atom(M), is_atom(N), is_integer(A) ->
check_arity(F, 0, T),
%% use run_testfun/1 to handle wrapper exceptions
case eunit_test:run_testfun(F) of
{ok, T1} ->
+ case eunit_lib:is_not_test(T1) of
+ true -> throw({bad_generator, {{M,N,A}, T1}});
+ false -> ok
+ end,
{data, T1};
{error, {Class, Reason, Trace}} ->
- throw({generator_failed, {Class, Reason, Trace}})
+ throw({generator_failed, {{M,N,A}, {Class, Reason, Trace}}})
end;
parse({generator, M, F}) when is_atom(M), is_atom(F) ->
- parse({generator, eunit_test:function_wrapper(M, F)});
+ parse({generator, eunit_test:mf_wrapper(M, F), {M,F,0}});
parse({inorder, T}) ->
group(#group{tests = T, order = inorder});
parse({inparallel, T}) ->
@@ -421,8 +432,11 @@ parse_simple(F) ->
parse_function(F) when is_function(F) ->
check_arity(F, 0, F),
#test{f = F, location = eunit_lib:fun_parent(F)};
-parse_function({M,F}) when is_atom(M), is_atom(F) ->
- #test{f = eunit_test:function_wrapper(M, F), location = {M, F, 0}};
+parse_function({test, M, F}) when is_atom(M), is_atom(F) ->
+ #test{f = eunit_test:mf_wrapper(M, F), location = {M, F, 0}};
+parse_function({M, F}) when is_atom(M), is_atom(F) ->
+ %% {M,F} is now considered obsolete; use {test,M,F} instead
+ parse_function({test, M, F});
parse_function(F) ->
bad_test(F).
@@ -580,7 +594,7 @@ testfuns(Es, M, TestSuffix, GeneratorSuffix) ->
N = atom_to_list(F),
case lists:suffix(TestSuffix, N) of
true ->
- [{M,F} | Fs];
+ [{test, M, F} | Fs];
false ->
case lists:suffix(GeneratorSuffix, N) of
true ->
@@ -723,6 +737,7 @@ data_test_() ->
Tests = [T,T,T],
[?_assertMatch(ok, eunit:test(T)),
?_assertMatch(error, eunit:test(Fail)),
+ ?_assertMatch(ok, eunit:test({test, ?MODULE, trivial_test})),
?_assertMatch(ok, eunit:test({generator, fun () -> Tests end})),
?_assertMatch(ok, eunit:test({generator, fun generator/0})),
?_assertMatch(ok, eunit:test({generator, ?MODULE, generator_exported_})),
@@ -740,6 +755,12 @@ data_test_() ->
%%?_test({foreach, Setup, [T, T, T]})
].
+trivial_test() ->
+ ok.
+
+trivial_generator_test_() ->
+ [?_test(ok)].
+
lazy_test_() ->
{spawn, [?_test(undefined = put(count, 0)),
lazy_gen(7),
diff --git a/lib/eunit/src/eunit_lib.erl b/lib/eunit/src/eunit_lib.erl
index 1c41e229c5..ea9e944d7e 100644
--- a/lib/eunit/src/eunit_lib.erl
+++ b/lib/eunit/src/eunit_lib.erl
@@ -30,7 +30,8 @@
-export([dlist_next/1, uniq/1, fun_parent/1, is_string/1, command/1,
command/2, command/3, trie_new/0, trie_store/2, trie_match/2,
split_node/1, consult_file/1, list_dir/1, format_exit_term/1,
- format_exception/1, format_exception/2, format_error/1]).
+ format_exception/1, format_exception/2, format_error/1,
+ is_not_test/1]).
%% Type definitions for describing exceptions
@@ -39,13 +40,10 @@
%%
%% @type exceptionClass() = error | exit | throw
%%
-%% @type stackTrace() = [{moduleName(), functionName(),
-%% arity() | argList()}]
+%% @type stackTrace() = [{moduleName(), functionName(), arity() | argList()}]
%%
%% @type moduleName() = atom()
%% @type functionName() = atom()
-%% @type arity() = integer()
-%% @type mfa() = {moduleName(), functionName(), arity()}
%% @type argList() = [term()]
%% @type fileName() = string()
@@ -59,8 +57,9 @@ format_exception({Class,Term,Trace}, Depth)
when is_atom(Class), is_list(Trace) ->
case is_stacktrace(Trace) of
true ->
- io_lib:format("~w:~P\n~s",
- [Class, Term, Depth, format_stacktrace(Trace)]);
+ io_lib:format("~s**~w:~s",
+ [format_stacktrace(Trace), Class,
+ format_term(Term, Depth)]);
false ->
format_term(Term, Depth)
end;
@@ -86,6 +85,12 @@ analyze_exit_term(Term) ->
is_stacktrace([]) ->
true;
+is_stacktrace([{M,F,A,L}|Fs])
+ when is_atom(M), is_atom(F), is_integer(A), is_list(L) ->
+ is_stacktrace(Fs);
+is_stacktrace([{M,F,As,L}|Fs])
+ when is_atom(M), is_atom(F), is_list(As), is_list(L) ->
+ is_stacktrace(Fs);
is_stacktrace([{M,F,A}|Fs]) when is_atom(M), is_atom(F), is_integer(A) ->
is_stacktrace(Fs);
is_stacktrace([{M,F,As}|Fs]) when is_atom(M), is_atom(F), is_list(As) ->
@@ -96,10 +101,11 @@ is_stacktrace(_) ->
format_stacktrace(Trace) ->
format_stacktrace(Trace, "in function", "in call from").
-format_stacktrace([{M,F,A}|Fs], Pre, Pre1) when is_integer(A) ->
- [io_lib:fwrite(" ~s ~w:~w/~w\n", [Pre, M, F, A])
+format_stacktrace([{M,F,A,L}|Fs], Pre, Pre1) when is_integer(A) ->
+ [io_lib:fwrite("~s ~w:~w/~w~s\n",
+ [Pre, M, F, A, format_stacktrace_location(L)])
| format_stacktrace(Fs, Pre1, Pre1)];
-format_stacktrace([{M,F,As}|Fs], Pre, Pre1) when is_list(As) ->
+format_stacktrace([{M,F,As,L}|Fs], Pre, Pre1) when is_list(As) ->
A = length(As),
C = case is_op(M,F,A) of
true when A =:= 1 ->
@@ -112,12 +118,23 @@ format_stacktrace([{M,F,As}|Fs], Pre, Pre1) when is_list(As) ->
false ->
io_lib:fwrite("~w(~s)", [F,format_arglist(As)])
end,
- [io_lib:fwrite(" ~s ~w:~w/~w\n called as ~s\n",
- [Pre,M,F,A,C])
+ [io_lib:fwrite("~s ~w:~w/~w~s\n called as ~s\n",
+ [Pre,M,F,A,format_stacktrace_location(L),C])
| format_stacktrace(Fs,Pre1,Pre1)];
+format_stacktrace([{M,F,As}|Fs], Pre, Pre1) ->
+ format_stacktrace([{M,F,As,[]}|Fs], Pre, Pre1);
format_stacktrace([],_Pre,_Pre1) ->
"".
+format_stacktrace_location(Location) ->
+ File = proplists:get_value(file, Location),
+ Line = proplists:get_value(line, Location),
+ if File =/= undefined, Line =/= undefined ->
+ io_lib:format(" (~s, line ~w)", [File, Line]);
+ true ->
+ ""
+ end.
+
format_arg(A) ->
io_lib:format("~P",[A,15]).
@@ -139,9 +156,13 @@ is_op(_M, _F, _A) ->
format_error({bad_test, Term}) ->
error_msg("bad test descriptor", "~P", [Term, 15]);
-format_error({generator_failed, Exception}) ->
- error_msg("test generator failed", "~s",
- [format_exception(Exception)]);
+format_error({bad_generator, {{M,F,A}, Term}}) ->
+ error_msg(io_lib:format("result from generator ~w:~w/~w is not a test",
+ [M,F,A]),
+ "~P", [Term, 15]);
+format_error({generator_failed, {{M,F,A}, Exception}}) ->
+ error_msg(io_lib:format("test generator ~w:~w/~w failed",[M,F,A]),
+ "~s", [format_exception(Exception)]);
format_error({no_such_function, {M,F,A}})
when is_atom(M), is_atom(F), is_integer(A) ->
error_msg(io_lib:format("no such function: ~w:~w/~w", [M,F,A]),
@@ -158,14 +179,55 @@ format_error({setup_failed, Exception}) ->
format_error({cleanup_failed, Exception}) ->
error_msg("context cleanup failed", "~s",
[format_exception(Exception)]);
+format_error({{bad_instantiator, {{M,F,A}, Term}}, _DummyException}) ->
+ error_msg(io_lib:format("result from instantiator ~w:~w/~w is not a test",
+ [M,F,A]),
+ "~P", [Term, 15]);
format_error({instantiation_failed, Exception}) ->
error_msg("instantiation of subtests failed", "~s",
[format_exception(Exception)]).
error_msg(Title, Fmt, Args) ->
- Msg = io_lib:format("::"++Fmt, Args), % gets indentation right
+ Msg = io_lib:format("**"++Fmt, Args), % gets indentation right
io_lib:fwrite("*** ~s ***\n~s\n\n", [Title, Msg]).
+-ifdef(TEST).
+format_exception_test_() ->
+ [?_assertMatch(
+ "\nymmud:rorre"++_,
+ lists:reverse(lists:flatten(
+ format_exception(try erlang:error(dummy)
+ catch C:R -> {C, R, erlang:get_stacktrace()}
+ end)))),
+ ?_assertMatch(
+ "\nymmud:rorre"++_,
+ lists:reverse(lists:flatten(
+ format_exception(try erlang:error(dummy, [a])
+ catch C:R -> {C, R, erlang:get_stacktrace()}
+ end))))].
+-endif.
+
+%% ---------------------------------------------------------------------
+%% detect common return values that are definitely not tests
+
+is_not_test(T) ->
+ case T of
+ ok -> true;
+ error -> true;
+ true -> true;
+ false -> true;
+ undefined -> true;
+ {ok, _} -> true;
+ {error, _} -> true;
+ {'EXIT', _} -> true;
+ N when is_number(N) -> true;
+ [N|_] when is_number(N) -> true;
+ X when is_binary(X) -> true;
+ X when is_pid(X) -> true;
+ X when is_port(X) -> true;
+ X when is_reference(X) -> true;
+ _ -> false
+ end.
%% ---------------------------------------------------------------------
%% Deep list iterator; accepts improper lists/sublists, and also accepts
diff --git a/lib/eunit/src/eunit_surefire.erl b/lib/eunit/src/eunit_surefire.erl
index 2a6cbca14d..46b8c8b503 100644
--- a/lib/eunit/src/eunit_surefire.erl
+++ b/lib/eunit/src/eunit_surefire.erl
@@ -156,9 +156,33 @@ handle_end(test, Data, St) ->
St#state{testsuites=store_suite(NewTestSuite, TestSuites)}.
%% Cancel group does not give information on the individual cancelled test case
-%% We ignore this event
-handle_cancel(group, _Data, St) ->
- St;
+%% We ignore this event...
+handle_cancel(group, Data, St) ->
+ %% ...except when it tells us that a fixture setup or cleanup failed.
+ case proplists:get_value(reason, Data) of
+ {abort, {SomethingFailed, Exception}}
+ when SomethingFailed =:= setup_failed;
+ SomethingFailed =:= cleanup_failed ->
+ [GroupId|_] = proplists:get_value(id, Data),
+ TestSuites = St#state.testsuites,
+ TestSuite = lookup_suite_by_group_id(GroupId, TestSuites),
+
+ %% We don't have any proper name. Let's give all the
+ %% clues that we have.
+ Name = case SomethingFailed of
+ setup_failed -> "fixture setup ";
+ cleanup_failed -> "fixture cleanup "
+ end
+ ++ io_lib:format("~p", [proplists:get_value(id, Data)]),
+ Desc = format_desc(proplists:get_value(desc, Data)),
+ TestCase = #testcase{
+ name = Name, description = Desc,
+ time = 0, output = <<>>},
+ NewTestSuite = add_testcase_to_testsuite({error, Exception}, TestCase, TestSuite),
+ St#state{testsuites=store_suite(NewTestSuite, TestSuites)};
+ _ ->
+ St
+ end;
handle_cancel(test, Data, St) ->
%% Retrieve existing test suite:
[GroupId|_] = proplists:get_value(id, Data),
@@ -232,7 +256,7 @@ write_reports(TestSuites, XmlDir) ->
write_report(#testsuite{name = Name} = TestSuite, XmlDir) ->
Filename = filename:join(XmlDir, lists:flatten(["TEST-", escape_suitename(Name)], ".xml")),
- case file:open(Filename, [write, raw]) of
+ case file:open(Filename, [write,{encoding,utf8}]) of
{ok, FileDescriptor} ->
try
write_report_to(TestSuite, FileDescriptor)
diff --git a/lib/eunit/src/eunit_test.erl b/lib/eunit/src/eunit_test.erl
index bca49ae626..9cf40a738d 100644
--- a/lib/eunit/src/eunit_test.erl
+++ b/lib/eunit/src/eunit_test.erl
@@ -21,8 +21,7 @@
-module(eunit_test).
--export([run_testfun/1, function_wrapper/2, enter_context/4,
- multi_setup/1]).
+-export([run_testfun/1, mf_wrapper/2, enter_context/4, multi_setup/1]).
-include("eunit.hrl").
@@ -43,8 +42,12 @@ get_stacktrace(Ts) ->
prune_trace([{eunit_data, _, _} | Rest], Tail) ->
prune_trace(Rest, Tail);
+prune_trace([{eunit_data, _, _, _} | Rest], Tail) ->
+ prune_trace(Rest, Tail);
prune_trace([{?MODULE, _, _} | _Rest], Tail) ->
Tail;
+prune_trace([{?MODULE, _, _, _} | _Rest], Tail) ->
+ Tail;
prune_trace([T | Ts], Tail) ->
[T | prune_trace(Ts, Tail)];
prune_trace([], Tail) ->
@@ -258,7 +261,7 @@ macro_test_() ->
%% @type wrapperError() = {no_such_function, mfa()}
%% | {module_not_found, moduleName()}
-function_wrapper(M, F) ->
+mf_wrapper(M, F) ->
fun () ->
try M:F()
catch
@@ -289,12 +292,12 @@ fail(Term) ->
wrapper_test_() ->
{"error handling in function wrapper",
[?_assertException(throw, {module_not_found, eunit_nonexisting},
- run_testfun(function_wrapper(eunit_nonexisting,test))),
+ run_testfun(mf_wrapper(eunit_nonexisting,test))),
?_assertException(throw,
{no_such_function, {?MODULE,nonexisting_test,0}},
- run_testfun(function_wrapper(?MODULE,nonexisting_test))),
+ run_testfun(mf_wrapper(?MODULE,nonexisting_test))),
?_test({error, {error, undef, _T}}
- = run_testfun(function_wrapper(?MODULE,wrapper_test_exported_)))
+ = run_testfun(mf_wrapper(?MODULE,wrapper_test_exported_)))
]}.
%% this must be exported (done automatically by the autoexport transform)
@@ -319,6 +322,17 @@ enter_context(Setup, Cleanup, Instantiate, Callback) ->
R ->
try Instantiate(R) of
T ->
+ case eunit_lib:is_not_test(T) of
+ true ->
+ catch throw(error), % generate a stack trace
+ {module,M} = erlang:fun_info(Instantiate, module),
+ {name,N} = erlang:fun_info(Instantiate, name),
+ {arity,A} = erlang:fun_info(Instantiate, arity),
+ context_error({bad_instantiator, {{M,N,A},T}},
+ error, badarg);
+ false ->
+ ok
+ end,
try Callback(T) %% call back to client code
after
%% Always run cleanup; client may be an idiot
diff --git a/lib/eunit/src/eunit_tty.erl b/lib/eunit/src/eunit_tty.erl
index e3e7b710b2..f21b2da3d3 100644
--- a/lib/eunit/src/eunit_tty.erl
+++ b/lib/eunit/src/eunit_tty.erl
@@ -44,6 +44,7 @@ start(Options) ->
init(Options) ->
St = #state{verbose = proplists:get_bool(verbose, Options)},
+ put(no_tty, proplists:get_bool(no_tty, Options)),
receive
{start, _Reference} ->
if St#state.verbose -> print_header();
@@ -59,30 +60,30 @@ terminate({ok, Data}, St) ->
Cancel = proplists:get_value(cancel, Data, 0),
if Fail =:= 0, Skip =:= 0, Cancel =:= 0 ->
if Pass =:= 0 ->
- io:fwrite(" There were no tests to run.\n");
+ fwrite(" There were no tests to run.\n");
true ->
if St#state.verbose -> print_bar();
true -> ok
end,
if Pass =:= 1 ->
- io:fwrite(" Test passed.\n");
+ fwrite(" Test passed.\n");
true ->
- io:fwrite(" All ~w tests passed.\n", [Pass])
+ fwrite(" All ~w tests passed.\n", [Pass])
end
end,
sync_end(ok);
true ->
print_bar(),
- io:fwrite(" Failed: ~w. Skipped: ~w. Passed: ~w.\n",
- [Fail, Skip, Pass]),
+ fwrite(" Failed: ~w. Skipped: ~w. Passed: ~w.\n",
+ [Fail, Skip, Pass]),
if Cancel =/= 0 ->
- io:fwrite("One or more tests were cancelled.\n");
+ fwrite("One or more tests were cancelled.\n");
true -> ok
end,
sync_end(error)
end;
terminate({error, Reason}, _St) ->
- io:fwrite("Internal error: ~P.\n", [Reason, 25]),
+ fwrite("Internal error: ~P.\n", [Reason, 25]),
sync_end(error).
sync_end(Result) ->
@@ -93,10 +94,10 @@ sync_end(Result) ->
end.
print_header() ->
- io:fwrite("======================== EUnit ========================\n").
+ fwrite("======================== EUnit ========================\n").
print_bar() ->
- io:fwrite("=======================================================\n").
+ fwrite("=======================================================\n").
handle_begin(group, Data, St) ->
@@ -170,18 +171,18 @@ handle_cancel(test, Data, St) ->
indent(N) when is_integer(N), N >= 1 ->
- io:put_chars(lists:duplicate(N * 2, $\s));
+ fwrite(lists:duplicate(N * 2, $\s));
indent(_N) ->
ok.
print_group_start(I, Desc) ->
indent(I),
- io:fwrite("~s\n", [Desc]).
+ fwrite("~s\n", [Desc]).
print_group_end(I, Time) ->
if Time > 0 ->
indent(I),
- io:fwrite("[done in ~.3f s]\n", [Time/1000]);
+ fwrite("[done in ~.3f s]\n", [Time/1000]);
true ->
ok
end.
@@ -198,9 +199,9 @@ print_test_begin(I, Data) ->
end,
case proplists:get_value(source, Data) of
{Module, Name, _Arity} ->
- io:fwrite("~s:~s ~s~s...", [Module, L, Name, D]);
+ fwrite("~s:~s ~s~s...", [Module, L, Name, D]);
_ ->
- io:fwrite("~s~s...", [L, D])
+ fwrite("~s~s...", [L, D])
end.
print_test_end(Data) ->
@@ -208,36 +209,35 @@ print_test_end(Data) ->
T = if Time > 0 -> io_lib:fwrite("[~.3f s] ", [Time/1000]);
true -> ""
end,
- io:fwrite("~sok\n", [T]).
+ fwrite("~sok\n", [T]).
print_test_error({error, Exception}, Data) ->
Output = proplists:get_value(output, Data),
- io:fwrite("*failed*\n::~s",
- [eunit_lib:format_exception(Exception)]),
+ fwrite("*failed*\n~s", [eunit_lib:format_exception(Exception)]),
case Output of
<<>> ->
- io:put_chars("\n\n");
+ fwrite("\n\n");
<<Text:800/binary, _:1/binary, _/binary>> ->
- io:fwrite(" output:<<\"~s\">>...\n\n", [Text]);
+ fwrite(" output:<<\"~s\">>...\n\n", [Text]);
_ ->
- io:fwrite(" output:<<\"~s\">>\n\n", [Output])
+ fwrite(" output:<<\"~s\">>\n\n", [Output])
end;
print_test_error({skipped, Reason}, _) ->
- io:fwrite("*did not run*\n::~s\n", [format_skipped(Reason)]).
+ fwrite("*did not run*\n::~s\n", [format_skipped(Reason)]).
format_skipped({module_not_found, M}) ->
- io_lib:format("missing module: ~w", [M]);
+ io_lib:fwrite("missing module: ~w", [M]);
format_skipped({no_such_function, {M,F,A}}) ->
- io_lib:format("no such function: ~w:~w/~w", [M,F,A]).
+ io_lib:fwrite("no such function: ~w:~w/~w", [M,F,A]).
print_test_cancel(Reason) ->
- io:fwrite(format_cancel(Reason)).
+ fwrite(format_cancel(Reason)).
print_group_cancel(_I, {blame, _}) ->
ok;
print_group_cancel(I, Reason) ->
indent(I),
- io:fwrite(format_cancel(Reason)).
+ fwrite(format_cancel(Reason)).
format_cancel(undefined) ->
"*skipped*\n";
@@ -253,3 +253,12 @@ format_cancel({exit, Reason}) ->
[Reason, 15]);
format_cancel({abort, Reason}) ->
eunit_lib:format_error(Reason).
+
+fwrite(String) ->
+ fwrite(String, []).
+
+fwrite(String, Args) ->
+ case get(no_tty) of
+ false -> io:fwrite(String, Args);
+ true -> ok
+ end.
diff --git a/lib/eunit/test/Makefile b/lib/eunit/test/Makefile
index 1586dd8857..e4ddf4e42c 100644
--- a/lib/eunit/test/Makefile
+++ b/lib/eunit/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2010. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. 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/lib/eunit/vsn.mk b/lib/eunit/vsn.mk
index 445c070e96..174d197117 100644
--- a/lib/eunit/vsn.mk
+++ b/lib/eunit/vsn.mk
@@ -1 +1 @@
-EUNIT_VSN = 2.2.2
+EUNIT_VSN = 2.2.3
diff --git a/lib/gs/contribs/bonk/Makefile b/lib/gs/contribs/bonk/Makefile
index dc92149784..a630deaf24 100644
--- a/lib/gs/contribs/bonk/Makefile
+++ b/lib/gs/contribs/bonk/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/gs/contribs/cols/Makefile b/lib/gs/contribs/cols/Makefile
index 4ce4204c92..3af91e1dae 100644
--- a/lib/gs/contribs/cols/Makefile
+++ b/lib/gs/contribs/cols/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/gs/contribs/mandel/Makefile b/lib/gs/contribs/mandel/Makefile
index 451ece2c8f..5f0b047955 100644
--- a/lib/gs/contribs/mandel/Makefile
+++ b/lib/gs/contribs/mandel/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/gs/contribs/othello/Makefile b/lib/gs/contribs/othello/Makefile
index 5f09f2ed13..5f37d164f5 100644
--- a/lib/gs/contribs/othello/Makefile
+++ b/lib/gs/contribs/othello/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/gs/doc/src/Makefile b/lib/gs/doc/src/Makefile
index 14204c52fc..bfc5e4c317 100644
--- a/lib/gs/doc/src/Makefile
+++ b/lib/gs/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/gs/doc/src/notes.xml b/lib/gs/doc/src/notes.xml
index d1b74ecfb1..69b1e90ba5 100644
--- a/lib/gs/doc/src/notes.xml
+++ b/lib/gs/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/gs/examples/Makefile b/lib/gs/examples/Makefile
index d214cd2256..ee77990191 100644
--- a/lib/gs/examples/Makefile
+++ b/lib/gs/examples/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/gs/src/Makefile b/lib/gs/src/Makefile
index a9904161a4..4b7a4523b9 100644
--- a/lib/gs/src/Makefile
+++ b/lib/gs/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/gs/tcl/Makefile.in b/lib/gs/tcl/Makefile.in
index 0bccb60c7b..c22a7037e8 100644
--- a/lib/gs/tcl/Makefile.in
+++ b/lib/gs/tcl/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+# Copyright Ericsson AB 2002-2012. 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/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 1ef73da1be..776e336aea 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -45,11 +45,9 @@
t_atom_vals/1,
t_binary/0,
t_bitstr/0,
- t_bitstrlist/0,
t_boolean/0,
t_byte/0,
t_char/0,
- t_charlist/0,
t_cons/0,
t_cons/2,
t_cons_hd/1,
@@ -72,8 +70,6 @@
t_non_neg_integer/0,
t_pos_integer/0,
t_integers/1,
- t_iodata/0,
- t_iolist/0,
t_is_any/1,
t_is_atom/1,
t_is_binary/1,
@@ -85,7 +81,6 @@
t_is_fun/1,
t_is_integer/1,
t_is_integer/1,
- t_is_list/1,
t_is_nil/1,
t_is_none/1,
t_is_none_or_unit/1,
@@ -118,14 +113,11 @@
t_subtract/2,
t_sup/1,
t_sup/2,
- t_tid/0,
- t_timeout/0,
t_tuple/0,
t_tuple/1,
t_tuple_args/1,
t_tuple_size/1,
- t_tuple_subtypes/1,
- t_unicode_string/0
+ t_tuple_subtypes/1
]).
-ifdef(DO_ERL_BIF_TYPES_TEST).
@@ -144,111 +136,14 @@ type(M, F, A) ->
-spec type(atom(), atom(), arity(), [erl_types:erl_type()]) -> erl_types:erl_type().
-%%-- binary -------------------------------------------------------------------
-type(binary, at, 2, Xs) ->
- strict(arg_types(binary, at, 2), Xs, fun(_) -> t_integer() end);
-type(binary, bin_to_list, Arity, Xs) when 1 =< Arity, Arity =< 3 ->
- strict(arg_types(binary, bin_to_list, Arity), Xs,
- fun(_) -> t_list(t_integer()) end);
-type(binary, compile_pattern, 1, Xs) ->
- strict(arg_types(binary, compile_pattern, 1), Xs,
- fun(_) -> t_binary_compiled_pattern() end);
-type(binary, copy, Arity, Xs) when Arity =:= 1; Arity =:= 2 ->
- strict(arg_types(binary, copy, Arity), Xs,
- fun(_) -> t_binary() end);
-type(binary, decode_unsigned, Arity, Xs) when Arity =:= 1; Arity =:= 2 ->
- strict(arg_types(binary, decode_unsigned, Arity), Xs,
- fun(_) -> t_non_neg_integer() end);
-type(binary, encode_unsigned, Arity, Xs) when Arity =:= 1; Arity =:= 2 ->
- strict(arg_types(binary, encode_unsigned, Arity), Xs,
- fun(_) -> t_binary() end);
-type(binary, first, 1, Xs) ->
- strict(arg_types(binary, first, 1), Xs, fun(_) -> t_non_neg_integer() end);
-type(binary, last, 1, Xs) ->
- strict(arg_types(binary, last, 1), Xs, fun(_) -> t_non_neg_integer() end);
-type(binary, list_to_bin, 1, Xs) ->
- type(erlang, list_to_binary, 1, Xs);
-type(binary, longest_common_prefix, 1, Xs) ->
- strict(arg_types(binary, longest_common_prefix, 1), Xs,
- fun(_) -> t_integer() end);
-type(binary, longest_common_suffix, 1, Xs) ->
- strict(arg_types(binary, longest_common_suffix, 1), Xs,
- fun(_) -> t_integer() end);
-type(binary, match, Arity, Xs) when Arity =:= 2; Arity =:= 3 ->
- strict(arg_types(binary, match, Arity), Xs,
- fun(_) ->
- t_sup(t_atom('nomatch'), t_binary_canonical_part())
- end);
-type(binary, matches, Arity, Xs) when Arity =:= 2; Arity =:= 3 ->
- strict(arg_types(binary, matches, Arity), Xs,
- fun(_) -> t_list(t_binary_canonical_part()) end);
-type(binary, part, 2, Xs) ->
- type(erlang, binary_part, 2, Xs);
-type(binary, part, 3, Xs) ->
- type(erlang, binary_part, 3, Xs);
-type(binary, referenced_byte_size, 1, Xs) ->
- strict(arg_types(binary, referenced_byte_size, 1), Xs,
- fun(_) -> t_non_neg_integer() end);
-%%-- code ---------------------------------------------------------------------
-type(code, get_chunk, 2, Xs) ->
- strict(arg_types(code, get_chunk, 2), Xs,
- fun (_) -> t_sup(t_binary(), t_atom('undefined')) end);
-type(code, is_module_native, 1, Xs) ->
- strict(arg_types(code, is_module_native, 1), Xs,
- fun (_) -> t_sup(t_boolean(), t_atom('undefined')) end);
-type(code, module_md5, 1, Xs) ->
- strict(arg_types(code, module_md5, 1), Xs,
- fun (_) -> t_sup(t_binary(), t_atom('undefined')) end);
-type(code, make_stub_module, 3, Xs) ->
- strict(arg_types(code, make_stub_module, 3), Xs, fun ([Mod,_,_]) -> Mod end);
-type(code, rehash, 0, _) ->
- t_atom('ok');
-%%-- erl_ddll -----------------------------------------------------------------
-type(erl_ddll, demonitor, 1, Xs) ->
- type(erlang, demonitor, 1, Xs);
-type(erl_ddll, format_error_int, 1, Xs) ->
- strict(arg_types(erl_ddll, format_error_int, 1), Xs,
- fun (_) -> t_string() end);
-type(erl_ddll, info, 2, Xs) ->
- strict(arg_types(erl_ddll, info, 2), Xs, fun (_) -> t_atom() end);
-type(erl_ddll, loaded_drivers, 0, _) ->
- t_tuple([t_atom('ok'), t_list(t_string())]);
-type(erl_ddll, monitor, 2, Xs) -> % return type is the same, though args are not
- type(erlang, monitor, 2, Xs);
-type(erl_ddll, try_load, 3, Xs) ->
- strict(arg_types(erl_ddll, try_load, 3), Xs,
- fun (_) ->
- t_sup([t_tuple([t_atom('ok'), t_atom('already_loaded')]),
- t_tuple([t_atom('ok'), t_atom('loaded')]),
- t_tuple([t_atom('ok'),
- t_atom('pending_driver'), t_reference()]),
- t_tuple([t_atom('error'), t_atom('inconsistent')]),
- t_tuple([t_atom('error'), t_atom('permanent')])])
- end);
-type(erl_ddll, try_unload, 2, Xs) ->
- strict(arg_types(erl_ddll, try_unload, 2), Xs,
- fun (_) ->
- t_sup([t_tuple([t_atom('ok'), t_atom('pending_process')]),
- t_tuple([t_atom('ok'), t_atom('unloaded')]),
- t_tuple([t_atom('ok'), t_atom('pending_driver')]),
- t_tuple([t_atom('ok'),
- t_atom('pending_driver'), t_reference()]),
- t_tuple([t_atom('error'), t_atom('permanent')]),
- t_tuple([t_atom('error'), t_atom('not_loaded')]),
- t_tuple([t_atom('error'),
- t_atom('not_loaded_by_this_process')])])
- end);
%%-- erlang -------------------------------------------------------------------
type(erlang, halt, 0, _) -> t_none();
type(erlang, halt, 1, _) -> t_none();
type(erlang, halt, 2, _) -> t_none();
type(erlang, exit, 1, _) -> t_none();
-%% Note that exit/2 sends an exit signal to another process.
-type(erlang, exit, 2, _) -> t_atom('true');
type(erlang, error, 1, _) -> t_none();
type(erlang, error, 2, _) -> t_none();
type(erlang, throw, 1, _) -> t_none();
-type(erlang, hibernate, 3, _) -> t_none();
type(erlang, '==', 2, Xs = [X1, X2]) ->
case t_is_atom(X1) andalso t_is_atom(X2) of
true -> type(erlang, '=:=', 2, Xs);
@@ -600,20 +495,12 @@ type(erlang, 'bnot', 1, Xs) ->
{ok, T} -> T
end
end);
-%% This returns (-X)-1, so it often gives a negative result.
-%% strict(arg_types(erlang, 'bnot', 1), Xs, fun (_) -> t_integer() end);
+%% Guard bif, needs to be here.
type(erlang, abs, 1, Xs) ->
strict(arg_types(erlang, abs, 1), Xs, fun ([X]) -> X end);
-type(erlang, adler32, 1, Xs) ->
- strict(arg_types(erlang, adler32, 1), Xs, fun (_) -> t_adler32() end);
-type(erlang, adler32, 2, Xs) ->
- strict(arg_types(erlang, adler32, 2), Xs, fun (_) -> t_adler32() end);
-type(erlang, adler32_combine, 3, Xs) ->
- strict(arg_types(erlang, adler32_combine, 3), Xs,
- fun (_) -> t_adler32() end);
+%% This returns (-X)-1, so it often gives a negative result.
+%% strict(arg_types(erlang, 'bnot', 1), Xs, fun (_) -> t_integer() end);
type(erlang, append, 2, Xs) -> type(erlang, '++', 2, Xs); % alias
-type(erlang, append_element, 2, Xs) ->
- strict(arg_types(erlang, append_element, 2), Xs, fun (_) -> t_tuple() end);
type(erlang, apply, 2, Xs) ->
Fun = fun ([X, _Y]) ->
case t_is_fun(X) of
@@ -626,111 +513,24 @@ type(erlang, apply, 2, Xs) ->
strict(arg_types(erlang, apply, 2), Xs, Fun);
type(erlang, apply, 3, Xs) ->
strict(arg_types(erlang, apply, 3), Xs, fun (_) -> t_any() end);
-type(erlang, atom_to_binary, 2, Xs) ->
- strict(arg_types(erlang, atom_to_binary, 2), Xs, fun (_) -> t_binary() end);
-type(erlang, atom_to_list, 1, Xs) ->
- strict(arg_types(erlang, atom_to_list, 1), Xs, fun (_) -> t_string() end);
+%% Guard bif, needs to be here.
type(erlang, binary_part, 2, Xs) ->
strict(arg_types(erlang, binary_part, 2), Xs, fun (_) -> t_binary() end);
+%% Guard bif, needs to be here.
type(erlang, binary_part, 3, Xs) ->
strict(arg_types(erlang, binary_part, 3), Xs, fun (_) -> t_binary() end);
-type(erlang, binary_to_atom, 2, Xs) ->
- strict(arg_types(erlang, binary_to_atom, 2), Xs, fun (_) -> t_atom() end);
-type(erlang, binary_to_existing_atom, 2, Xs) ->
- type(erlang, binary_to_atom, 2, Xs);
-type(erlang, binary_to_list, 1, Xs) ->
- strict(arg_types(erlang, binary_to_list, 1), Xs,
- fun (_) -> t_list(t_byte()) end);
-type(erlang, binary_to_list, 3, Xs) ->
- strict(arg_types(erlang, binary_to_list, 3), Xs,
- fun (_) -> t_list(t_byte()) end);
-type(erlang, binary_to_term, 1, Xs) ->
- strict(arg_types(erlang, binary_to_term, 1), Xs, fun (_) -> t_any() end);
-type(erlang, binary_to_term, 2, Xs) ->
- strict(arg_types(erlang, binary_to_term, 2), Xs, fun (_) -> t_any() end);
-type(erlang, bitsize, 1, Xs) -> % XXX: TAKE OUT
- type(erlang, bit_size, 1, Xs);
+%% Guard bif, needs to be here.
type(erlang, bit_size, 1, Xs) ->
strict(arg_types(erlang, bit_size, 1), Xs,
fun (_) -> t_non_neg_integer() end);
-type(erlang, bitstr_to_list, 1, Xs) -> % XXX: TAKE OUT
- type(erlang, bitstring_to_list, 1, Xs);
-type(erlang, bitstring_to_list, 1, Xs) ->
- strict(arg_types(erlang, bitstring_to_list, 1), Xs,
- fun (_) -> t_list(t_sup(t_byte(), t_bitstr())) end);
-type(erlang, bump_reductions, 1, Xs) ->
- strict(arg_types(erlang, bump_reductions, 1), Xs,
- fun (_) -> t_atom('true') end);
+%% Guard bif, needs to be here.
type(erlang, byte_size, 1, Xs) ->
strict(arg_types(erlang, byte_size, 1), Xs,
fun (_) -> t_non_neg_integer() end);
-type(erlang, call_on_load_function, 1, Xs) ->
- %% Internal BIF used by on_load.
- strict(arg_types(erlang, call_on_load_function, 1), Xs,
- fun (_) -> t_any() end);
-type(erlang, cancel_timer, 1, Xs) ->
- strict(arg_types(erlang, cancel_timer, 1), Xs,
- fun (_) -> t_sup(t_integer(), t_atom('false')) end);
-type(erlang, check_old_code, 1, Xs) ->
- strict(arg_types(erlang, check_old_code, 1), Xs,
- fun (_) -> t_boolean() end);
-type(erlang, check_process_code, 2, Xs) ->
- strict(arg_types(erlang, check_process_code, 2), Xs,
- fun (_) -> t_boolean() end);
-type(erlang, crc32, 1, Xs) ->
- strict(arg_types(erlang, crc32, 1), Xs, fun (_) -> t_crc32() end);
-type(erlang, crc32, 2, Xs) ->
- strict(arg_types(erlang, crc32, 2), Xs, fun (_) -> t_crc32() end);
-type(erlang, crc32_combine, 3, Xs) ->
- strict(arg_types(erlang, crc32_combine, 3), Xs, fun (_) -> t_crc32() end);
-type(erlang, date, 0, _) ->
- t_date();
-type(erlang, decode_packet, 3, Xs) ->
- strict(arg_types(erlang, decode_packet, 3), Xs,
- fun (_) ->
- t_sup([t_tuple([t_atom('ok'), t_packet(), t_binary()]),
- t_tuple([t_atom('more'), t_sup([t_non_neg_integer(),
- t_atom('undefined')])]),
- t_tuple([t_atom('error'), t_any()])])
- end);
-type(erlang, delete_module, 1, Xs) ->
- strict(arg_types(erlang, delete_module, 1), Xs,
- fun (_) -> t_sup(t_atom('true'), t_atom('undefined')) end);
-type(erlang, demonitor, 1, Xs) ->
- strict(arg_types(erlang, demonitor, 1), Xs, fun (_) -> t_atom('true') end);
-%% TODO: overapproximation -- boolean only if 'info' is part of arg2 otherwise 'true'
-type(erlang, demonitor, 2, Xs) ->
- strict(arg_types(erlang, demonitor, 2), Xs, fun (_) -> t_boolean() end);
type(erlang, disconnect_node, 1, Xs) ->
strict(arg_types(erlang, disconnect_node, 1), Xs, fun (_) -> t_sup([t_boolean(), t_atom('ignored')]) end);
-type(erlang, display, 1, _) -> t_atom('true');
-type(erlang, display_string, 1, Xs) ->
- strict(arg_types(erlang, display_string, 1), Xs, fun(_) -> t_atom('true') end);
-type(erlang, display_nl, 0, _) ->
- t_atom('true');
-type(erlang, dist_exit, 3, Xs) ->
- strict(arg_types(erlang, dist_exit, 3), Xs, fun (_) -> t_atom('true') end);
-type(erlang, dt_append_vm_tag_data, 1, Xs) ->
- strict(arg_types(erlang, dt_append_vm_tag_data, 1),
- Xs,
- fun(_) -> t_iodata() end);
-type(erlang, dt_get_tag, 0, _) ->
- t_sup(t_binary(), t_atom('undefined'));
-type(erlang, dt_get_tag_data, 0, _) ->
- t_sup(t_binary(), t_atom('undefined'));
-type(erlang, dt_prepend_vm_tag_data, 1, Xs) ->
- strict(arg_types(erlang, dt_prepend_vm_tag_data, 1),
- Xs,
- fun(_) -> t_iodata() end);
-type(erlang, dt_put_tag, 1, Xs) ->
- strict(arg_types(erlang, dt_put_tag, 1), Xs,
- fun(_) -> t_sup(t_binary(), t_atom('undefined')) end);
-type(erlang, dt_restore_tag, 1, Xs) ->
- strict(arg_types(erlang, dt_restore_tag, 1), Xs, fun(_) -> t_atom('true') end);
-type(erlang, dt_spread_tag, 1, Xs) ->
- strict(arg_types(erlang, dt_spread_tag, 1), Xs,
- fun(_) -> t_sup(t_tuple([t_non_neg_integer(), t_sup(t_binary(), t_nil())]),
- t_atom('true')) end);
+%% Guard bif, needs to be here.
+%% Also much more expressive than anything you could write in a spec...
type(erlang, element, 2, Xs) ->
strict(arg_types(erlang, element, 2), Xs,
fun ([X1, X2]) ->
@@ -754,95 +554,22 @@ type(erlang, element, 2, Xs) ->
t_sup([type(erlang, element, 2, [X1, Y]) || Y <- Ts])
end
end);
-type(erlang, erase, 0, _) -> t_any();
-type(erlang, erase, 1, _) -> t_any();
-type(erlang, external_size, 1, _) -> t_integer();
-type(erlang, external_size, 2, _) -> t_integer();
-type(erlang, finish_after_on_load, 2, Xs) ->
- %% Internal BIF used by on_load.
- strict(arg_types(erlang, finish_after_on_load, 2), Xs,
- fun (_) -> t_atom('true') end);
+%% Guard bif, needs to be here.
type(erlang, float, 1, Xs) ->
strict(arg_types(erlang, float, 1), Xs, fun (_) -> t_float() end);
-type(erlang, float_to_list, 1, Xs) ->
- strict(arg_types(erlang, float_to_list, 1), Xs, fun (_) -> t_string() end);
-type(erlang, function_exported, 3, Xs) ->
- strict(arg_types(erlang, function_exported, 3), Xs,
- fun (_) -> t_boolean() end);
type(erlang, fun_info, 1, Xs) ->
strict(arg_types(erlang, fun_info, 1), Xs,
fun (_) -> t_list(t_tuple([t_atom(), t_any()])) end);
-type(erlang, fun_info, 2, Xs) ->
- strict(arg_types(erlang, fun_info, 2), Xs,
- fun (_) -> t_tuple([t_atom(), t_any()]) end);
-type(erlang, fun_to_list, 1, Xs) ->
- strict(arg_types(erlang, fun_to_list, 1), Xs, fun (_) -> t_string() end);
-type(erlang, garbage_collect, 0, _) -> t_atom('true');
-type(erlang, garbage_collect, 1, Xs) ->
- strict(arg_types(erlang, garbage_collect, 1), Xs, fun (_) -> t_boolean() end);
-type(erlang, garbage_collect_message_area, 0, _) ->
- t_boolean();
-type(erlang, get, 0, _) -> t_list(t_tuple(2));
-type(erlang, get, 1, _) -> t_any(); % | t_atom('undefined')
type(erlang, get_cookie, 0, _) -> t_atom(); % | t_atom('nocookie')
-type(erlang, get_keys, 1, _) -> t_list();
-type(erlang, get_module_info, 1, Xs) ->
- strict(arg_types(erlang, get_module_info, 1), Xs,
- fun (_) ->
- t_list(t_tuple([t_atom(), t_list(t_tuple([t_atom(), t_any()]))]))
- end);
-type(erlang, get_module_info, 2, Xs) ->
- T_module_info_2_returns =
- t_sup([t_atom(),
- t_list(t_tuple([t_atom(), t_any()])),
- t_list(t_tuple([t_atom(), t_arity(), t_integer()]))]),
- strict(arg_types(erlang, get_module_info, 2), Xs,
- fun ([Module, Item]) ->
- case t_is_atom(Item) of
- true ->
- case t_atom_vals(Item) of
- ['module'] -> t_inf(t_atom(), Module);
- ['imports'] -> t_nil();
- ['exports'] -> t_list(t_tuple([t_atom(), t_arity()]));
- ['functions'] -> t_list(t_tuple([t_atom(), t_arity()]));
- ['attributes'] -> t_list(t_tuple([t_atom(), t_any()]));
- ['compile'] -> t_list(t_tuple([t_atom(), t_any()]));
- ['native_addresses'] -> % [{FunName, Arity, Address}]
- t_list(t_tuple([t_atom(), t_arity(), t_integer()]));
- List when is_list(List) ->
- T_module_info_2_returns;
- unknown ->
- T_module_info_2_returns
- end;
- false ->
- T_module_info_2_returns
- end
- end);
-type(erlang, get_stacktrace, 0, _) ->
- t_list(t_tuple([t_atom(), t_atom(), t_sup([t_arity(), t_list()]),
- t_list()]));
-type(erlang, group_leader, 0, _) -> t_pid();
-type(erlang, group_leader, 2, Xs) ->
- strict(arg_types(erlang, group_leader, 2), Xs,
- fun (_) -> t_atom('true') end);
-type(erlang, hash, 2, Xs) ->
- strict(arg_types(erlang, hash, 2), Xs, fun (_) -> t_integer() end);
+%% Guard bif, needs to be here.
type(erlang, hd, 1, Xs) ->
strict(arg_types(erlang, hd, 1), Xs, fun ([X]) -> t_cons_hd(X) end);
-type(erlang, integer_to_list, 1, Xs) ->
- strict(arg_types(erlang, integer_to_list, 1), Xs,
- fun (_) -> t_string() end);
type(erlang, integer_to_list, 2, Xs) ->
strict(arg_types(erlang, integer_to_list, 2), Xs,
fun (_) -> t_string() end);
type(erlang, info, 1, Xs) -> type(erlang, system_info, 1, Xs); % alias
-type(erlang, iolist_size, 1, Xs) ->
- strict(arg_types(erlang, iolist_size, 1), Xs,
- fun (_) -> t_non_neg_integer() end);
-type(erlang, iolist_to_binary, 1, Xs) ->
- strict(arg_types(erlang, iolist_to_binary, 1), Xs,
- fun (_) -> t_binary() end);
-type(erlang, is_alive, 0, _) -> t_boolean();
+%% All type tests are guard BIF's and may be implemented in ways that
+%% cannot be expressed in a type spec, why they are kept in erl_bif_types.
type(erlang, is_atom, 1, Xs) ->
Fun = fun (X) -> check_guard(X, fun (Y) -> t_is_atom(Y) end, t_atom()) end,
strict(arg_types(erlang, is_atom, 1), Xs, Fun);
@@ -851,8 +578,6 @@ type(erlang, is_binary, 1, Xs) ->
check_guard(X, fun (Y) -> t_is_binary(Y) end, t_binary())
end,
strict(arg_types(erlang, is_binary, 1), Xs, Fun);
-type(erlang, is_bitstr, 1, Xs) -> % XXX: TAKE OUT
- type(erlang, is_bitstring, 1, Xs);
type(erlang, is_bitstring, 1, Xs) ->
Fun = fun (X) ->
check_guard(X, fun (Y) -> t_is_bitstr(Y) end, t_bitstr())
@@ -863,8 +588,6 @@ type(erlang, is_boolean, 1, Xs) ->
check_guard(X, fun (Y) -> t_is_boolean(Y) end, t_boolean())
end,
strict(arg_types(erlang, is_boolean, 1), Xs, Fun);
-type(erlang, is_builtin, 3, Xs) ->
- strict(arg_types(erlang, is_builtin, 3), Xs, fun (_) -> t_boolean() end);
type(erlang, is_float, 1, Xs) ->
Fun = fun (X) ->
check_guard(X, fun (Y) -> t_is_float(Y) end, t_float())
@@ -909,9 +632,6 @@ type(erlang, is_pid, 1, Xs) ->
type(erlang, is_port, 1, Xs) ->
Fun = fun (X) -> check_guard(X, fun (Y) -> t_is_port(Y) end, t_port()) end,
strict(arg_types(erlang, is_port, 1), Xs, Fun);
-type(erlang, is_process_alive, 1, Xs) ->
- strict(arg_types(erlang, is_process_alive, 1), Xs,
- fun (_) -> t_boolean() end);
type(erlang, is_record, 2, Xs) ->
Fun = fun ([X, Y]) ->
case t_is_tuple(X) of
@@ -1013,68 +733,12 @@ type(erlang, is_tuple, 1, Xs) ->
check_guard(X, fun (Y) -> t_is_tuple(Y) end, t_tuple())
end,
strict(arg_types(erlang, is_tuple, 1), Xs, Fun);
+%% Guard bif, needs to be here.
type(erlang, length, 1, Xs) ->
strict(arg_types(erlang, length, 1), Xs, fun (_) -> t_non_neg_fixnum() end);
-type(erlang, link, 1, Xs) ->
- strict(arg_types(erlang, link, 1), Xs, fun (_) -> t_atom('true') end);
-type(erlang, list_to_atom, 1, Xs) ->
- strict(arg_types(erlang, list_to_atom, 1), Xs, fun (_) -> t_atom() end);
-type(erlang, list_to_binary, 1, Xs) ->
- strict(arg_types(erlang, list_to_binary, 1), Xs,
- fun (_) -> t_binary() end);
-type(erlang, list_to_bitstr, 1, Xs) ->
- type(erlang, list_to_bitstring, 1, Xs);
-type(erlang, list_to_bitstring, 1, Xs) ->
- strict(arg_types(erlang, list_to_bitstring, 1), Xs,
- fun (_) -> t_bitstr() end);
-type(erlang, list_to_existing_atom, 1, Xs) ->
- strict(arg_types(erlang, list_to_existing_atom, 1), Xs,
- fun (_) -> t_atom() end);
-type(erlang, list_to_float, 1, Xs) ->
- strict(arg_types(erlang, list_to_float, 1), Xs, fun (_) -> t_float() end);
-type(erlang, list_to_integer, 1, Xs) ->
- strict(arg_types(erlang, list_to_integer, 1), Xs,
- fun (_) -> t_integer() end);
type(erlang, list_to_integer, 2, Xs) ->
strict(arg_types(erlang, list_to_integer, 2), Xs,
fun (_) -> t_integer() end);
-type(erlang, list_to_pid, 1, Xs) ->
- strict(arg_types(erlang, list_to_pid, 1), Xs, fun (_) -> t_pid() end);
-type(erlang, list_to_tuple, 1, Xs) ->
- strict(arg_types(erlang, list_to_tuple, 1), Xs, fun (_) -> t_tuple() end);
-type(erlang, load_module, 2, Xs) ->
- strict(arg_types(erlang, load_module, 2), Xs,
- fun ([Mod,_Bin]) -> t_code_load_return(Mod) end);
-type(erlang, load_nif, 2, Xs) ->
- strict(arg_types(erlang, load_nif, 2), Xs,
- fun (_) ->
- Reason = t_atoms(['load_failed', 'bad_lib', 'load',
- 'reload', 'upgrade', 'old_code']),
- RsnPair = t_tuple([Reason, t_string()]),
- t_sup(t_atom('ok'), t_tuple([t_atom('error'), RsnPair]))
- end);
-type(erlang, loaded, 0, _) ->
- t_list(t_atom());
-type(erlang, localtime, 0, Xs) ->
- type(erlang, universaltime, 0, Xs); % same
-type(erlang, localtime_to_universaltime, 1, Xs) ->
- type(erlang, universaltime_to_localtime, 1, Xs); % same
-type(erlang, localtime_to_universaltime, 2, Xs) ->
- strict(arg_types(erlang, localtime_to_universaltime, 2), Xs, % typecheck
- fun ([X,_]) -> type(erlang, localtime_to_universaltime, 1, [X]) end);
-type(erlang, make_fun, 3, Xs) ->
- strict(arg_types(erlang, make_fun, 3), Xs,
- fun ([_, _, Arity]) ->
- case t_number_vals(Arity) of
- [N] ->
- case is_integer(N) andalso 0 =< N andalso N =< 255 of
- true -> t_fun(N, t_any());
- false -> t_none()
- end;
- _Other -> t_fun()
- end
- end);
-type(erlang, make_ref, 0, _) -> t_reference();
type(erlang, make_tuple, 2, Xs) ->
strict(arg_types(erlang, make_tuple, 2), Xs,
fun ([Int, _]) ->
@@ -1091,87 +755,19 @@ type(erlang, make_tuple, 3, Xs) ->
_Other -> t_tuple()
end
end);
-type(erlang, match_spec_test, 3, Xs) ->
- strict(arg_types(erlang, match_spec_test, 3), Xs,
- fun (_) -> t_sup(t_tuple([t_atom('ok'),
- t_any(), % it can be any term
- t_list(t_atom('return_trace')),
- t_match_spec_test_errors()]),
- t_tuple([t_atom('error'),
- t_match_spec_test_errors()])) end);
-type(erlang, md5, 1, Xs) ->
- strict(arg_types(erlang, md5, 1), Xs, fun (_) -> t_binary() end);
-type(erlang, md5_final, 1, Xs) ->
- strict(arg_types(erlang, md5_final, 1), Xs, fun (_) -> t_binary() end);
-type(erlang, md5_init, 0, _) -> t_binary();
-type(erlang, md5_update, 2, Xs) ->
- strict(arg_types(erlang, md5_update, 2), Xs, fun (_) -> t_binary() end);
type(erlang, memory, 0, _) -> t_list(t_tuple([t_atom(), t_non_neg_fixnum()]));
-type(erlang, memory, 1, Xs) ->
- strict(arg_types(erlang, memory, 1), Xs,
- fun ([Type]) ->
- case t_is_atom(Type) of
- true -> t_non_neg_fixnum();
- false ->
- case t_is_list(Type) of
- true -> t_list(t_tuple([t_atom(), t_non_neg_fixnum()]));
- false ->
- t_sup(t_non_neg_fixnum(),
- t_list(t_tuple([t_atom(), t_non_neg_fixnum()])))
- end
- end
- end);
-type(erlang, module_loaded, 1, Xs) ->
- strict(arg_types(erlang, module_loaded, 1), Xs, fun (_) -> t_boolean() end);
-type(erlang, monitor, 2, Xs) ->
- strict(arg_types(erlang, monitor, 2), Xs, fun (_) -> t_reference() end);
-type(erlang, monitor_node, 2, Xs) ->
- strict(arg_types(erlang, monitor_node, 2), Xs,
- fun (_) -> t_atom('true') end);
-type(erlang, monitor_node, 3, Xs) ->
- strict(arg_types(erlang, monitor_node, 3), Xs,
- fun (_) -> t_atom('true') end);
type(erlang, nif_error, 1, _) ->
t_any(); % this BIF and the next one are stubs for NIFs and never return
type(erlang, nif_error, 2, Xs) ->
strict(arg_types(erlang, nif_error, 2), Xs, fun (_) -> t_any() end);
+%% Guard bif, needs to be here.
type(erlang, node, 0, _) -> t_node();
+%% Guard bif, needs to be here.
type(erlang, node, 1, Xs) ->
strict(arg_types(erlang, node, 1), Xs, fun (_) -> t_node() end);
type(erlang, nodes, 0, _) -> t_list(t_node());
-type(erlang, nodes, 1, Xs) ->
- strict(arg_types(erlang, nodes, 1), Xs, fun (_) -> t_list(t_node()) end);
-type(erlang, now, 0, _) ->
- t_timestamp();
-type(erlang, open_port, 2, Xs) ->
- strict(arg_types(erlang, open_port, 2), Xs, fun (_) -> t_port() end);
-type(erlang, phash, 2, Xs) ->
- strict(arg_types(erlang, phash, 2), Xs, fun (_) -> t_pos_integer() end);
-type(erlang, phash2, 1, Xs) ->
- strict(arg_types(erlang, phash2, 1), Xs, fun (_) -> t_non_neg_integer() end);
-type(erlang, phash2, 2, Xs) ->
- strict(arg_types(erlang, phash2, 2), Xs, fun (_) -> t_non_neg_integer() end);
-type(erlang, pid_to_list, 1, Xs) ->
- strict(arg_types(erlang, pid_to_list, 1), Xs, fun (_) -> t_string() end);
type(erlang, port_call, Arity, Xs) when Arity =:= 2; Arity =:= 3 ->
strict(arg_types(erlang, port_call, Arity), Xs, fun (_) -> t_any() end);
-type(erlang, port_close, 1, Xs) ->
- strict(arg_types(erlang, port_close, 1), Xs,
- fun (_) -> t_atom('true') end);
-type(erlang, port_command, 2, Xs) ->
- strict(arg_types(erlang, port_command, 2), Xs,
- fun (_) -> t_atom('true') end);
-type(erlang, port_command, 3, Xs) ->
- strict(arg_types(erlang, port_command, 3), Xs,
- fun (_) -> t_boolean() end);
-type(erlang, port_connect, 2, Xs) ->
- strict(arg_types(erlang, port_connect, 2), Xs,
- fun (_) -> t_atom('true') end);
-type(erlang, port_control, 3, Xs) ->
- strict(arg_types(erlang, port_control, 3), Xs,
- fun (_) -> t_sup(t_string(), t_binary()) end);
-type(erlang, port_get_data, 1, Xs) ->
- strict(arg_types(erlang, port_get_data, 1), Xs, fun (_) -> t_any() end);
type(erlang, port_info, 1, Xs) ->
strict(arg_types(erlang, port_info, 1), Xs,
fun (_) -> t_sup(t_atom('undefined'), t_list()) end);
@@ -1201,180 +797,11 @@ type(erlang, port_info, 2, Xs) ->
t_string()])])
end)
end);
-type(erlang, port_to_list, 1, Xs) ->
- strict(arg_types(erlang, port_to_list, 1), Xs, fun (_) -> t_string() end);
-type(erlang, ports, 0, _) -> t_list(t_port());
-type(erlang, port_set_data, 2, Xs) ->
- strict(arg_types(erlang, port_set_data, 2), Xs,
- fun (_) -> t_atom('true') end);
-type(erlang, pre_loaded, 0, _) -> t_list(t_atom());
-type(erlang, process_display, 2, _) -> t_atom('true');
-type(erlang, process_flag, 2, Xs) ->
- T_process_flag_returns = t_sup([t_boolean(), t_atom(), t_non_neg_integer()]),
- strict(arg_types(erlang, process_flag, 2), Xs,
- fun ([Flag, _Option]) ->
- case t_is_atom(Flag) of
- true ->
- case t_atom_vals(Flag) of
- ['error_handler'] -> t_atom();
- ['min_heap_size'] -> t_non_neg_integer();
- ['min_bin_vheap_size'] -> t_non_neg_integer();
- ['scheduler'] -> t_non_neg_integer();
- ['monitor_nodes'] -> t_boolean();
- ['priority'] -> t_process_priority_level();
- ['save_calls'] -> t_non_neg_integer();
- ['trap_exit'] -> t_boolean();
- ['sensitive'] -> t_boolean();
- List when is_list(List) ->
- T_process_flag_returns;
- unknown ->
- T_process_flag_returns
- end;
- false -> % XXX: over-approximation if Flag is tuple
- T_process_flag_returns
- end
- end);
-type(erlang, process_flag, 3, Xs) ->
- strict(arg_types(erlang, process_flag, 3), Xs,
- fun (_) -> t_non_neg_integer() end);
-type(erlang, process_info, 1, Xs) ->
- strict(arg_types(erlang, process_info, 1), Xs,
- fun (_) ->
- t_sup(t_list(t_tuple([t_pinfo(), t_any()])),
- t_atom('undefined'))
- end);
-type(erlang, process_info, 2, Xs) ->
- %% we define all normal return values: the return when the process exists
- %% t_nil() is the return for 'registered_name'; perhaps for more
- T_process_info_2_normal_returns =
- t_sup([t_tuple([t_pinfo_item(), t_any()]), t_nil()]),
- strict(arg_types(erlang, process_info, 2), Xs,
- fun ([_Pid, InfoItem]) ->
- Ret = case t_is_atom(InfoItem) of
- true ->
- case t_atom_vals(InfoItem) of
- ['backtrace'] -> t_tuple([InfoItem, t_binary()]);
- ['current_function'] -> t_tuple([InfoItem, t_mfa()]);
- ['dictionary'] -> t_tuple([InfoItem, t_list()]);
- ['error_handler'] -> t_tuple([InfoItem, t_atom()]);
- ['garbage_collection'] ->
- t_tuple([InfoItem, t_list()]);
- ['group_leader'] -> t_tuple([InfoItem, t_pid()]);
- ['heap_size'] ->
- t_tuple([InfoItem, t_non_neg_integer()]);
- ['initial_call'] -> t_tuple([InfoItem, t_mfa()]);
- ['last_calls'] ->
- t_tuple([InfoItem,
- t_sup(t_atom('false'), t_list())]);
- ['links'] -> t_tuple([InfoItem, t_list(t_pid())]);
- ['memory'] ->
- t_tuple([InfoItem, t_non_neg_integer()]);
- ['message_queue_len'] ->
- t_tuple([InfoItem, t_non_neg_integer()]);
- ['messages'] -> t_tuple([InfoItem, t_list()]);
- ['monitored_by'] ->
- t_tuple([InfoItem, t_list(t_pid())]);
- ['monitors'] ->
- t_tuple([InfoItem,
- t_list(t_sup(t_tuple([t_atom('process'),
- t_pid()]),
- t_tuple([t_atom('process'),
- t_tuple([t_atom(),
- t_atom()])])))]);
- ['priority'] ->
- t_tuple([InfoItem, t_process_priority_level()]);
- ['reductions'] ->
- t_tuple([InfoItem, t_non_neg_integer()]);
- ['registered_name'] ->
- t_sup(t_tuple([InfoItem, t_atom()]), t_nil());
- ['sequential_trace_token'] ->
- t_tuple([InfoItem, t_any()]); %% Underspecified
- ['stack_size'] ->
- t_tuple([InfoItem, t_non_neg_integer()]);
- ['status'] ->
- t_tuple([InfoItem, t_process_status()]);
- ['suspending'] ->
- t_tuple([InfoItem,
- t_list(t_tuple([t_pid(),
- t_non_neg_integer(),
- t_non_neg_integer()]))]);
- ['total_heap_size'] ->
- t_tuple([InfoItem, t_non_neg_integer()]);
- ['trap_exit'] ->
- t_tuple([InfoItem, t_boolean()]);
- List when is_list(List) ->
- T_process_info_2_normal_returns;
- unknown ->
- T_process_info_2_normal_returns
- end;
- false ->
- case t_is_list(InfoItem) of
- true ->
- t_list(t_tuple([t_pinfo_item(), t_any()]));
- false ->
- t_sup(T_process_info_2_normal_returns,
- t_list(t_tuple([t_pinfo_item(), t_any()])))
- end
- end,
- t_sup([Ret, t_atom('undefined')])
- end);
-type(erlang, processes, 0, _) -> t_list(t_pid());
-type(erlang, purge_module, 1, Xs) ->
- strict(arg_types(erlang, purge_module, 1), Xs,
- fun (_) -> t_atom('true') end);
-type(erlang, put, 2, Xs) ->
- strict(arg_types(erlang, put, 2), Xs, fun (_) -> t_any() end);
-type(erlang, raise, 3, _) -> t_none();
-type(erlang, read_timer, 1, Xs) ->
- strict(arg_types(erlang, read_timer, 1), Xs,
- fun (_) -> t_sup(t_non_neg_integer(), t_atom('false')) end);
-type(erlang, ref_to_list, 1, Xs) ->
- strict(arg_types(erlang, ref_to_list, 1), Xs, fun (_) -> t_string() end);
-type(erlang, register, 2, Xs) ->
- strict(arg_types(erlang, register, 2), Xs, fun (_) -> t_atom('true') end);
-type(erlang, registered, 0, _) -> t_list(t_atom());
-type(erlang, resume_process, 1, Xs) ->
- strict(arg_types(erlang, resume_process, 1), Xs,
- fun (_) -> t_any() end); %% TODO: overapproximation -- fix this
+%% Guard bif, needs to be here.
type(erlang, round, 1, Xs) ->
strict(arg_types(erlang, round, 1), Xs, fun (_) -> t_integer() end);
-type(erlang, posixtime_to_universaltime, 1, Xs) ->
- strict(arg_types(erlang, posixtime_to_universaltime, 1), Xs,
- fun(_) -> t_tuple([t_date(), t_time()]) end);
+%% Guard bif, needs to be here.
type(erlang, self, 0, _) -> t_pid();
-type(erlang, send, 2, Xs) -> type(erlang, '!', 2, Xs); % alias
-type(erlang, send, 3, Xs) ->
- strict(arg_types(erlang, send, 3), Xs,
- fun (_) -> t_sup(t_atom('ok'), t_sendoptions()) end);
-type(erlang, send_after, 3, Xs) ->
- strict(arg_types(erlang, send_after, 3), Xs, fun (_) -> t_reference() end);
-type(erlang, seq_trace, 2, Xs) ->
- strict(arg_types(erlang, seq_trace, 2), Xs,
- fun (_) -> t_sup(t_seq_trace_info_returns(), t_tuple(5)) end);
-type(erlang, seq_trace_info, 1, Xs) ->
- strict(arg_types(erlang, seq_trace_info, 1), Xs,
- fun ([Item]) ->
- case t_atom_vals(Item) of
- ['label'] ->
- t_sup(t_tuple([Item, t_non_neg_integer()]), t_nil());
- ['serial'] ->
- t_sup(t_tuple([Item, t_tuple([t_non_neg_integer(),
- t_non_neg_integer()])]),
- t_nil());
- ['send'] -> t_tuple([Item, t_boolean()]);
- ['receive'] -> t_tuple([Item, t_boolean()]);
- ['print'] -> t_tuple([Item, t_boolean()]);
- ['timestamp'] -> t_tuple([Item, t_boolean()]);
- List when is_list(List) ->
- t_seq_trace_info_returns();
- unknown ->
- t_seq_trace_info_returns()
- end
- end);
-type(erlang, seq_trace_print, 1, Xs) ->
- strict(arg_types(erlang, seq_trace_print, 1), Xs, fun (_) -> t_boolean() end);
-type(erlang, seq_trace_print, 2, Xs) ->
- strict(arg_types(erlang, seq_trace_print, 2), Xs, fun (_) -> t_boolean() end);
type(erlang, set_cookie, 2, Xs) ->
strict(arg_types(erlang, set_cookie, 2), Xs, fun (_) -> t_atom('true') end);
type(erlang, setelement, 3, Xs) ->
@@ -1408,144 +835,22 @@ type(erlang, setelement, 3, Xs) ->
t_sup([type(erlang, setelement, 3, [X1, Y, X3]) || Y <- Ts])
end
end);
-type(erlang, setnode, 2, Xs) ->
- strict(arg_types(erlang, setnode, 2), Xs, fun (_) -> t_atom('true') end);
-type(erlang, setnode, 3, Xs) ->
- strict(arg_types(erlang, setnode, 3), Xs, fun (_) -> t_atom('true') end);
+%% Guard bif, needs to be here.
type(erlang, size, 1, Xs) ->
strict(arg_types(erlang, size, 1), Xs, fun (_) -> t_non_neg_integer() end);
type(erlang, spawn, 1, Xs) ->
strict(arg_types(erlang, spawn, 1), Xs, fun (_) -> t_pid() end);
type(erlang, spawn, 2, Xs) ->
strict(arg_types(erlang, spawn, 2), Xs, fun (_) -> t_pid() end);
-type(erlang, spawn, 3, Xs) ->
- strict(arg_types(erlang, spawn, 3), Xs, fun (_) -> t_pid() end);
type(erlang, spawn, 4, Xs) ->
strict(arg_types(erlang, spawn, 4), Xs, fun (_) -> t_pid() end);
type(erlang, spawn_link, 1, Xs) -> type(erlang, spawn, 1, Xs); % same
type(erlang, spawn_link, 2, Xs) -> type(erlang, spawn, 2, Xs); % same
-type(erlang, spawn_link, 3, Xs) -> type(erlang, spawn, 3, Xs); % same
type(erlang, spawn_link, 4, Xs) -> type(erlang, spawn, 4, Xs); % same
-type(erlang, spawn_opt, 1, Xs) ->
- strict(arg_types(erlang, spawn_opt, 1), Xs,
- fun ([Tuple]) ->
- Fun = fun (TS) ->
- [_, _, _, List] = t_tuple_args(TS),
- t_spawn_opt_return(List)
- end,
- t_sup([Fun(TS) || TS <- t_tuple_subtypes(Tuple)])
- end);
-type(erlang, spawn_opt, 2, Xs) ->
- strict(arg_types(erlang, spawn_opt, 2), Xs,
- fun ([_, List]) -> t_spawn_opt_return(List) end);
-type(erlang, spawn_opt, 3, Xs) ->
- strict(arg_types(erlang, spawn_opt, 3), Xs,
- fun ([_, _, List]) -> t_spawn_opt_return(List) end);
-type(erlang, spawn_opt, 4, Xs) ->
- strict(arg_types(erlang, spawn_opt, 4), Xs,
- fun ([_, _, _, List]) -> t_spawn_opt_return(List) end);
-type(erlang, split_binary, 2, Xs) ->
- strict(arg_types(erlang, split_binary, 2), Xs,
- fun (_) -> t_tuple([t_binary(), t_binary()]) end);
-type(erlang, start_timer, 3, Xs) ->
- strict(arg_types(erlang, start_timer, 3), Xs, fun (_) -> t_reference() end);
-type(erlang, statistics, 1, Xs) ->
- strict(arg_types(erlang, statistics, 1), Xs,
- fun ([Type]) ->
- T_statistics_1 = t_sup([t_non_neg_integer(),
- t_tuple([t_non_neg_integer(),
- t_non_neg_integer()]),
- %% When called with the argument 'io'.
- t_tuple([t_tuple([t_atom('input'),
- t_non_neg_integer()]),
- t_tuple([t_atom('output'),
- t_non_neg_integer()])]),
- t_tuple([t_non_neg_integer(),
- t_non_neg_integer(),
- t_non_neg_integer()])]),
- case t_atom_vals(Type) of
- ['context_switches'] ->
- t_tuple([t_non_neg_integer(), t_integer(0)]);
- ['exact_reductions'] ->
- t_tuple([t_non_neg_integer(), t_non_neg_integer()]);
- ['garbage_collection'] ->
- t_tuple([t_non_neg_integer(),
- t_non_neg_integer(),
- t_integer(0)]);
- ['io'] ->
- t_tuple([t_tuple([t_atom('input'), t_non_neg_integer()]),
- t_tuple([t_atom('output'), t_non_neg_integer()])]);
- ['reductions'] ->
- t_tuple([t_non_neg_integer(), t_non_neg_integer()]);
- ['run_queue'] ->
- t_non_neg_integer();
- ['runtime'] ->
- t_tuple([t_non_neg_integer(), t_integer(0)]);
- ['wall_clock'] ->
- t_tuple([t_non_neg_integer(), t_integer(0)]);
- List when is_list(List) ->
- T_statistics_1;
- unknown ->
- T_statistics_1
- end
- end);
type(erlang, subtract, 2, Xs) -> type(erlang, '--', 2, Xs); % alias
type(erlang, suspend_process, 1, Xs) ->
strict(arg_types(erlang, suspend_process, 1), Xs,
fun (_) -> t_atom('true') end);
-type(erlang, suspend_process, 2, Xs) ->
- strict(arg_types(erlang, suspend_process, 2), Xs,
- fun (_) -> t_boolean() end);
-type(erlang, system_flag, 2, Xs) ->
- strict(arg_types(erlang, system_flag, 2), Xs,
- fun ([Flag,_Value]) ->
- %% this provides an overapproximation of all return values
- T_system_flag_2 = t_sup([t_boolean(),
- t_integer(),
- t_sequential_tracer(),
- t_system_cpu_topology(),
- t_system_multi_scheduling()]),
- case t_is_atom(Flag) of
- true ->
- case t_atom_vals(Flag) of
- ['backtrace_depth'] ->
- t_non_neg_fixnum();
- ['cpu_topology'] ->
- t_system_cpu_topology();
- ['debug_flags'] ->
- t_atom('true');
- ['display_items'] ->
- t_non_neg_fixnum();
- ['fullsweep_after'] ->
- t_non_neg_fixnum();
- ['min_heap_size'] ->
- t_non_neg_fixnum();
- ['min_bin_vheap_size'] ->
- t_non_neg_fixnum();
- ['multi_scheduling'] ->
- t_system_multi_scheduling();
- ['schedulers_online'] ->
- t_pos_fixnum();
- ['scheduler_bind_type'] ->
- t_scheduler_bind_type_results();
- ['sequential_tracer'] ->
- t_sequential_tracer();
- ['trace_control_word'] ->
- t_integer();
- List when is_list(List) ->
- T_system_flag_2;
- unknown ->
- T_system_flag_2
- end;
- false ->
- case t_is_integer(Flag) of % SHOULD BE: t_is_fixnum
- true ->
- t_atom('true');
- false ->
- T_system_flag_2
- end
- end
- end);
type(erlang, system_info, 1, Xs) ->
strict(arg_types(erlang, system_info, 1), Xs,
fun ([Type]) ->
@@ -1565,12 +870,8 @@ type(erlang, system_info, 1, Xs) ->
t_list(t_tuple([t_atom(),
t_list(t_tuple([t_atom(),
t_any()]))]))]);
- ['build_type'] ->
- t_system_build_type_return();
['break_ignored'] ->
t_boolean();
- ['c_compiler_used'] ->
- t_tuple([t_atom(), t_any()]);
['cpu_topology'] ->
t_system_cpu_topology();
['compat_rel'] ->
@@ -1582,9 +883,7 @@ type(erlang, system_info, 1, Xs) ->
['dist'] ->
t_binary();
['dist_ctrl'] ->
- t_list(t_tuple([t_atom(), t_sup([t_pid(), t_port])]));
- ['driver_version'] ->
- t_string();
+ t_list(t_tuple([t_atom(), t_sup([t_pid(), t_port()])]));
%% elib_malloc is intentionally not included,
%% because it scheduled for removal in R15.
['endian'] ->
@@ -1604,27 +903,18 @@ type(erlang, system_info, 1, Xs) ->
t_binary();
['internal_cpu_topology'] -> %% Undocumented internal feature
t_internal_cpu_topology();
- ['kernel_poll'] ->
- t_boolean();
['loaded'] ->
t_binary();
['logical_processors'] ->
t_non_neg_fixnum();
['machine'] ->
t_string();
- ['min_heap_size'] ->
- t_tuple([t_atom('min_heap_size'),
- t_non_neg_integer()]);
- ['min_bin_vheap_size'] ->
- t_tuple([t_atom('min_bin_vheap_size'),
- t_non_neg_integer()]);
['multi_scheduling'] ->
t_system_multi_scheduling();
['multi_scheduling_blockers'] ->
t_list(t_pid());
['os_type'] ->
t_tuple([t_sup([t_atom('unix'),
- t_atom('vxworks'),
t_atom('win32')]),
t_atom()]);
['os_version'] ->
@@ -1632,8 +922,6 @@ type(erlang, system_info, 1, Xs) ->
t_non_neg_fixnum(),
t_non_neg_fixnum()]),
t_string());
- ['otp_release'] ->
- t_string();
['process_count'] ->
t_non_neg_fixnum();
['process_limit'] ->
@@ -1663,8 +951,6 @@ type(erlang, system_info, 1, Xs) ->
t_non_neg_fixnum();
['trace_control_word'] ->
t_integer();
- ['update_cpu_info'] ->
- t_sup([t_atom('changed'), t_atom('unchanged')]);
['version'] ->
t_string();
['wordsize'] ->
@@ -1678,56 +964,13 @@ type(erlang, system_info, 1, Xs) ->
t_any() %% overapproximation as the return value might change
end
end);
-type(erlang, system_monitor, 0, Xs) ->
- strict(arg_types(erlang, system_monitor, 0), Xs,
- fun (_) -> t_system_monitor_settings() end);
-type(erlang, system_monitor, 1, Xs) ->
- strict(arg_types(erlang, system_monitor, 1), Xs,
- fun (_) -> t_system_monitor_settings() end);
-type(erlang, system_monitor, 2, Xs) ->
- strict(arg_types(erlang, system_monitor, 2), Xs,
- fun (_) -> t_system_monitor_settings() end);
-type(erlang, system_profile, 0, _) ->
- t_system_profile_return();
-type(erlang, system_profile, 2, Xs) ->
- strict(arg_types(erlang, system_profile, 2), Xs,
- fun (_) -> t_system_profile_return() end);
-type(erlang, term_to_binary, 1, Xs) ->
- strict(arg_types(erlang, term_to_binary, 1), Xs, fun (_) -> t_binary() end);
-type(erlang, term_to_binary, 2, Xs) ->
- strict(arg_types(erlang, term_to_binary, 2), Xs, fun (_) -> t_binary() end);
-type(erlang, time, 0, _) ->
- t_tuple([t_non_neg_integer(), t_non_neg_integer(), t_non_neg_integer()]);
+%% Guard bif, needs to be here.
type(erlang, tl, 1, Xs) ->
strict(arg_types(erlang, tl, 1), Xs, fun ([X]) -> t_cons_tl(X) end);
-type(erlang, trace, 3, Xs) ->
- strict(arg_types(erlang, trace, 3), Xs, fun (_) -> t_integer() end);
-type(erlang, trace_delivered, 1, Xs) ->
- strict(arg_types(erlang, trace_delivered, 1), Xs,
- fun (_) -> t_reference() end);
-type(erlang, trace_info, 2, Xs) ->
- strict(arg_types(erlang, trace_info, 2), Xs,
- fun (_) ->
- t_tuple([t_atom(),
- t_sup([%% the following is info about a PID
- t_list(t_atom()), t_pid(), t_port(),
- %% the following is info about a func
- t_atom('global'), t_atom('local'),
- t_atom('false'), t_atom('true'),
- t_list(), t_pid(), t_port(),
- t_integer(),
- t_list(t_tuple([t_atom(), t_any()])),
- %% and this is the 'not found' value
- t_atom('undefined')])])
- end);
-type(erlang, trace_pattern, 2, Xs) ->
- strict(arg_types(erlang, trace_pattern, 2), Xs,
- fun (_) -> t_non_neg_fixnum() end); %% num of MFAs that match pattern
-type(erlang, trace_pattern, 3, Xs) ->
- strict(arg_types(erlang, trace_pattern, 3), Xs,
- fun (_) -> t_non_neg_fixnum() end); %% num of MFAs that match pattern
+%% Guard bif, needs to be here.
type(erlang, trunc, 1, Xs) ->
strict(arg_types(erlang, trunc, 1), Xs, fun (_) -> t_integer() end);
+%% Guard bif, needs to be here.
type(erlang, tuple_size, 1, Xs) ->
strict(arg_types(erlang, tuple_size, 1), Xs, fun (_) -> t_non_neg_integer() end);
type(erlang, tuple_to_list, 1, Xs) ->
@@ -1750,266 +993,10 @@ type(erlang, tuple_to_list, 1, Xs) ->
end
end
end);
-type(erlang, universaltime, 0, _) ->
- t_tuple([t_date(), t_time()]);
-type(erlang, universaltime_to_localtime, 1, Xs) ->
- strict(arg_types(erlang, universaltime_to_localtime, 1), Xs,
- fun ([T]) -> T end);
-type(erlang, universaltime_to_posixtime, 1, Xs) ->
- strict(arg_types(erlang, universaltime_to_posixtime,1), Xs,
- fun(_) -> t_integer() end);
-type(erlang, unlink, 1, Xs) ->
- strict(arg_types(erlang, unlink, 1), Xs, fun (_) -> t_atom('true') end);
-type(erlang, unregister, 1, Xs) ->
- strict(arg_types(erlang, unregister, 1), Xs, fun (_) -> t_atom('true') end);
-type(erlang, whereis, 1, Xs) ->
- strict(arg_types(erlang, whereis, 1), Xs,
- fun (_) -> t_sup([t_pid(), t_port(), t_atom('undefined')]) end);
type(erlang, yield, 0, _) -> t_atom('true');
-%%-- erl_prim_loader ----------------------------------------------------------
-type(erl_prim_loader, get_file, 1, Xs) ->
- strict(arg_types(erl_prim_loader, get_file, 1), Xs,
- fun (_) ->
- t_sup(t_tuple([t_atom('ok'), t_binary(), t_string()]),
- t_atom('error'))
- end);
-type(erl_prim_loader, get_path, 0, _) ->
- t_tuple([t_atom('ok'), t_list(t_string())]);
-type(erl_prim_loader, set_path, 1, Xs) ->
- strict(arg_types(erl_prim_loader, set_path, 1), Xs,
- fun (_) -> t_atom('ok') end);
-%%-- error_logger -------------------------------------------------------------
-type(error_logger, warning_map, 0, _) ->
- t_sup([t_atom('info'), t_atom('warning'), t_atom('error')]);
-%%-- erts_debug ---------------------------------------------------------------
-type(erts_debug, breakpoint, 2, Xs) ->
- strict(arg_types(erts_debug, breakpoint, 2), Xs, fun (_) -> t_fixnum() end);
-type(erts_debug, disassemble, 1, Xs) ->
- strict(arg_types(erts_debug, disassemble, 1), Xs,
- fun (_) -> t_sup([t_atom('false'),
- t_atom('undef'),
- t_tuple([t_integer(), t_binary(), t_mfa()])]) end);
-type(erts_debug, display, 1, _) ->
- t_string();
-type(erts_debug, dist_ext_to_term, 2, Xs) ->
- strict(arg_types(erts_debug, dist_ext_to_term, 2), Xs,
- fun (_) -> t_any() end);
-type(erts_debug, dump_monitors, 1, Xs) ->
- strict(arg_types(erts_debug, dump_monitors, 1), Xs,
- fun(_) -> t_atom('true') end);
-type(erts_debug, dump_links, 1, Xs) ->
- strict(arg_types(erts_debug, dump_links, 1), Xs,
- fun(_) -> t_atom('true') end);
-type(erts_debug, flat_size, 1, Xs) ->
- strict(arg_types(erts_debug, flat_size, 1), Xs, fun (_) -> t_integer() end);
-type(erts_debug, get_internal_state, 1, _) ->
- t_any();
-type(erts_debug, instructions, 0, _) ->
- t_list(t_list(t_byte()));
-type(erts_debug, lock_counters, 1, Xs) ->
- strict(arg_types(erts_debug, lock_counters, 1), Xs,
- fun ([Arg]) ->
- case t_is_atom(Arg) of
- true ->
- case t_atom_vals(Arg) of
- ['enabled'] -> t_boolean();
- ['info'] -> t_any();
- ['clear'] -> t_atom(ok);
- _ -> t_sup([t_boolean(), t_any(), t_atom('ok')])
- end;
- false ->
- case t_is_tuple(Arg) of
- true -> t_boolean();
- false -> t_sup([t_boolean(), t_any(), t_atom('ok')])
- end
- end
- end);
-type(erts_debug, same, 2, Xs) ->
- strict(arg_types(erts_debug, same, 2), Xs, fun (_) -> t_boolean() end);
-type(erts_debug, set_internal_state, 2, _) ->
- t_any();
%%-- ets ----------------------------------------------------------------------
-type(ets, all, 0, _) ->
- t_list(t_tab());
-type(ets, delete, 1, Xs) ->
- strict(arg_types(ets, delete, 1), Xs, fun (_) -> t_atom('true') end);
-type(ets, delete, 2, Xs) ->
- strict(arg_types(ets, delete, 2), Xs, fun (_) -> t_atom('true') end);
-type(ets, delete_all_objects, 1, Xs) ->
- strict(arg_types(ets, delete_all_objects, 1), Xs,
- fun (_) -> t_atom('true') end);
-type(ets, delete_object, 2, Xs) ->
- strict(arg_types(ets, delete_object, 2), Xs, fun (_) -> t_atom('true') end);
-type(ets, first, 1, Xs) ->
- strict(arg_types(ets, first, 1), Xs, fun (_) -> t_any() end);
-type(ets, give_away, 3, Xs) ->
- strict(arg_types(ets, give_away, 3), Xs, fun (_) -> t_atom('true') end);
-type(ets, info, 1, Xs) ->
- strict(arg_types(ets, info, 1), Xs,
- fun (_) ->
- t_sup(t_list(t_tuple([t_ets_info_items(), t_any()])),
- t_atom('undefined'))
- end);
-type(ets, info, 2, Xs) ->
- strict(arg_types(ets, info, 2), Xs, fun (_) -> t_any() end);
-type(ets, insert, 2, Xs) ->
- strict(arg_types(ets, insert, 2), Xs, fun (_) -> t_atom('true') end);
-type(ets, insert_new, 2, Xs) ->
- strict(arg_types(ets, insert_new, 2), Xs, fun (_) -> t_boolean() end);
-type(ets, is_compiled_ms, 1, Xs) ->
- strict(arg_types(ets, is_compiled_ms, 1), Xs, fun (_) -> t_boolean() end);
-type(ets, last, 1, Xs) ->
- type(ets, first, 1, Xs);
-type(ets, lookup, 2, Xs) ->
- strict(arg_types(ets, lookup, 2), Xs, fun (_) -> t_list(t_tuple()) end);
-type(ets, lookup_element, 3, Xs) ->
- strict(arg_types(ets, lookup_element, 3), Xs, fun (_) -> t_any() end);
-type(ets, match, 1, Xs) ->
- strict(arg_types(ets, match, 1), Xs, fun (_) -> t_matchres() end);
-type(ets, match, 2, Xs) ->
- strict(arg_types(ets, match, 2), Xs, fun (_) -> t_list() end);
-type(ets, match, 3, Xs) ->
- strict(arg_types(ets, match, 3), Xs, fun (_) -> t_matchres() end);
-type(ets, match_object, 1, Xs) -> type(ets, match, 1, Xs);
-type(ets, match_object, 2, Xs) -> type(ets, match, 2, Xs);
-type(ets, match_object, 3, Xs) -> type(ets, match, 3, Xs);
-type(ets, match_spec_compile, 1, Xs) ->
- strict(arg_types(ets, match_spec_compile, 1), Xs, fun (_) -> t_any() end);
-type(ets, match_spec_run_r, 3, Xs) ->
- strict(arg_types(ets, match_spec_run_r, 3), Xs, fun (_) -> t_list() end);
-type(ets, member, 2, Xs) ->
- strict(arg_types(ets, member, 2), Xs, fun (_) -> t_boolean() end);
-type(ets, new, 2, Xs) ->
- strict(arg_types(ets, new, 2), Xs, fun (_) -> t_tab() end);
-type(ets, next, 2, Xs) ->
- strict(arg_types(ets, next, 2), Xs,
- %% t_any below stands for: term() | '$end_of_table'
- fun (_) -> t_any() end);
-type(ets, prev, 2, Xs) -> type(ets, next, 2, Xs);
type(ets, rename, 2, Xs) ->
strict(arg_types(ets, rename, 2), Xs, fun ([_, Name]) -> Name end);
-type(ets, safe_fixtable, 2, Xs) ->
- strict(arg_types(ets, safe_fixtable, 2), Xs, fun (_) -> t_atom('true') end);
-type(ets, select, 1, Xs) ->
- strict(arg_types(ets, select, 1), Xs, fun (_) -> t_matchres() end);
-type(ets, select, 2, Xs) ->
- strict(arg_types(ets, select, 2), Xs, fun (_) -> t_list() end);
-type(ets, select, 3, Xs) ->
- strict(arg_types(ets, select, 3), Xs, fun (_) -> t_matchres() end);
-type(ets, select_count, 2, Xs) ->
- strict(arg_types(ets, select_count, 2), Xs,
- fun (_) -> t_non_neg_fixnum() end);
-type(ets, select_delete, 2, Xs) ->
- strict(arg_types(ets, select_delete, 2), Xs,
- fun (_) -> t_non_neg_fixnum() end);
-type(ets, select_reverse, 1, Xs) -> type(ets, select, 1, Xs);
-type(ets, select_reverse, 2, Xs) -> type(ets, select, 2, Xs);
-type(ets, select_reverse, 3, Xs) -> type(ets, select, 3, Xs);
-type(ets, setopts, 2, Xs) ->
- strict(arg_types(ets, setopts, 2), Xs, fun (_) -> t_atom('true') end);
-type(ets, slot, 2, Xs) ->
- strict(arg_types(ets, slot, 2), Xs,
- fun (_) -> t_sup(t_list(t_tuple()), t_atom('$end_of_table')) end);
-type(ets, update_counter, 3, Xs) ->
- strict(arg_types(ets, update_counter, 3), Xs,
- fun ([_, _, Op]) ->
- case t_is_integer(Op) of
- true -> t_integer();
- false ->
- case t_is_tuple(Op) of
- true -> t_integer();
- false ->
- case t_is_list(Op) of
- true -> t_list(t_integer());
- false ->
- case t_is_nil(Op) of
- true -> t_nil();
- false -> t_sup([t_integer(), t_list(t_integer())])
- end
- end
- end
- end
- end);
-type(ets, update_element, 3, Xs) ->
- strict(arg_types(ets, update_element, 3), Xs, fun (_) -> t_boolean() end);
-%%-- file ---------------------------------------------------------------------
-type(file, native_name_encoding, 0, _) ->
- t_file_encoding();
-%%-- prim_file ----------------------------------------------------------------
-type(prim_file, internal_name2native, 1, Xs) ->
- strict(arg_types(prim_file, internal_name2native, 1), Xs,
- fun (_) -> t_binary() end);
-type(prim_file, internal_native2name, 1, Xs) ->
- strict(arg_types(prim_file, internal_native2name, 1), Xs,
- fun (_) -> t_prim_file_name() end);
-type(prim_file, internal_normalize_utf8, 1, Xs) ->
- strict(arg_types(prim_file, internal_normalize_utf8, 1), Xs,
- fun (_) -> t_unicode_string() end);
-%%-- gen_tcp ------------------------------------------------------------------
-%% NOTE: All type information for this module added to avoid loss of precision
-type(gen_tcp, accept, 1, Xs) ->
- strict(arg_types(gen_tcp, accept, 1), Xs, fun (_) -> t_gen_tcp_accept() end);
-type(gen_tcp, accept, 2, Xs) ->
- strict(arg_types(gen_tcp, accept, 2), Xs, fun (_) -> t_gen_tcp_accept() end);
-type(gen_tcp, connect, 3, Xs) ->
- strict(arg_types(gen_tcp, connect, 3), Xs,
- fun (_) ->
- t_sup(t_tuple([t_atom('ok'), t_socket()]),
- t_tuple([t_atom('error'), t_inet_posix_error()]))
- end);
-type(gen_tcp, connect, 4, Xs) ->
- strict(arg_types(gen_tcp, connect, 4), Xs,
- fun (_) ->
- t_sup(t_tuple([t_atom('ok'), t_socket()]),
- t_tuple([t_atom('error'), t_inet_posix_error()]))
- end);
-type(gen_tcp, listen, 2, Xs) ->
- strict(arg_types(gen_tcp, listen, 2), Xs,
- fun (_) ->
- t_sup(t_tuple([t_atom('ok'), t_socket()]),
- t_tuple([t_atom('error'), t_inet_posix_error()]))
- end);
-type(gen_tcp, recv, 2, Xs) ->
- strict(arg_types(gen_tcp, recv, 2), Xs, fun (_) -> t_gen_tcp_recv() end);
-type(gen_tcp, recv, 3, Xs) ->
- strict(arg_types(gen_tcp, recv, 3), Xs, fun (_) -> t_gen_tcp_recv() end);
-type(gen_tcp, send, 2, Xs) ->
- strict(arg_types(gen_tcp, send, 2), Xs,
- fun (_) ->
- t_sup(t_atom('ok'),
- t_tuple([t_atom('error'), t_inet_posix_error()]))
- end);
-type(gen_tcp, shutdown, 2, Xs) ->
- strict(arg_types(gen_tcp, shutdown, 2), Xs,
- fun (_) ->
- t_sup(t_atom('ok'),
- t_tuple([t_atom('error'), t_inet_posix_error()]))
- end);
-%%-- gen_udp ------------------------------------------------------------------
-%% NOTE: All type information for this module added to avoid loss of precision
-type(gen_udp, open, 1, Xs) ->
- strict(arg_types(gen_udp, open, 1), Xs,
- fun (_) ->
- t_sup(t_tuple([t_atom('ok'), t_socket()]),
- t_tuple([t_atom('error'), t_inet_posix_error()]))
- end);
-type(gen_udp, open, 2, Xs) ->
- strict(arg_types(gen_udp, open, 2), Xs,
- fun (_) ->
- t_sup(t_tuple([t_atom('ok'), t_socket()]),
- t_tuple([t_atom('error'), t_inet_posix_error()]))
- end);
-type(gen_udp, recv, 2, Xs) ->
- strict(arg_types(gen_udp, recv, 2), Xs, fun (_) -> t_gen_udp_recv() end);
-type(gen_udp, recv, 3, Xs) ->
- strict(arg_types(gen_udp, recv, 3), Xs, fun (_) -> t_gen_udp_recv() end);
-type(gen_udp, send, 4, Xs) ->
- strict(arg_types(gen_udp, send, 4), Xs,
- fun (_) ->
- t_sup(t_atom('ok'),
- t_tuple([t_atom('error'), t_sup(t_atom('not_owner'),
- t_inet_posix_error())]))
- end);
%%-- hipe_bifs ----------------------------------------------------------------
type(hipe_bifs, add_ref, 2, Xs) ->
strict(arg_types(hipe_bifs, add_ref, 2), Xs, fun (_) -> t_nil() end);
@@ -2132,26 +1119,6 @@ type(hipe_bifs, write_u32, 2, Xs) ->
strict(arg_types(hipe_bifs, write_u32, 2), Xs, fun (_) -> t_nil() end);
type(hipe_bifs, write_u64, 2, Xs) ->
strict(arg_types(hipe_bifs, write_u64, 2), Xs, fun (_) -> t_nil() end);
-%%-- io -----------------------------------------------------------------------
-type(io, format, 1, Xs) ->
- strict(arg_types(io, format, 1), Xs, fun (_) -> t_atom('ok') end);
-type(io, format, 2, Xs) ->
- strict(arg_types(io, format, 2), Xs, fun (_) -> t_atom('ok') end);
-type(io, format, 3, Xs) ->
- strict(arg_types(io, format, 3), Xs, fun (_) -> t_atom('ok') end);
-type(io, fwrite, 1, Xs) -> type(io, format, 1, Xs); % same
-type(io, fwrite, 2, Xs) -> type(io, format, 2, Xs); % same
-type(io, fwrite, 3, Xs) -> type(io, format, 3, Xs); % same
-type(io, put_chars, 1, Xs) ->
- strict(arg_types(io, put_chars, 1), Xs, fun (_) -> t_atom('ok') end);
-type(io, put_chars, 2, Xs) ->
- strict(arg_types(io, put_chars, 2), Xs, fun (_) -> t_atom('ok') end);
-%%-- io_lib -------------------------------------------------------------------
-type(io_lib, format, 2, Xs) ->
- strict(arg_types(io_lib, format, 2), Xs,
- %% t_list() because the character list might be arbitrarily nested
- fun (_) -> t_list(t_sup(t_char(), t_list())) end);
-type(io_lib, fwrite, 2, Xs) -> type(io_lib, format, 2, Xs); % same
%%-- lists --------------------------------------------------------------------
type(lists, all, 2, Xs) ->
strict(arg_types(lists, all, 2), Xs,
@@ -2521,8 +1488,6 @@ type(lists, merge, 2, Xs) ->
end
end
end);
-%% type(lists, merge, 3, Xs) ->
-%% type(lists, merge3, 3, Xs) ->
type(lists, min, 1, Xs) ->
strict(arg_types(lists, min, 1), Xs, fun ([L]) -> t_list_elements(L) end);
type(lists, nth, 2, Xs) ->
@@ -2559,10 +1524,6 @@ type(lists, reverse, 1, Xs) ->
strict(arg_types(lists, reverse, 1), Xs, fun ([X]) -> X end);
type(lists, reverse, 2, Xs) ->
type(erlang, '++', 2, Xs); % reverse-onto is just like append
-type(lists, seq, 2, Xs) ->
- strict(arg_types(lists, seq, 2), Xs, fun (_) -> t_list(t_integer()) end);
-type(lists, seq, 3, Xs) ->
- strict(arg_types(lists, seq, 3), Xs, fun (_) -> t_list(t_integer()) end);
type(lists, sort, 1, Xs) ->
strict(arg_types(lists, sort, 1), Xs, fun ([X]) -> X end);
type(lists, sort, 2, Xs) ->
@@ -2669,95 +1630,7 @@ type(lists, zipwith, 3, Xs) ->
type(lists, zipwith3, 4, Xs) ->
strict(arg_types(lists, zipwith3, 4), Xs,
fun ([F,_As,_Bs,_Cs]) -> t_sup(t_list(t_fun_range(F)), t_nil()) end);
-%%-- math ---------------------------------------------------------------------
-type(math, acos, 1, Xs) ->
- strict(arg_types(math, acos, 1), Xs, fun (_) -> t_float() end);
-type(math, acosh, 1, Xs) ->
- strict(arg_types(math, acosh, 1), Xs, fun (_) -> t_float() end);
-type(math, asin, 1, Xs) ->
- strict(arg_types(math, asin, 1), Xs, fun (_) -> t_float() end);
-type(math, asinh, 1, Xs) ->
- strict(arg_types(math, asinh, 1), Xs, fun (_) -> t_float() end);
-type(math, atan, 1, Xs) ->
- strict(arg_types(math, atan, 1), Xs, fun (_) -> t_float() end);
-type(math, atan2, 2, Xs) ->
- strict(arg_types(math, atan2, 2), Xs, fun (_) -> t_float() end);
-type(math, atanh, 1, Xs) ->
- strict(arg_types(math, atanh, 1), Xs, fun (_) -> t_float() end);
-type(math, cos, 1, Xs) ->
- strict(arg_types(math, cos, 1), Xs, fun (_) -> t_float() end);
-type(math, cosh, 1, Xs) ->
- strict(arg_types(math, cosh, 1), Xs, fun (_) -> t_float() end);
-type(math, erf, 1, Xs) ->
- strict(arg_types(math, erf, 1), Xs, fun (_) -> t_float() end);
-type(math, erfc, 1, Xs) ->
- strict(arg_types(math, erfc, 1), Xs, fun (_) -> t_float() end);
-type(math, exp, 1, Xs) ->
- strict(arg_types(math, exp, 1), Xs, fun (_) -> t_float() end);
-type(math, log, 1, Xs) ->
- strict(arg_types(math, log, 1), Xs, fun (_) -> t_float() end);
-type(math, log10, 1, Xs) ->
- strict(arg_types(math, log10, 1), Xs, fun (_) -> t_float() end);
-type(math, pi, 0, _) -> t_float();
-type(math, pow, 2, Xs) ->
- strict(arg_types(math, pow, 2), Xs, fun (_) -> t_float() end);
-type(math, sin, 1, Xs) ->
- strict(arg_types(math, sin, 1), Xs, fun (_) -> t_float() end);
-type(math, sinh, 1, Xs) ->
- strict(arg_types(math, sinh, 1), Xs, fun (_) -> t_float() end);
-type(math, sqrt, 1, Xs) ->
- strict(arg_types(math, sqrt, 1), Xs, fun (_) -> t_float() end);
-type(math, tan, 1, Xs) ->
- strict(arg_types(math, tan, 1), Xs, fun (_) -> t_float() end);
-type(math, tanh, 1, Xs) ->
- strict(arg_types(math, tanh, 1), Xs, fun (_) -> t_float() end);
-%%-- net_kernel ---------------------------------------------------------------
-type(net_kernel, dflag_unicode_io, 1, Xs) ->
- strict(arg_types(net_kernel, dflag_unicode_io, 1), Xs,
- fun (_) -> t_boolean() end);
-%%-- ordsets ------------------------------------------------------------------
-type(ordsets, filter, 2, Xs) ->
- type(lists, filter, 2, Xs);
-type(ordsets, fold, 3, Xs) ->
- type(lists, foldl, 3, Xs);
-%%-- os -----------------------------------------------------------------------
-type(os, getenv, 0, _) -> t_list(t_string());
-type(os, getenv, 1, Xs) ->
- strict(arg_types(os, getenv, 1), Xs,
- fun (_) -> t_sup(t_string(), t_atom('false')) end);
-type(os, getpid, 0, _) -> t_string();
-type(os, putenv, 2, Xs) ->
- strict(arg_types(os, putenv, 2), Xs, fun (_) -> t_atom('true') end);
-type(os, timestamp, 0, _) ->
- t_timestamp();
-%%-- re -----------------------------------------------------------------------
-type(re, compile, 1, Xs) ->
- strict(arg_types(re, compile, 1), Xs,
- fun (_) ->
- t_sup(t_tuple([t_atom('ok'), t_re_MP()]),
- t_tuple([t_atom('error'), t_re_ErrorSpec()]))
- end);
-type(re, compile, 2, Xs) ->
- strict(arg_types(re, compile, 2), Xs,
- fun (_) ->
- t_sup(t_tuple([t_atom('ok'), t_re_MP()]),
- t_tuple([t_atom('error'), t_re_ErrorSpec()]))
- end);
-type(re, run, 2, Xs) ->
- strict(arg_types(re, run, 2), Xs,
- fun (_) ->
- t_sup([t_tuple([t_atom('match'), t_re_Captured()]),
- t_atom('nomatch'),
- t_tuple([t_atom('error'), t_re_ErrorSpec()])])
- end);
-type(re, run, 3, Xs) ->
- strict(arg_types(re, run, 3), Xs,
- fun (_) ->
- t_sup([t_tuple([t_atom('match'), t_re_Captured()]),
- t_atom('match'),
- t_atom('nomatch'),
- t_tuple([t_atom('error'), t_re_ErrorSpec()])])
- end);
+
%%-- string -------------------------------------------------------------------
type(string, chars, 2, Xs) -> % NOTE: added to avoid loss of information
strict(arg_types(string, chars, 2), Xs, fun (_) -> t_string() end);
@@ -2776,41 +1649,6 @@ type(string, chars, 3, Xs) -> % NOTE: added to avoid loss of information
end
end
end);
-type(string, concat, 2, Xs) -> % NOTE: added to avoid loss of information
- strict(arg_types(string, concat, 2), Xs, fun (_) -> t_string() end);
-type(string, equal, 2, Xs) -> % NOTE: added to avoid loss of information
- strict(arg_types(string, equal, 2), Xs, fun (_) -> t_boolean() end);
-type(string, to_float, 1, Xs) ->
- strict(arg_types(string, to_float, 1), Xs,
- fun (_) -> t_sup(t_tuple([t_float(), t_string()]),
- t_tuple([t_atom('error'),
- t_sup(t_atom('no_float'),
- t_atom('not_a_list'))]))
- end);
-type(string, to_integer, 1, Xs) ->
- strict(arg_types(string, to_integer, 1), Xs,
- fun (_) -> t_sup(t_tuple([t_integer(), t_string()]),
- t_tuple([t_atom('error'),
- t_sup(t_atom('no_integer'),
- t_atom('not_a_list'))]))
- end);
-%%-- unicode ------------------------------------------------------------------
-type(unicode, characters_to_binary, 2, Xs) ->
- strict(arg_types(unicode, characters_to_binary, 2), Xs,
- fun (_) ->
- t_sup([t_binary(),
- t_tuple([t_atom('error'), t_binary(), t_ML()]),
- t_tuple([t_atom('incomplete'), t_binary(), t_ML()])])
- end);
-type(unicode, characters_to_list, 2, Xs) ->
- strict(arg_types(unicode, characters_to_list, 2), Xs,
- fun (_) ->
- t_sup([t_string(),
- t_tuple([t_atom('error'), t_string(), t_ML()]),
- t_tuple([t_atom('incomplete'), t_string(), t_ML()])])
- end);
-type(unicode, bin_is_7bit, 1, Xs) ->
- strict(arg_types(unicode, bin_is_7bit, 1), Xs, fun (_) -> t_boolean() end);
%%-----------------------------------------------------------------------------
type(M, F, A, Xs) when is_atom(M), is_atom(F),
@@ -3265,107 +2103,6 @@ key_comparisons_fail(X0, KeyPos, TupleList) ->
-spec arg_types(atom(), atom(), arity()) -> [erl_types:erl_type()] | 'unknown'.
-%%------- binary --------------------------------------------------------------
-arg_types(binary, at, 2) ->
- [t_binary(), t_non_neg_integer()];
-arg_types(binary, bin_to_list, 1) ->
- [t_binary()];
-arg_types(binary, bin_to_list, 2) ->
- [t_binary(), t_binary_part()];
-arg_types(binary, bin_to_list, 3) ->
- [t_binary(), t_integer(), t_non_neg_integer()];
-arg_types(binary, compile_pattern, 1) ->
- [t_sup(t_binary(), t_list(t_binary()))];
-arg_types(binary, copy, 1) ->
- [t_binary()];
-arg_types(binary, copy, 2) ->
- [t_binary(), t_non_neg_integer()];
-arg_types(binary, decode_unsigned, 1) ->
- [t_binary()];
-arg_types(binary, decode_unsigned, 2) ->
- [t_binary(), t_endian()];
-arg_types(binary, encode_unsigned, 1) ->
- [t_non_neg_integer()];
-arg_types(binary, encode_unsigned, 2) ->
- [t_non_neg_integer(), t_endian()];
-arg_types(binary, first, 1) ->
- [t_binary()];
-arg_types(binary, last, 1) ->
- [t_binary()];
-arg_types(binary, list_to_bin, 1) ->
- arg_types(erlang, list_to_binary, 1);
-arg_types(binary, longest_common_prefix, 1) ->
- [t_list(t_binary())];
-arg_types(binary, longest_common_suffix, 1) ->
- [t_list(t_binary())];
-arg_types(binary, match, 2) ->
- [t_binary(), t_binary_pattern()];
-arg_types(binary, match, 3) ->
- [t_binary(), t_binary_pattern(), t_binary_options()];
-arg_types(binary, matches, 2) ->
- [t_binary(), t_binary_pattern()];
-arg_types(binary, matches, 3) ->
- [t_binary(), t_binary_pattern(), t_binary_options()];
-arg_types(binary, part, 2) ->
- arg_types(erlang, binary_part, 2);
-arg_types(binary, part, 3) ->
- arg_types(erlang, binary_part, 3);
-arg_types(binary, referenced_byte_size, 1) ->
- [t_binary()];
-%%------- code ----------------------------------------------------------------
-arg_types(code, get_chunk, 2) ->
- [t_binary(), t_string()];
-arg_types(code, is_module_native, 1) ->
- [t_atom()];
-arg_types(code, module_md5, 1) ->
- [t_binary()];
-arg_types(code, make_stub_module, 3) ->
- [t_atom(), t_binary(), t_tuple([t_list(), t_list()])];
-arg_types(code, rehash, 0) ->
- [];
-%%------- erl_ddll ------------------------------------------------------------
-arg_types(erl_ddll, demonitor, 1) ->
- arg_types(erlang, demonitor, 1);
-arg_types(erl_ddll, format_error_int, 1) ->
- [t_sup([t_atom('inconsistent'),
- t_atom('linked_in_driver'),
- t_atom('permanent'),
- t_atom('not_loaded'),
- t_atom('not_loaded_by_this_process'),
- t_atom('not_pending'),
- t_atom('already_loaded'),
- t_atom('unloading')])];
-arg_types(erl_ddll, info, 2) ->
- [t_sup([t_atom(), t_string()]),
- t_sup([t_atom('awaiting_load'),
- t_atom('awaiting_unload'),
- t_atom('driver_options'),
- t_atom('linked_in_driver'),
- t_atom('permanent'),
- t_atom('port_count'),
- t_atom('processes')])];
-arg_types(erl_ddll, loaded_drivers, 0) ->
- [];
-arg_types(erl_ddll, monitor, 2) ->
- [t_atom('driver'),
- t_tuple([t_atom(), t_sup([t_atom('loaded'), t_atom('unloaded')])])];
-arg_types(erl_ddll, try_load, 3) ->
- [t_sup([t_atom(), t_string(), t_nonempty_list(t_sup([t_atom(), t_string()]))]),
- t_sup([t_atom(), t_string()]),
- t_list(t_sup([t_tuple([t_atom('driver_options'),
- t_list(t_atom('kill_ports'))]),
- t_tuple([t_atom('monitor'),
- t_sup([t_atom('pending_driver'),
- t_atom('pending')])]),
- t_tuple([t_atom('reload'),
- t_sup([t_atom('pending_driver'),
- t_atom('pending')])])]))];
-arg_types(erl_ddll, try_unload, 2) ->
- [t_sup([t_atom(), t_string(), t_nonempty_list(t_sup([t_atom(), t_string()]))]),
- t_list(t_sup([t_atom('kill_ports'),
- t_tuple([t_atom('monitor'),
- t_sup([t_atom('pending_driver'),
- t_atom('pending')])])]))];
%%------- erlang --------------------------------------------------------------
arg_types(erlang, '!', 2) ->
Pid = t_sup([t_pid(), t_port(), t_atom(),
@@ -3427,18 +2164,11 @@ arg_types(erlang, 'bsl', 2) ->
[t_integer(), t_integer()];
arg_types(erlang, 'bnot', 1) ->
[t_integer()];
+%% Guard bif, needs to be here.
arg_types(erlang, abs, 1) ->
[t_number()];
-arg_types(erlang, adler32, 1) ->
- [t_iodata()];
-arg_types(erlang, adler32, 2) ->
- [t_adler32(), t_iodata()];
-arg_types(erlang, adler32_combine, 3) ->
- [t_adler32(), t_adler32(), t_non_neg_integer()];
arg_types(erlang, append, 2) ->
arg_types(erlang, '++', 2);
-arg_types(erlang, append_element, 2) ->
- [t_tuple(), t_any()];
arg_types(erlang, apply, 2) ->
[t_sup(t_tuple([t_module(),
t_atom()]),
@@ -3446,179 +2176,58 @@ arg_types(erlang, apply, 2) ->
t_list()];
arg_types(erlang, apply, 3) ->
[t_sup(t_atom(), t_tuple()), t_atom(), t_list()];
-arg_types(erlang, atom_to_binary, 2) ->
- [t_atom(), t_encoding_a2b()];
-arg_types(erlang, atom_to_list, 1) ->
- [t_atom()];
+%% Guard bif, needs to be here.
arg_types(erlang, binary_part, 2) ->
[t_binary(), t_tuple([t_non_neg_integer(), t_integer()])];
+%% Guard bif, needs to be here.
arg_types(erlang, binary_part, 3) ->
[t_binary(), t_non_neg_integer(), t_integer()];
-arg_types(erlang, binary_to_atom, 2) ->
- [t_binary(), t_encoding_a2b()];
-arg_types(erlang, binary_to_existing_atom, 2) ->
- arg_types(erlang, binary_to_atom, 2);
-arg_types(erlang, binary_to_list, 1) ->
- [t_binary()];
-arg_types(erlang, binary_to_list, 3) ->
- [t_binary(), t_pos_integer(), t_pos_integer()]; % I want fixnum, but cannot
-arg_types(erlang, binary_to_term, 1) ->
- [t_binary()];
-arg_types(erlang, binary_to_term, 2) ->
- [t_binary(), t_list(t_atom('safe'))];
-arg_types(erlang, bitsize, 1) -> % XXX: TAKE OUT
- arg_types(erlang, bit_size, 1);
+%% Guard bif, needs to be here.
arg_types(erlang, bit_size, 1) ->
[t_bitstr()];
-arg_types(erlang, bitstr_to_list, 1) -> % XXX: TAKE OUT
- arg_types(erlang, bitstring_to_list, 1);
-arg_types(erlang, bitstring_to_list, 1) ->
- [t_bitstr()];
-arg_types(erlang, bump_reductions, 1) ->
- [t_pos_fixnum()];
+%% Guard bif, needs to be here.
arg_types(erlang, byte_size, 1) ->
[t_binary()];
-arg_types(erlang, call_on_load_function, 1) ->
- [t_atom()];
-arg_types(erlang, cancel_timer, 1) ->
- [t_reference()];
-arg_types(erlang, check_old_code, 1) ->
- [t_atom()];
-arg_types(erlang, check_process_code, 2) ->
- [t_pid(), t_atom()];
-arg_types(erlang, crc32, 1) ->
- [t_iodata()];
-arg_types(erlang, crc32, 2) ->
- [t_crc32(), t_iodata()];
-arg_types(erlang, crc32_combine, 3) ->
- [t_crc32(), t_crc32(), t_non_neg_integer()];
-arg_types(erlang, date, 0) ->
- [];
-arg_types(erlang, decode_packet, 3) ->
- [t_decode_packet_type(), t_binary(), t_list(t_decode_packet_option())];
-arg_types(erlang, delete_module, 1) ->
- [t_atom()];
-arg_types(erlang, demonitor, 1) ->
- [t_reference()];
-arg_types(erlang, demonitor, 2) ->
- [t_reference(), t_list(t_atoms(['flush', 'info']))];
arg_types(erlang, disconnect_node, 1) ->
[t_node()];
-arg_types(erlang, display, 1) ->
- [t_any()];
-arg_types(erlang, display_nl, 0) ->
- [];
-arg_types(erlang, display_string, 1) ->
- [t_string()];
-arg_types(erlang, dist_exit, 3) ->
- [t_pid(), t_dist_exit(), t_sup(t_pid(), t_port())];
-arg_types(erlang, dt_append_vm_tag_data, 1) ->
- [t_iodata()];
-arg_types(erlang, dt_get_tag, 0) ->
- [];
-arg_types(erlang, dt_get_tag_data, 0) ->
- [];
-arg_types(erlang, dt_prepend_vm_tag_data, 1) ->
- [t_iodata()];
-arg_types(erlang, dt_put_tag, 1) ->
- [t_sup(t_binary(), t_atom('undefined'))];
-arg_types(erlang, dt_restore_tag, 1) ->
- [t_sup(t_tuple([t_non_neg_integer(), t_sup(t_binary(), t_nil())]), t_atom('true'))];
-arg_types(erlang, dt_spread_tag, 1) ->
- [t_boolean()];
-arg_types(erlang, element, 2) ->
- [t_pos_fixnum(), t_tuple()];
-arg_types(erlang, erase, 0) ->
+arg_types(erlang, halt, 0) ->
[];
-arg_types(erlang, erase, 1) ->
- [t_any()];
+arg_types(erlang, halt, 1) ->
+ [t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()])];
+arg_types(erlang, halt, 2) ->
+ [t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()]),
+ t_list(t_tuple([t_atom('flush'), t_boolean()]))];
arg_types(erlang, error, 1) ->
[t_any()];
arg_types(erlang, error, 2) ->
[t_any(), t_list()];
arg_types(erlang, exit, 1) ->
[t_any()];
-arg_types(erlang, exit, 2) ->
- [t_sup(t_pid(), t_port()), t_any()];
-arg_types(erlang, external_size, 1) ->
- [t_any()]; % takes any term as input
-arg_types(erlang, external_size, 2) ->
- [t_any(), t_list()]; % takes any term as input and a list of options
-arg_types(erlang, finish_after_on_load, 2) ->
- [t_atom(), t_boolean()];
+%% Guard bif, needs to be here.
+arg_types(erlang, element, 2) ->
+ [t_pos_fixnum(), t_tuple()];
+%% Guard bif, needs to be here.
arg_types(erlang, float, 1) ->
[t_number()];
-arg_types(erlang, float_to_list, 1) ->
- [t_float()];
-arg_types(erlang, function_exported, 3) ->
- [t_atom(), t_atom(), t_arity()];
arg_types(erlang, fun_info, 1) ->
[t_fun()];
-arg_types(erlang, fun_info, 2) ->
- [t_fun(), t_atom()];
-arg_types(erlang, fun_to_list, 1) ->
- [t_fun()];
-arg_types(erlang, garbage_collect, 0) ->
- [];
-arg_types(erlang, garbage_collect, 1) ->
- [t_pid()];
-arg_types(erlang, garbage_collect_message_area, 0) ->
- [];
-arg_types(erlang, get, 0) ->
- [];
-arg_types(erlang, get, 1) ->
- [t_any()];
arg_types(erlang, get_cookie, 0) ->
[];
-arg_types(erlang, get_keys, 1) ->
- [t_any()];
-arg_types(erlang, get_stacktrace, 0) ->
- [];
-arg_types(erlang, get_module_info, 1) ->
- [t_atom()];
-arg_types(erlang, get_module_info, 2) ->
- [t_atom(), t_module_info_2()];
-arg_types(erlang, group_leader, 0) ->
- [];
-arg_types(erlang, group_leader, 2) ->
- [t_pid(), t_pid()];
-arg_types(erlang, halt, 0) ->
- [];
-arg_types(erlang, halt, 1) ->
- [t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()])];
-arg_types(erlang, halt, 2) ->
- [t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()]),
- t_list(t_tuple([t_atom('flush'), t_boolean()]))];
-arg_types(erlang, hash, 2) ->
- [t_any(), t_integer()];
+%% Guard bif, needs to be here.
arg_types(erlang, hd, 1) ->
[t_cons()];
-arg_types(erlang, hibernate, 3) ->
- [t_atom(), t_atom(), t_list()];
arg_types(erlang, info, 1) ->
arg_types(erlang, system_info, 1); % alias
-arg_types(erlang, iolist_to_binary, 1) ->
- [t_sup(t_iolist(), t_binary())];
-arg_types(erlang, iolist_size, 1) ->
- [t_sup(t_iolist(), t_binary())];
-arg_types(erlang, integer_to_list, 1) ->
- [t_integer()];
arg_types(erlang, integer_to_list, 2) ->
[t_integer(), t_from_range(2, 36)];
-arg_types(erlang, is_alive, 0) ->
- [];
arg_types(erlang, is_atom, 1) ->
[t_any()];
arg_types(erlang, is_binary, 1) ->
[t_any()];
-arg_types(erlang, is_bitstr, 1) -> % XXX: TAKE OUT
- arg_types(erlang, is_bitstring, 1);
arg_types(erlang, is_bitstring, 1) ->
[t_any()];
arg_types(erlang, is_boolean, 1) ->
[t_any()];
-arg_types(erlang, is_builtin, 3) ->
- [t_atom(), t_atom(), t_arity()];
arg_types(erlang, is_float, 1) ->
[t_any()];
arg_types(erlang, is_function, 1) ->
@@ -3635,8 +2244,6 @@ arg_types(erlang, is_pid, 1) ->
[t_any()];
arg_types(erlang, is_port, 1) ->
[t_any()];
-arg_types(erlang, is_process_alive, 1) ->
- [t_pid()];
arg_types(erlang, is_record, 2) ->
[t_any(), t_atom()];
arg_types(erlang, is_record, 3) ->
@@ -3645,548 +2252,91 @@ arg_types(erlang, is_reference, 1) ->
[t_any()];
arg_types(erlang, is_tuple, 1) ->
[t_any()];
+%% Guard bif, needs to be here.
arg_types(erlang, length, 1) ->
[t_list()];
-arg_types(erlang, link, 1) ->
- [t_sup(t_pid(), t_port())];
-arg_types(erlang, list_to_atom, 1) ->
- [t_string()];
-arg_types(erlang, list_to_binary, 1) ->
- [t_iolist()];
-arg_types(erlang, list_to_bitstr, 1) -> % XXX: TAKE OUT
- arg_types(erlang, list_to_bitstring, 1);
-arg_types(erlang, list_to_bitstring, 1) ->
- [t_bitstrlist()];
-arg_types(erlang, list_to_existing_atom, 1) ->
- [t_string()];
-arg_types(erlang, list_to_float, 1) ->
- [t_list(t_byte())];
-arg_types(erlang, list_to_integer, 1) ->
- [t_list(t_byte())];
arg_types(erlang, list_to_integer, 2) ->
[t_list(t_byte()), t_from_range(2, 36)];
-arg_types(erlang, list_to_pid, 1) ->
- [t_string()];
-arg_types(erlang, list_to_tuple, 1) ->
- [t_list()];
-arg_types(erlang, load_module, 2) ->
- [t_atom(), t_binary()];
-arg_types(erlang, load_nif, 2) ->
- [t_string(), t_any()];
-arg_types(erlang, loaded, 0) ->
- [];
-arg_types(erlang, localtime, 0) ->
- [];
-arg_types(erlang, localtime_to_universaltime, 1) ->
- [t_tuple([t_date(), t_time()])];
-arg_types(erlang, localtime_to_universaltime, 2) ->
- arg_types(erlang, localtime_to_universaltime, 1) ++
- [t_sup(t_boolean(), t_atom('undefined'))];
-arg_types(erlang, make_fun, 3) ->
- [t_atom(), t_atom(), t_arity()];
-arg_types(erlang, make_ref, 0) ->
- [];
arg_types(erlang, make_tuple, 2) ->
[t_non_neg_fixnum(), t_any()]; % the value 0 is OK as first argument
arg_types(erlang, make_tuple, 3) ->
[t_non_neg_fixnum(), t_any(), t_list(t_tuple([t_pos_integer(), t_any()]))];
-arg_types(erlang, match_spec_test, 3) ->
- [t_sup(t_list(), t_tuple()),
- t_any(),
- t_sup(t_atom('table'), t_atom('trace'))];
-arg_types(erlang, md5, 1) ->
- [t_sup(t_iolist(), t_binary())];
-arg_types(erlang, md5_final, 1) ->
- [t_binary()];
-arg_types(erlang, md5_init, 0) ->
- [];
-arg_types(erlang, md5_update, 2) ->
- [t_binary(), t_sup(t_iolist(), t_binary())];
arg_types(erlang, memory, 0) ->
[];
-arg_types(erlang, memory, 1) ->
- Arg = t_atoms(['total', 'processes', 'processes_used', 'system',
- 'atom', 'atom_used', 'binary', 'code', 'ets',
- 'maximum']),
- [t_sup(Arg, t_list(Arg))];
-arg_types(erlang, module_loaded, 1) ->
- [t_atom()];
-arg_types(erlang, monitor, 2) ->
- [t_atom(), t_sup([t_pid(), t_atom(), t_tuple([t_atom(), t_node()])])];
-arg_types(erlang, monitor_node, 2) ->
- [t_node(), t_boolean()];
-arg_types(erlang, monitor_node, 3) ->
- [t_node(), t_boolean(), t_list(t_atom('allow_passive_connect'))];
arg_types(erlang, nif_error, 1) ->
[t_any()];
arg_types(erlang, nif_error, 2) ->
[t_any(), t_list()];
+%% Guard bif, needs to be here.
arg_types(erlang, node, 0) ->
[];
+%% Guard bif, needs to be here.
arg_types(erlang, node, 1) ->
[t_identifier()];
arg_types(erlang, nodes, 0) ->
[];
-arg_types(erlang, nodes, 1) ->
- NodesArg = t_atoms(['visible', 'hidden', 'connected', 'this', 'known']),
- [t_sup(NodesArg, t_list(NodesArg))];
-arg_types(erlang, now, 0) ->
- [];
-arg_types(erlang, open_port, 2) ->
- ArgT = t_sup(t_unicode_string(), t_binary()),
- [t_sup(t_atom(), t_sup([t_tuple([t_atom('spawn'), t_string()]),
- t_tuple([t_atom('spawn_driver'), t_string()]),
- t_tuple([t_atom('spawn_executable'), ArgT]),
- t_tuple([t_atom('fd'), t_integer(), t_integer()])])),
- t_list(t_sup(t_sup([t_atom('stream'),
- t_atom('exit_status'),
- t_atom('use_stdio'),
- t_atom('nouse_stdio'),
- t_atom('stderr_to_stdout'),
- t_atom('in'),
- t_atom('out'),
- t_atom('binary'),
- t_atom('eof'),
- t_atom('hide')]),
- t_sup([t_tuple([t_atom('packet'), t_integer()]),
- t_tuple([t_atom('line'), t_integer()]),
- t_tuple([t_atom('cd'), t_string()]),
- t_tuple([t_atom('env'), t_list(t_tuple(2))]), % XXX: More
- t_tuple([t_atom('args'), t_list(ArgT)]),
- t_tuple([t_atom('arg0'), ArgT])])))];
-arg_types(erlang, phash, 2) ->
- [t_any(), t_pos_integer()];
-arg_types(erlang, phash2, 1) ->
- [t_any()];
-arg_types(erlang, phash2, 2) ->
- [t_any(), t_pos_integer()];
-arg_types(erlang, pid_to_list, 1) ->
- [t_pid()];
arg_types(erlang, port_call, 2) ->
[t_sup(t_port(), t_atom()), t_any()];
arg_types(erlang, port_call, 3) ->
[t_sup(t_port(), t_atom()), t_integer(), t_any()];
-arg_types(erlang, port_close, 1) ->
- [t_sup(t_port(), t_atom())];
-arg_types(erlang, port_command, 2) ->
- [t_sup(t_port(), t_atom()), t_sup(t_iolist(), t_binary())];
-arg_types(erlang, port_command, 3) ->
- [t_sup(t_port(), t_atom()),
- t_sup(t_iolist(), t_binary()),
- t_list(t_atoms(['force', 'nosuspend']))];
-arg_types(erlang, port_connect, 2) ->
- [t_sup(t_port(), t_atom()), t_pid()];
-arg_types(erlang, port_control, 3) ->
- [t_sup(t_port(), t_atom()), t_integer(), t_sup(t_iolist(), t_binary())];
-arg_types(erlang, port_get_data, 1) ->
- [t_sup(t_port(), t_atom())];
arg_types(erlang, port_info, 1) ->
[t_sup(t_port(), t_atom())];
arg_types(erlang, port_info, 2) ->
[t_sup(t_port(), t_atom()),
t_atoms(['registered_name', 'id', 'connected',
'links', 'name', 'input', 'output', 'os_pid'])];
-arg_types(erlang, port_to_list, 1) ->
- [t_port()];
-arg_types(erlang, ports, 0) ->
- [];
-arg_types(erlang, port_set_data, 2) ->
- [t_sup(t_port(), t_atom()), t_any()];
-arg_types(erlang, pre_loaded, 0) ->
- [];
-arg_types(erlang, process_display, 2) ->
- [t_pid(), t_atom('backtrace')];
-arg_types(erlang, process_flag, 2) ->
- [t_sup([t_atom('trap_exit'),
- t_atom('error_handler'),
- t_atom('min_heap_size'),
- t_atom('min_bin_vheap_size'),
- t_atom('priority'),
- t_atom('save_calls'),
- t_atom('sensitive'),
- t_atom('scheduler'), % undocumented
- t_atom('monitor_nodes'), % undocumented
- t_tuple([t_atom('monitor_nodes'), t_list()])]), % undocumented
- t_sup([t_boolean(), t_atom(), t_non_neg_integer()])];
-arg_types(erlang, process_flag, 3) ->
- [t_pid(), t_atom('save_calls'), t_non_neg_integer()];
-arg_types(erlang, process_info, 1) ->
- [t_pid()];
-arg_types(erlang, process_info, 2) ->
- [t_pid(), t_pinfo()];
-arg_types(erlang, processes, 0) ->
- [];
-arg_types(erlang, purge_module, 1) ->
- [t_atom()];
-arg_types(erlang, put, 2) ->
- [t_any(), t_any()];
-arg_types(erlang, raise, 3) ->
- OldStyleType = t_list(t_tuple([t_atom(), t_atom(),
- t_sup([t_arity(), t_list()])])),
- NewStyleType = type(erlang, get_stacktrace, 0, []),
- [t_raise_errorclass(), t_any(), t_sup(OldStyleType, NewStyleType)];
-arg_types(erlang, read_timer, 1) ->
- [t_reference()];
-arg_types(erlang, ref_to_list, 1) ->
- [t_reference()];
-arg_types(erlang, register, 2) ->
- [t_atom(), t_sup(t_port(), t_pid())];
-arg_types(erlang, registered, 0) ->
- [];
-arg_types(erlang, resume_process, 1) ->
- [t_pid()]; % intended for debugging only
+%% Guard bif, needs to be here.
arg_types(erlang, round, 1) ->
[t_number()];
-arg_types(erlang, posixtime_to_universaltime, 1) ->
- [t_integer()];
+%% Guard bif, needs to be here.
arg_types(erlang, self, 0) ->
[];
-arg_types(erlang, send, 2) ->
- arg_types(erlang, '!', 2); % alias
-arg_types(erlang, send, 3) ->
- arg_types(erlang, send, 2) ++ [t_list(t_sendoptions())];
-arg_types(erlang, send_after, 3) ->
- [t_non_neg_integer(), t_sup(t_pid(), t_atom()), t_any()];
-arg_types(erlang, seq_trace, 2) ->
- [t_atom(), t_sup([t_boolean(), t_tuple([t_fixnum(), t_fixnum()]), t_fixnum(), t_nil()])];
-arg_types(erlang, seq_trace_info, 1) ->
- [t_seq_trace_info()];
-arg_types(erlang, seq_trace_print, 1) ->
- [t_any()];
-arg_types(erlang, seq_trace_print, 2) ->
- [t_sup(t_atom(), t_fixnum()), t_any()];
arg_types(erlang, set_cookie, 2) ->
[t_node(), t_atom()];
arg_types(erlang, setelement, 3) ->
[t_pos_integer(), t_tuple(), t_any()];
-arg_types(erlang, setnode, 2) ->
- [t_atom(), t_integer()];
-arg_types(erlang, setnode, 3) ->
- [t_atom(), t_port(), t_tuple(4)];
+%% Guard bif, needs to be here.
arg_types(erlang, size, 1) ->
[t_sup(t_tuple(), t_binary())];
arg_types(erlang, spawn, 1) -> %% TODO: Tuple?
[t_fun()];
arg_types(erlang, spawn, 2) -> %% TODO: Tuple?
[t_node(), t_fun()];
-arg_types(erlang, spawn, 3) -> %% TODO: Tuple?
- [t_atom(), t_atom(), t_list()];
arg_types(erlang, spawn, 4) -> %% TODO: Tuple?
[t_node(), t_atom(), t_atom(), t_list()];
arg_types(erlang, spawn_link, 1) ->
arg_types(erlang, spawn, 1); % same
arg_types(erlang, spawn_link, 2) ->
arg_types(erlang, spawn, 2); % same
-arg_types(erlang, spawn_link, 3) ->
- arg_types(erlang, spawn, 3); % same
arg_types(erlang, spawn_link, 4) ->
arg_types(erlang, spawn, 4); % same
-arg_types(erlang, spawn_opt, 1) ->
- [t_tuple([t_atom(), t_atom(), t_list(), t_list(t_spawn_options())])];
-arg_types(erlang, spawn_opt, 2) ->
- [t_fun(), t_list(t_spawn_options())];
-arg_types(erlang, spawn_opt, 3) ->
- [t_atom(), t_fun(), t_list(t_spawn_options())];
-arg_types(erlang, spawn_opt, 4) ->
- [t_node(), t_atom(), t_list(), t_list(t_spawn_options())];
-arg_types(erlang, split_binary, 2) ->
- [t_binary(), t_non_neg_integer()];
-arg_types(erlang, start_timer, 3) ->
- [t_non_neg_integer(), t_sup(t_pid(), t_atom()), t_any()];
-arg_types(erlang, statistics, 1) ->
- [t_sup([t_atom('context_switches'),
- t_atom('exact_reductions'),
- t_atom('garbage_collection'),
- t_atom('io'),
- t_atom('reductions'),
- t_atom('run_queue'),
- t_atom('runtime'),
- t_atom('wall_clock')])];
arg_types(erlang, subtract, 2) ->
arg_types(erlang, '--', 2);
arg_types(erlang, suspend_process, 1) ->
[t_pid()];
-arg_types(erlang, suspend_process, 2) ->
- [t_pid(), t_list(t_sup([t_atom('unless_suspending'),
- t_atom('asynchronous')]))];
-arg_types(erlang, system_flag, 2) ->
- [t_sup([t_atom('backtrace_depth'),
- t_atom('cpu_topology'),
- t_atom('debug_flags'), % undocumented
- t_atom('display_items'), % undocumented
- t_atom('fullsweep_after'),
- t_atom('min_heap_size'),
- t_atom('min_bin_vheap_size'),
- t_atom('multi_scheduling'),
- t_atom('schedulers_online'),
- t_atom('scheduler_bind_type'),
- %% Undocumented; used to implement (the documented) seq_trace module.
- t_atom('sequential_tracer'),
- t_atom('trace_control_word'),
- %% 'internal_cpu_topology' is an undocumented internal feature.
- t_atom('internal_cpu_topology'),
- t_integer()]),
- t_sup([t_integer(),
- %% 'cpu_topology'
- t_system_cpu_topology(),
- %% 'scheduler_bind_type'
- t_scheduler_bind_type_args(),
- %% Undocumented: the following is for 'debug_flags' that
- %% takes any erlang term as flags and currently ignores it.
- %% t_any(), % commented out since it destroys the type signature
- %%
- %% Again undocumented; the following are for 'sequential_tracer'
- t_sequential_tracer(),
- %% The following two are for 'multi_scheduling'
- t_atom('block'),
- t_atom('unblock'),
- %% The following is for 'internal_cpu_topology'
- t_internal_cpu_topology()])];
arg_types(erlang, system_info, 1) ->
[t_sup([t_atom(), % documented
t_tuple([t_atom(), t_any()]), % documented
t_tuple([t_atom(), t_atom(), t_any()]),
t_tuple([t_atom(allocator_sizes), t_reference(), t_any()])])];
-arg_types(erlang, system_monitor, 0) ->
- [];
-arg_types(erlang, system_monitor, 1) ->
- [t_system_monitor_settings()];
-arg_types(erlang, system_monitor, 2) ->
- [t_pid(), t_system_monitor_options()];
-arg_types(erlang, system_profile, 0) ->
- [];
-arg_types(erlang, system_profile, 2) ->
- [t_sup([t_pid(), t_port(), t_atom('undefined')]),
- t_system_profile_options()];
-arg_types(erlang, term_to_binary, 1) ->
- [t_any()];
-arg_types(erlang, term_to_binary, 2) ->
- [t_any(), t_list(t_sup([t_atom('compressed'),
- t_tuple([t_atom('compressed'), t_from_range(0, 9)]),
- t_tuple([t_atom('minor_version'), t_integers([0, 1])])]))];
arg_types(erlang, throw, 1) ->
[t_any()];
-arg_types(erlang, time, 0) ->
- [];
+%% Guard bif, needs to be here.
arg_types(erlang, tl, 1) ->
[t_cons()];
-arg_types(erlang, trace, 3) ->
- [t_sup(t_pid(), t_sup([t_atom('existing'), t_atom('new'), t_atom('all')])),
- t_boolean(),
- t_list(t_sup(t_atom(), t_tuple(2)))];
-arg_types(erlang, trace_delivered, 1) ->
- [t_sup(t_pid(), t_atom('all'))];
-arg_types(erlang, trace_info, 2) ->
- [t_sup([%% the following two get info about a PID
- t_pid(), t_atom('new'),
- %% while the following two get info about a func
- t_mfa(), t_atom('on_load')]),
- t_sup([%% the following are items about a PID
- t_atom('flags'), t_atom('tracer'),
- %% while the following are items about a func
- t_atom('traced'), t_atom('match_spec'), t_atom('meta'),
- t_atom('meta_match_spec'), t_atom('call_count'),
- t_atom('call_time'), t_atom('all')])];
-arg_types(erlang, trace_pattern, 2) ->
- [t_sup(t_tuple([t_atom(), t_atom(), t_sup(t_arity(), t_atom('_'))]),
- t_atom('on_load')),
- t_sup([t_boolean(), t_list(), t_atom('restart'), t_atom('pause')])];
-arg_types(erlang, trace_pattern, 3) ->
- arg_types(erlang, trace_pattern, 2) ++
- [t_list(t_sup([t_atom('global'), t_atom('local'),
- t_atom('meta'), t_tuple([t_atom('meta'), t_pid()]),
- t_atom('call_count'), t_atom('call_time')]))];
+%% Guard bif, needs to be here.
arg_types(erlang, trunc, 1) ->
[t_number()];
+%% Guard bif, needs to be here.
arg_types(erlang, tuple_size, 1) ->
[t_tuple()];
arg_types(erlang, tuple_to_list, 1) ->
[t_tuple()];
-arg_types(erlang, universaltime, 0) ->
- [];
-arg_types(erlang, universaltime_to_localtime, 1) ->
- [t_tuple([t_date(), t_time()])];
-arg_types(erlang, universaltime_to_posixtime, 1) ->
- [t_tuple([t_date(), t_time()])];
-arg_types(erlang, unlink, 1) ->
- [t_sup(t_pid(), t_port())];
-arg_types(erlang, unregister, 1) ->
- [t_atom()];
-arg_types(erlang, whereis, 1) ->
- [t_atom()];
arg_types(erlang, yield, 0) ->
[];
-%%------- erl_prim_loader -----------------------------------------------------
-arg_types(erl_prim_loader, get_file, 1) ->
- [t_sup(t_atom(), t_string())];
-arg_types(erl_prim_loader, get_path, 0) ->
- [];
-arg_types(erl_prim_loader, set_path, 1) ->
- [t_list(t_string())];
-%%------- error_logger --------------------------------------------------------
-arg_types(error_logger, warning_map, 0) ->
- [];
-%%------- erts_debug ----------------------------------------------------------
-arg_types(erts_debug, breakpoint, 2) ->
- [t_tuple([t_atom(), t_atom(), t_sup(t_integer(), t_atom('_'))]), t_boolean()];
-arg_types(erts_debug, disassemble, 1) ->
- [t_sup(t_mfa(), t_integer())];
-arg_types(erts_debug, display, 1) ->
- [t_any()];
-arg_types(erts_debug, dist_ext_to_term, 2) ->
- [t_tuple(), t_binary()];
-arg_types(erts_debug, dump_monitors, 1) ->
- [t_sup([t_pid(),t_atom()])];
-arg_types(erts_debug, dump_links, 1) ->
- [t_sup([t_pid(),t_atom(),t_port()])];
-arg_types(erts_debug, flat_size, 1) ->
- [t_any()];
-arg_types(erts_debug, get_internal_state, 1) ->
- [t_any()];
-arg_types(erts_debug, instructions, 0) ->
- [];
-arg_types(erts_debug, lock_counters, 1) ->
- [t_sup([t_atom(enabled),
- t_atom(info),
- t_atom(clear),
- t_tuple([t_atom(copy_save), t_boolean()]),
- t_tuple([t_atom(process_locks), t_boolean()])])];
-arg_types(erts_debug, same, 2) ->
- [t_any(), t_any()];
-arg_types(erts_debug, set_internal_state, 2) ->
- [t_any(), t_any()];
%%------- ets -----------------------------------------------------------------
-arg_types(ets, all, 0) ->
- [];
-arg_types(ets, delete, 1) ->
- [t_tab()];
-arg_types(ets, delete, 2) ->
- [t_tab(), t_any()];
-arg_types(ets, delete_all_objects, 1) ->
- [t_tab()];
-arg_types(ets, delete_object, 2) ->
- [t_tab(), t_tuple()];
-arg_types(ets, first, 1) ->
- [t_tab()];
-arg_types(ets, give_away, 3) ->
- [t_tab(), t_pid(), t_any()];
-arg_types(ets, info, 1) ->
- [t_tab()];
-arg_types(ets, info, 2) ->
- [t_tab(), t_ets_info_items()];
-arg_types(ets, insert, 2) ->
- [t_tab(), t_sup(t_tuple(), t_list(t_tuple()))];
-arg_types(ets, insert_new, 2) ->
- [t_tab(), t_sup(t_tuple(), t_list(t_tuple()))];
-arg_types(ets, is_compiled_ms, 1) ->
- [t_any()];
-arg_types(ets, last, 1) ->
- arg_types(ets, first, 1);
-arg_types(ets, lookup, 2) ->
- [t_tab(), t_any()];
-arg_types(ets, lookup_element, 3) ->
- [t_tab(), t_any(), t_pos_fixnum()];
-arg_types(ets, match, 1) ->
- [t_any()];
-arg_types(ets, match, 2) ->
- [t_tab(), t_match_pattern()];
-arg_types(ets, match, 3) ->
- [t_tab(), t_match_pattern(), t_pos_fixnum()];
-arg_types(ets, match_object, 1) ->
- arg_types(ets, match, 1);
-arg_types(ets, match_object, 2) ->
- arg_types(ets, match, 2);
-arg_types(ets, match_object, 3) ->
- arg_types(ets, match, 3);
-arg_types(ets, match_spec_compile, 1) ->
- [t_matchspecs()];
-arg_types(ets, match_spec_run_r, 3) ->
- [t_list(t_tuple()),t_matchspecs(), t_list()];
-arg_types(ets, member, 2) ->
- [t_tab(), t_any()];
-arg_types(ets, new, 2) ->
- [t_atom(), t_ets_new_options()];
-arg_types(ets, next, 2) ->
- [t_tab(), t_any()];
-arg_types(ets, prev, 2) ->
- [t_tab(), t_any()];
arg_types(ets, rename, 2) ->
[t_atom(), t_atom()];
-arg_types(ets, safe_fixtable, 2) ->
- [t_tab(), t_boolean()];
-arg_types(ets, select, 1) ->
- [t_any()];
-arg_types(ets, select, 2) ->
- [t_tab(), t_matchspecs()];
-arg_types(ets, select, 3) ->
- [t_tab(), t_matchspecs(), t_pos_fixnum()];
-arg_types(ets, select_count, 2) ->
- [t_tab(), t_matchspecs()];
-arg_types(ets, select_delete, 2) ->
- [t_tab(), t_matchspecs()];
-arg_types(ets, select_reverse, 1) ->
- arg_types(ets, select, 1);
-arg_types(ets, select_reverse, 2) ->
- arg_types(ets, select, 2);
-arg_types(ets, select_reverse, 3) ->
- arg_types(ets, select, 3);
-arg_types(ets, slot, 2) ->
- [t_tab(), t_non_neg_fixnum()]; % 2nd arg can be 0
-arg_types(ets, setopts, 2) ->
- Opt = t_sup([t_tuple([t_atom('heir'), t_pid(), t_any()]),
- t_tuple([t_atom('heir'), t_atom('none')]),
- t_tuple([t_atom('protection'),
- t_sup([t_atom('protected'),
- t_atom('private'),
- t_atom('public')])])]),
- [t_tab(), t_sup(Opt, t_list(Opt))];
-arg_types(ets, update_counter, 3) ->
- Int = t_integer(),
- UpdateOp = t_sup(t_tuple([Int, Int]), t_tuple([Int, Int, Int, Int])),
- [t_tab(), t_any(), t_sup([UpdateOp, t_list(UpdateOp), Int])];
-arg_types(ets, update_element, 3) ->
- PosValue = t_tuple([t_integer(), t_any()]),
- [t_tab(), t_any(), t_sup(PosValue, t_list(PosValue))];
-%%------- file ----------------------------------------------------------------
-arg_types(file, native_name_encoding, 0) ->
- [];
-%%-- prim_file ----------------------------------------------------------------
-arg_types(prim_file, internal_name2native, 1) ->
- [t_prim_file_name()];
-arg_types(prim_file, internal_native2name, 1) ->
- [t_binary()];
-arg_types(prim_file, internal_normalize_utf8, 1) ->
- [t_binary()];
-%%------- gen_tcp -------------------------------------------------------------
-arg_types(gen_tcp, accept, 1) ->
- [t_socket()];
-arg_types(gen_tcp, accept, 2) ->
- [t_socket(), t_timeout()];
-arg_types(gen_tcp, connect, 3) ->
- [t_gen_tcp_address(), t_gen_tcp_port(), t_list(t_gen_tcp_connect_option())];
-arg_types(gen_tcp, connect, 4) ->
- arg_types(gen_tcp, connect, 3) ++ [t_timeout()];
-arg_types(gen_tcp, listen, 2) ->
- [t_gen_tcp_port(), t_list(t_gen_tcp_listen_option())];
-arg_types(gen_tcp, recv, 2) ->
- [t_socket(), t_non_neg_integer()];
-arg_types(gen_tcp, recv, 3) ->
- arg_types(gen_tcp, recv, 2) ++ [t_timeout()];
-arg_types(gen_tcp, send, 2) ->
- [t_socket(), t_packet()];
-arg_types(gen_tcp, shutdown, 2) ->
- [t_socket(), t_sup([t_atom('read'), t_atom('write'), t_atom('read_write')])];
-%%------- gen_udp -------------------------------------------------------------
-arg_types(gen_udp, open, 1) ->
- [t_gen_tcp_port()];
-arg_types(gen_udp, open, 2) ->
- [t_gen_tcp_port(), t_list(t_gen_udp_connect_option())];
-arg_types(gen_udp, recv, 2) ->
- arg_types(gen_tcp, recv, 2);
-arg_types(gen_udp, recv, 3) ->
- arg_types(gen_tcp, recv, 3);
-arg_types(gen_udp, send, 4) ->
- [t_socket(), t_gen_tcp_address(), t_gen_tcp_port(), t_packet()];
%%------- hipe_bifs -----------------------------------------------------------
arg_types(hipe_bifs, add_ref, 2) ->
[t_mfa(), t_tuple([t_mfa(),
@@ -4233,7 +2383,7 @@ arg_types(hipe_bifs, check_crc, 1) ->
arg_types(hipe_bifs, enter_code, 2) ->
[t_binary(), t_sup(t_nil(), t_tuple())];
arg_types(hipe_bifs, enter_sdesc, 1) ->
- [t_tuple([t_integer(), t_integer(), t_integer(), t_integer(), t_integer()])];
+ [t_tuple([t_integer(), t_integer(), t_integer(), t_integer(), t_integer(), t_mfa()])];
arg_types(hipe_bifs, find_na_or_make_stub, 2) ->
[t_mfa(), t_boolean()];
arg_types(hipe_bifs, fun_to_address, 1) ->
@@ -4267,7 +2417,7 @@ arg_types(hipe_bifs, ref_get, 1) ->
arg_types(hipe_bifs, ref_set, 2) ->
[t_hiperef(), t_immediate()];
arg_types(hipe_bifs, remove_refs_from, 1) ->
- [t_mfa()];
+ [t_sup([t_mfa(), t_atom('all')])];
arg_types(hipe_bifs, set_funinfo_native_address, 3) ->
arg_types(hipe_bifs, set_native_address, 3);
arg_types(hipe_bifs, set_native_address, 3) ->
@@ -4284,28 +2434,6 @@ arg_types(hipe_bifs, write_u32, 2) ->
[t_integer(), t_integer()];
arg_types(hipe_bifs, write_u64, 2) ->
[t_integer(), t_integer()];
-%%------- io ------------------------------------------------------------------
-arg_types(io, format, 1) ->
- [t_io_format_string()];
-arg_types(io, format, 2) ->
- [t_io_format_string(), t_list()];
-arg_types(io, format, 3) ->
- [t_io_device(), t_io_format_string(), t_list()];
-arg_types(io, fwrite, 1) ->
- arg_types(io, format, 1);
-arg_types(io, fwrite, 2) ->
- arg_types(io, format, 2);
-arg_types(io, fwrite, 3) ->
- arg_types(io, format, 3);
-arg_types(io, put_chars, 1) ->
- [t_iodata()];
-arg_types(io, put_chars, 2) ->
- [t_io_device(), t_iodata()];
-%%------- io_lib --------------------------------------------------------------
-arg_types(io_lib, format, 2) ->
- arg_types(io, format, 2);
-arg_types(io_lib, fwrite, 2) ->
- arg_types(io_lib, format, 2);
%%------- lists ---------------------------------------------------------------
arg_types(lists, all, 2) ->
[t_fun([t_any()], t_boolean()), t_list()];
@@ -4377,10 +2505,6 @@ arg_types(lists, reverse, 1) ->
[t_list()];
arg_types(lists, reverse, 2) ->
[t_list(), t_any()];
-arg_types(lists, seq, 2) ->
- [t_integer(), t_integer()];
-arg_types(lists, seq, 3) ->
- [t_integer(), t_integer(), t_integer()];
arg_types(lists, sort, 1) ->
[t_list()];
arg_types(lists, sort, 2) ->
@@ -4409,97 +2533,12 @@ arg_types(lists, zipwith, 3) ->
[t_fun([t_any(), t_any()], t_any()), t_list(), t_list()];
arg_types(lists, zipwith3, 4) ->
[t_fun([t_any(), t_any(), t_any()], t_any()), t_list(), t_list(), t_list()];
-%%------- math ----------------------------------------------------------------
-arg_types(math, acos, 1) ->
- [t_number()];
-arg_types(math, acosh, 1) ->
- [t_number()];
-arg_types(math, asin, 1) ->
- [t_number()];
-arg_types(math, asinh, 1) ->
- [t_number()];
-arg_types(math, atan, 1) ->
- [t_number()];
-arg_types(math, atan2, 2) ->
- [t_number(), t_number()];
-arg_types(math, atanh, 1) ->
- [t_number()];
-arg_types(math, cos, 1) ->
- [t_number()];
-arg_types(math, cosh, 1) ->
- [t_number()];
-arg_types(math, erf, 1) ->
- [t_number()];
-arg_types(math, erfc, 1) ->
- [t_number()];
-arg_types(math, exp, 1) ->
- [t_number()];
-arg_types(math, log, 1) ->
- [t_number()];
-arg_types(math, log10, 1) ->
- [t_number()];
-arg_types(math, pi, 0) ->
- [];
-arg_types(math, pow, 2) ->
- [t_number(), t_number()];
-arg_types(math, sin, 1) ->
- [t_number()];
-arg_types(math, sinh, 1) ->
- [t_number()];
-arg_types(math, sqrt, 1) ->
- [t_number()];
-arg_types(math, tan, 1) ->
- [t_number()];
-arg_types(math, tanh, 1) ->
- [t_number()];
-%%-- net_kernel ---------------------------------------------------------------
-arg_types(net_kernel, dflag_unicode_io, 1) ->
- [t_pid()];
-%%------- ordsets -------------------------------------------------------------
-arg_types(ordsets, filter, 2) ->
- arg_types(lists, filter, 2);
-arg_types(ordsets, fold, 3) ->
- arg_types(lists, foldl, 3);
-%%------- os ------------------------------------------------------------------
-arg_types(os, getenv, 0) ->
- [];
-arg_types(os, getenv, 1) ->
- [t_string()];
-arg_types(os, getpid, 0) ->
- [];
-arg_types(os, putenv, 2) ->
- [t_string(), t_string()];
-arg_types(os, timestamp, 0) ->
- [];
-%%-- re -----------------------------------------------------------------------
-arg_types(re, compile, 1) ->
- [t_iodata()];
-arg_types(re, compile, 2) ->
- [t_sup(t_iodata(), t_charlist()), t_list(t_re_compile_option())];
-arg_types(re, run, 2) ->
- [t_sup(t_iodata(), t_charlist()), t_re_RE()];
-arg_types(re, run, 3) ->
- [t_sup(t_iodata(), t_charlist()), t_re_RE(), t_list(t_re_run_option())];
+
%%------- string --------------------------------------------------------------
arg_types(string, chars, 2) ->
[t_char(), t_non_neg_integer()];
arg_types(string, chars, 3) ->
[t_char(), t_non_neg_integer(), t_any()];
-arg_types(string, concat, 2) ->
- [t_string(), t_string()];
-arg_types(string, equal, 2) ->
- [t_string(), t_string()];
-arg_types(string, to_float, 1) ->
- [t_string()];
-arg_types(string, to_integer, 1) ->
- [t_string()];
-%%------- unicode -------------------------------------------------------------
-arg_types(unicode, characters_to_binary, 2) ->
- [t_ML(), t_encoding()];
-arg_types(unicode, characters_to_list, 2) ->
- [t_ML(), t_encoding()];
-arg_types(unicode, bin_is_7bit, 1) ->
- [t_binary()];
%%-----------------------------------------------------------------------------
arg_types(M, F, A) when is_atom(M), is_atom(F),
is_integer(A), 0 =< A, A =< 255 ->
@@ -4559,244 +2598,22 @@ check_fun_application(Fun, Args) ->
%% =====================================================================
-%% These are basic types that should probably be moved to erl_types
-%% =====================================================================
-
-t_socket() -> t_port(). % alias
-
-t_ip_address() ->
- T_int16 = t_from_range(0, 16#FFFF),
- t_sup(t_tuple([t_byte(), t_byte(), t_byte(), t_byte()]),
- t_tuple([T_int16, T_int16, T_int16, T_int16,
- T_int16, T_int16, T_int16, T_int16])).
-
-%% =====================================================================
%% Some basic types used in various parts of the system
%% =====================================================================
-t_date() ->
- t_tuple([t_pos_fixnum(), t_pos_fixnum(), t_pos_fixnum()]).
-
-t_time() ->
- t_tuple([t_non_neg_fixnum(), t_non_neg_fixnum(), t_non_neg_fixnum()]).
-
-t_timestamp() ->
- t_tuple([t_non_neg_fixnum(), t_non_neg_fixnum(), t_non_neg_fixnum()]).
-
-t_packet() ->
- t_sup([t_binary(), t_iolist(), t_httppacket()]).
-
-t_httppacket() ->
- t_sup([t_HttpRequest(), t_HttpResponse(),
- t_HttpHeader(), t_atom('http_eoh'), t_HttpError()]).
-
t_endian() ->
t_sup(t_atom('big'), t_atom('little')).
%% =====================================================================
-%% HTTP types documented in R12B-4
-%% =====================================================================
-
-t_HttpRequest() ->
- t_tuple([t_atom('http_request'), t_HttpMethod(), t_HttpUri(), t_HttpVersion()]).
-
-t_HttpResponse() ->
- t_tuple([t_atom('http_response'), t_HttpVersion(), t_integer(), t_HttpString()]).
-
-t_HttpHeader() ->
- t_tuple([t_atom('http_header'), t_integer(), t_HttpField(), t_any(), t_HttpString()]).
-
-t_HttpError() ->
- t_tuple([t_atom('http_error'), t_HttpString()]).
-
-t_HttpMethod() ->
- t_sup(t_HttpMethodAtom(), t_HttpString()).
-
-t_HttpMethodAtom() ->
- t_atoms(['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE']).
-
-t_HttpUri() ->
- t_sup([t_atom('*'),
- t_tuple([t_atom('absoluteURI'),
- t_sup(t_atom('http'), t_atom('https')),
- t_HttpString(),
- t_sup(t_non_neg_integer(), t_atom('undefined')),
- t_HttpString()]),
- t_tuple([t_atom('scheme'), t_HttpString(), t_HttpString()]),
- t_tuple([t_atom('abs_path'), t_HttpString()]),
- t_HttpString()]).
-
-t_HttpVersion() ->
- t_tuple([t_non_neg_integer(), t_non_neg_integer()]).
-
-t_HttpField() ->
- t_sup(t_HttpFieldAtom(), t_HttpString()).
-
-t_HttpFieldAtom() ->
- t_atoms(['Cache-Control', 'Connection', 'Date', 'Pragma', 'Transfer-Encoding',
- 'Upgrade', 'Via', 'Accept', 'Accept-Charset', 'Accept-Encoding',
- 'Accept-Language', 'Authorization', 'From', 'Host',
- 'If-Modified-Since', 'If-Match', 'If-None-Match', 'If-Range',
- 'If-Unmodified-Since', 'Max-Forwards', 'Proxy-Authorization',
- 'Range', 'Referer', 'User-Agent', 'Age', 'Location',
- 'Proxy-Authenticate', 'Public', 'Retry-After', 'Server', 'Vary',
- 'Warning', 'Www-Authenticate', 'Allow', 'Content-Base',
- 'Content-Encoding', 'Content-Language', 'Content-Length',
- 'Content-Location', 'Content-Md5', 'Content-Range', 'Content-Type',
- 'Etag', 'Expires', 'Last-Modified', 'Accept-Ranges',
- 'Set-Cookie', 'Set-Cookie2', 'X-Forwarded-For', 'Cookie',
- 'Keep-Alive', 'Proxy-Connection']).
-
-t_HttpString() ->
- t_sup(t_string(), t_binary()).
-
-%% =====================================================================
-%% These are used for the built-in functions of 'binary'
-%% =====================================================================
-
-t_binary_part() ->
- t_tuple([t_non_neg_integer(), t_integer()]).
-
-t_binary_canonical_part() ->
- t_tuple([t_non_neg_integer(), t_non_neg_integer()]).
-
-t_binary_pattern() ->
- t_sup([t_binary(),
- t_list(t_binary()),
- t_binary_compiled_pattern()]).
-
-t_binary_compiled_pattern() ->
- t_tuple([t_sup(t_atom('bm'), t_atom('ac')), t_binary()]).
-
-t_binary_options() ->
- t_list(t_tuple([t_atom('scope'), t_binary_part()])).
-
-%% =====================================================================
-%% These are used for the built-in functions of 'code'
-%% =====================================================================
-
-t_code_load_return(Mod) ->
- t_sup(t_tuple([t_atom('module'), case t_is_atom(Mod) of
- true -> Mod;
- false -> t_atom()
- end]),
- t_tuple([t_atom('error'), t_code_load_error_rsn()])).
-
-t_code_load_error_rsn() -> % also used in erlang:load_module/2
- t_sup([t_atom('badfile'),
- t_atom('nofile'),
- t_atom('not_purged'),
- t_atom('native_code'),
- t_atom('on_load'),
- t_atom('sticky_directory')]). % only for the 'code' functions
-
-%% =====================================================================
%% These are used for the built-in functions of 'erlang'
%% =====================================================================
-t_adler32() ->
- t_non_neg_integer().
-
t_crc32() ->
t_non_neg_integer().
-t_decode_packet_option() ->
- t_sup([t_tuple([t_atom('packet_size'), t_non_neg_integer()]),
- t_tuple([t_atom('line_length'), t_non_neg_integer()])]).
-
-t_decode_packet_type() ->
- t_sup([t_inet_setoption_packettype(), t_atom('httph'), t_atom('httph_bin')]).
-
-t_dist_exit() ->
- t_sup([t_atom('kill'), t_atom('noconnection'), t_atom('normal')]).
-
-t_match_spec_test_errors() ->
- t_list(t_sup(t_tuple([t_atom('error'), t_string()]),
- t_tuple([t_atom('warning'), t_string()]))).
-
-t_module_info_2() ->
- t_sup([t_atom('module'),
- t_atom('imports'),
- t_atom('exports'),
- t_atom('functions'),
- t_atom('attributes'),
- t_atom('compile'),
- t_atom('native_addresses')]).
-
-t_pinfo() ->
- t_sup([t_pinfo_item(), t_list(t_pinfo_item())]).
-
-t_pinfo_item() ->
- t_sup([t_atom('backtrace'),
- t_atom('current_function'),
- t_atom('dictionary'),
- t_atom('error_handler'),
- t_atom('garbage_collection'),
- t_atom('group_leader'),
- t_atom('heap_size'),
- t_atom('initial_call'),
- t_atom('last_calls'),
- t_atom('links'),
- t_atom('memory'),
- t_atom('message_queue_len'),
- t_atom('messages'),
- t_atom('monitored_by'),
- t_atom('monitors'),
- t_atom('priority'),
- t_atom('reductions'),
- t_atom('registered_name'),
- t_atom('sequential_trace_token'),
- t_atom('stack_size'),
- t_atom('status'),
- t_atom('suspending'),
- t_atom('total_heap_size'),
- t_atom('trap_exit')]).
-
-t_process_priority_level() ->
- t_sup([t_atom('max'), t_atom('high'), t_atom('normal'), t_atom('low')]).
-
-t_process_status() ->
- t_sup([t_atom('exiting'), t_atom('garbage_collecting'),
- t_atom('runnable'), t_atom('running'),
- t_atom('suspended'), t_atom('waiting')]).
-
-t_raise_errorclass() ->
- t_sup([t_atom('error'), t_atom('exit'), t_atom('throw')]).
-
-t_sendoptions() ->
- t_sup(t_atom('noconnect'), t_atom('nosuspend')).
-
-t_seq_trace_info() ->
- t_sup([t_atom('send'),
- t_atom('receive'),
- t_atom('print'),
- t_atom('timestamp'),
- t_atom('label'),
- t_atom('serial')]).
-
-%% XXX: Better if we also maintain correspondencies between infos and values
-t_seq_trace_info_returns() ->
- Values = t_sup([t_non_neg_integer(), t_boolean(),
- t_tuple([t_non_neg_integer(), t_non_neg_integer()])]),
- t_sup(t_tuple([t_seq_trace_info(), Values]), t_nil()).
-
t_sequential_tracer() ->
t_sup([t_atom('false'), t_pid(), t_port()]).
-t_spawn_options() ->
- t_sup([t_atom('link'),
- t_atom('monitor'),
- t_tuple([t_atom('priority'), t_process_priority_level()]),
- t_tuple([t_atom('min_heap_size'), t_fixnum()]),
- t_tuple([t_atom('min_bin_vheap_size'), t_fixnum()]),
- t_tuple([t_atom('fullsweep_after'), t_fixnum()])]).
-
-t_spawn_opt_return(List) ->
- case t_is_none(t_inf(t_list(t_atom('monitor')), List)) of
- true -> t_pid();
- false -> t_sup(t_pid(), t_tuple([t_pid(), t_reference()]))
- end.
-
t_system_cpu_topology() ->
t_sup(t_atom('undefined'), t_system_cpu_topology_level_entry_list()).
@@ -4833,17 +2650,6 @@ t_internal_cpu_topology() -> %% Internal undocumented type
t_non_neg_fixnum()])),
t_atom('undefined')).
-t_scheduler_bind_type_args() ->
- t_sup([t_atom('default_bind'),
- t_atom('no_node_processor_spread'),
- t_atom('no_node_thread_spread'),
- t_atom('no_spread'),
- t_atom('processor_spread'),
- t_atom('spread'),
- t_atom('thread_spread'),
- t_atom('thread_no_node_processor_spread'),
- t_atom('unbound')]).
-
t_scheduler_bind_type_results() ->
t_sup([t_atom('no_node_processor_spread'),
t_atom('no_node_thread_spread'),
@@ -4854,160 +2660,9 @@ t_scheduler_bind_type_results() ->
t_atom('thread_no_node_processor_spread'),
t_atom('unbound')]).
-t_system_monitor_settings() ->
- t_sup([t_atom('undefined'),
- t_tuple([t_pid(), t_system_monitor_options()])]).
-
-t_system_monitor_options() ->
- t_list(t_sup([t_atom('busy_port'),
- t_atom('busy_dist_port'),
- t_tuple([t_atom('long_gc'), t_integer()]),
- t_tuple([t_atom('large_heap'), t_integer()])])).
-
t_system_multi_scheduling() ->
t_sup([t_atom('blocked'), t_atom('disabled'), t_atom('enabled')]).
-t_system_profile_options() ->
- t_list(t_sup([t_atom('exclusive'),
- t_atom('runnable_ports'),
- t_atom('runnable_procs'),
- t_atom('scheduler')])).
-
-t_system_profile_return() ->
- t_sup(t_atom('undefined'),
- t_tuple([t_sup(t_pid(), t_port()), t_system_profile_options()])).
-
-t_system_build_type_return() ->
- t_sup([t_atom('opt'),
- t_atom('debug'),
- t_atom('purify'),
- t_atom('quantify'),
- t_atom('purecov'),
- t_atom('gcov'),
- t_atom('valgrind'),
- t_atom('gprof'),
- t_atom('lcnt')]).
-
-%% =====================================================================
-%% These are used for the built-in functions of 'ets'
-%% =====================================================================
-
-t_tab() ->
- t_sup(t_tid(), t_atom()).
-
-t_match_pattern() ->
- t_sup(t_atom(), t_tuple()).
-
-t_matchspecs() ->
- t_list(t_tuple([t_match_pattern(), t_list(), t_list()])).
-
-t_matchres() ->
- t_sup(t_tuple([t_list(), t_any()]), t_atom('$end_of_table')).
-
-%% From the 'ets' documentation
-%%-----------------------------
-%% Option = Type | Access | named_table | {keypos,Pos}
-%% | {heir,pid(),HeirData} | {heir,none} | Tweaks
-%% Type = set | ordered_set | bag | duplicate_bag
-%% Access = public | protected | private
-%% Tweaks = {write_concurrency,boolean()}
-%% | {read_concurrency,boolean()} | compressed
-%% Pos = integer()
-%% HeirData = term()
-t_ets_new_options() ->
- t_list(t_sup([t_atom('set'),
- t_atom('ordered_set'),
- t_atom('bag'),
- t_atom('duplicate_bag'),
- t_atom('public'),
- t_atom('protected'),
- t_atom('private'),
- t_atom('named_table'),
- t_tuple([t_atom('keypos'), t_integer()]),
- t_tuple([t_atom('heir'), t_pid(), t_any()]),
- t_tuple([t_atom('heir'), t_atom('none')]),
- t_tuple([t_atom('write_concurrency'), t_boolean()]),
- t_tuple([t_atom('read_concurrency'), t_boolean()]),
- t_atom('compressed')])).
-
-t_ets_info_items() ->
- t_sup([t_atom('fixed'),
- t_atom('safe_fixed'),
- t_atom('keypos'),
- t_atom('memory'),
- t_atom('name'),
- t_atom('named_table'),
- t_atom('node'),
- t_atom('owner'),
- t_atom('protection'),
- t_atom('size'),
- t_atom('compressed'),
- t_atom('heir'),
- t_atom('stats'),
- t_atom('type')]).
-
-%% =====================================================================
-%% These are used for the built-in functions of 'gen_tcp'
-%% =====================================================================
-
-t_gen_tcp_accept() ->
- t_sup(t_tuple([t_atom('ok'), t_socket()]),
- t_tuple([t_atom('error'), t_sup([t_atom('closed'),
- t_atom('timeout'),
- t_inet_posix_error()])])).
-
-t_gen_tcp_address() ->
- t_sup([t_string(), t_atom(), t_ip_address()]).
-
-t_gen_tcp_port() ->
- t_from_range(0, 16#FFFF).
-
-t_gen_tcp_connect_option() ->
- t_sup([t_atom('list'),
- t_atom('binary'),
- t_tuple([t_atom('ip'), t_ip_address()]),
- t_tuple([t_atom('port'), t_gen_tcp_port()]),
- t_tuple([t_atom('fd'), t_integer()]),
- t_atom('inet6'),
- t_atom('inet'),
- t_inet_setoption()]).
-
-t_gen_tcp_listen_option() ->
- t_sup([t_atom('list'),
- t_atom('binary'),
- t_tuple([t_atom('backlog'), t_non_neg_integer()]),
- t_tuple([t_atom('ip'), t_ip_address()]),
- t_tuple([t_atom('fd'), t_integer()]),
- t_atom('inet6'),
- t_atom('inet'),
- t_inet_setoption()]).
-
-t_gen_tcp_recv() ->
- t_sup(t_tuple([t_atom('ok'), t_packet()]),
- t_tuple([t_atom('error'), t_sup([t_atom('closed'),
- t_inet_posix_error()])])).
-
-%% =====================================================================
-%% These are used for the built-in functions of 'gen_udp'
-%% =====================================================================
-
-t_gen_udp_connect_option() ->
- t_sup([t_atom('list'),
- t_atom('binary'),
- t_tuple([t_atom('ip'), t_ip_address()]),
- t_tuple([t_atom('fd'), t_integer()]),
- t_atom('inet6'),
- t_atom('inet'),
- t_inet_setoption()]).
-
-t_gen_udp_recv() ->
- t_sup(t_tuple([t_atom('ok'),
- t_tuple([t_ip_address(),
- t_gen_tcp_port(),
- t_packet()])]),
- t_tuple([t_atom('error'),
- t_sup(t_atom('not_owner'), t_inet_posix_error())])).
-
%% =====================================================================
%% These are used for the built-in functions of 'hipe_bifs'
%% =====================================================================
@@ -5040,131 +2695,6 @@ t_insn_type() ->
t_atom('closure')]).
%% =====================================================================
-%% These are used for the built-in functions of 'inet'
-%% =====================================================================
-
-t_inet_setoption() ->
- t_sup([%% first the 2-tuple options
- t_tuple([t_atom('active'), t_sup(t_boolean(), t_atom('once'))]),
- t_tuple([t_atom('broadcast'), t_boolean()]),
- t_tuple([t_atom('delay_send'), t_boolean()]),
- t_tuple([t_atom('dontroute'), t_boolean()]),
- t_tuple([t_atom('exit_on_close'), t_boolean()]),
- t_tuple([t_atom('header'), t_non_neg_integer()]),
- t_tuple([t_atom('keepalive'), t_boolean()]),
- t_tuple([t_atom('nodelay'), t_boolean()]),
- t_tuple([t_atom('packet'), t_inet_setoption_packettype()]),
- t_tuple([t_atom('packet_size'), t_non_neg_integer()]),
- t_tuple([t_atom('read_packets'), t_non_neg_integer()]),
- t_tuple([t_atom('recbuf'), t_non_neg_integer()]),
- t_tuple([t_atom('reuseaddr'), t_boolean()]),
- t_tuple([t_atom('send_timeout'), t_non_neg_integer()]),
- t_tuple([t_atom('sndbuf'), t_non_neg_integer()]),
- t_tuple([t_atom('priority'), t_non_neg_integer()]),
- t_tuple([t_atom('tos'), t_non_neg_integer()]),
- %% and a 4-tuple option
- t_tuple([t_atom('raw'),
- t_non_neg_integer(), % protocol level
- t_non_neg_integer(), % option number
- t_binary()])]). % actual option value
-
-t_inet_setoption_packettype() ->
- t_sup([t_atom('raw'),
- t_integers([0,1,2,4]),
- t_atom('asn1'), t_atom('cdr'), t_atom('sunrm'),
- t_atom('fcgi'), t_atom('tpkt'), t_atom('line'),
- t_atom('http'),
- t_atom('http_bin')]). %% but t_atom('httph') is not needed
-
-t_inet_posix_error() ->
- t_atom(). %% XXX: Very underspecified
-
-%% =====================================================================
-%% These are used for the built-in functions of 'io'
-%% =====================================================================
-
-t_io_device() ->
- t_sup(t_atom(), t_pid()).
-
-%% The documentation in R11B-4 reads
-%% Format ::= atom() | string() | binary()
-%% but the Format can also be a (deep) list, hence the type below
-t_io_format_string() ->
- t_sup([t_atom(), t_list(), t_binary()]).
-
-%% =====================================================================
-%% These are used for the built-in functions of 're'; the functions
-%% whose last name component starts with a capital letter are types
-%% =====================================================================
-
-t_re_MP() -> %% it's supposed to be an opaque data type
- t_tuple([t_atom('re_pattern'), t_integer(), t_integer(), t_binary()]).
-
-t_re_RE() ->
- t_sup([t_re_MP(), t_iodata(), t_charlist()]).
-
-t_re_compile_option() ->
- t_sup([t_atoms(['unicode', 'anchored', 'caseless', 'dollar_endonly',
- 'dotall', 'extended', 'firstline', 'multiline',
- 'no_auto_capture', 'dupnames', 'ungreedy']),
- t_tuple([t_atom('newline'), t_re_NLSpec()]),
- t_atoms(['bsr_anycrlf', 'bsr_unicode'])]).
-
-t_re_run_option() ->
- t_sup([t_atoms(['anchored', 'global', 'notbol', 'noteol', 'notempty']),
- t_tuple([t_atom('offset'), t_integer()]),
- t_tuple([t_atom('newline'), t_re_NLSpec()]),
- t_tuple([t_atom('capture'), t_re_ValueSpec()]),
- t_tuple([t_atom('capture'), t_re_ValueSpec(), t_re_Type()]),
- t_re_compile_option()]).
-
-t_re_ErrorSpec() ->
- t_tuple([t_string(), t_non_neg_integer()]).
-
-t_re_Type() ->
- t_atoms(['index', 'list', 'binary']).
-
-t_re_NLSpec() ->
- t_atoms(['cr', 'crlf', 'lf', 'anycrlf', 'any']).
-
-t_re_ValueSpec() ->
- t_sup(t_atoms(['all', 'all_but_first', 'first', 'none']), t_re_ValueList()).
-
-t_re_ValueList() ->
- t_list(t_sup([t_integer(), t_string(), t_atom()])).
-
-t_re_Captured() ->
- t_list(t_sup(t_re_CapturedData(), t_list(t_re_CapturedData()))).
-
-t_re_CapturedData() ->
- t_sup([t_tuple([t_integer(), t_integer()]), t_string(), t_binary()]).
-
-%% =====================================================================
-%% These are used for the built-in functions of 'prim_file'
-%% =====================================================================
-
-t_prim_file_name() ->
- t_sup(t_unicode_string(), t_binary()).
-
-%% =====================================================================
-%% These are used for the built-in functions of 'unicode'
-%% =====================================================================
-
-t_ML() -> % a binary or a possibly deep list of integers or binaries
- t_sup(t_list(t_sup([t_integer(), t_binary(), t_list()])), t_binary()).
-
-t_encoding() ->
- t_sup([t_atoms(['latin1', 'unicode', 'utf8', 'utf16', 'utf32']),
- t_tuple([t_atom('utf16'), t_endian()]),
- t_tuple([t_atom('utf32'), t_endian()])]).
-
-t_file_encoding() ->
- t_atoms(['latin1', 'utf8']).
-
-t_encoding_a2b() -> % for the 2nd arg of atom_to_binary/2 and binary_to_atom/2
- t_atoms(['latin1', 'unicode', 'utf8']).
-
-%% =====================================================================
%% Some testing code for ranges below
%% =====================================================================
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index 1789fc79fa..1579735773 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -275,8 +275,8 @@
-type tag() :: ?atom_tag | ?binary_tag | ?function_tag | ?identifier_tag
| ?list_tag | ?matchstate_tag | ?nil_tag | ?number_tag
- | ?opaque_tag | ?product_tag | ?tuple_tag | ?tuple_set_tag
- | ?union_tag | ?var_tag.
+ | ?opaque_tag | ?product_tag | ?remote_tag
+ | ?tuple_tag | ?tuple_set_tag | ?union_tag | ?var_tag.
-define(float_qual, float).
-define(integer_qual, integer).
@@ -2318,10 +2318,14 @@ t_inf(?product(_), _, _Mode) ->
?none;
t_inf(_, ?product(_), _Mode) ->
?none;
-t_inf(?tuple(?any, ?any, ?any), ?tuple(_, _, _) = T, _Mode) -> T;
-t_inf(?tuple(_, _, _) = T, ?tuple(?any, ?any, ?any), _Mode) -> T;
-t_inf(?tuple(?any, ?any, ?any), ?tuple_set(_) = T, _Mode) -> T;
-t_inf(?tuple_set(_) = T, ?tuple(?any, ?any, ?any), _Mode) -> T;
+t_inf(?tuple(?any, ?any, ?any), ?tuple(_, _, _) = T, _Mode) ->
+ subst_all_vars_to_any(T);
+t_inf(?tuple(_, _, _) = T, ?tuple(?any, ?any, ?any), _Mode) ->
+ subst_all_vars_to_any(T);
+t_inf(?tuple(?any, ?any, ?any), ?tuple_set(_) = T, _Mode) ->
+ subst_all_vars_to_any(T);
+t_inf(?tuple_set(_) = T, ?tuple(?any, ?any, ?any), _Mode) ->
+ subst_all_vars_to_any(T);
t_inf(?tuple(Elements1, Arity, _Tag1), ?tuple(Elements2, Arity, _Tag2), Mode) ->
case t_inf_lists_strict(Elements1, Elements2, Mode) of
bottom -> ?none;
diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml
index ed8cf0b8d3..1841c7d9de 100644
--- a/lib/hipe/doc/src/notes.xml
+++ b/lib/hipe/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2006</year><year>2011</year>
+ <year>2006</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,50 @@
</header>
<p>This document describes the changes made to HiPE.</p>
+<section><title>Hipe 3.9.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Correct handling of type names in contracts. Fix
+ crash related to contract checking. Do not rewrite
+ unchanged PLT. </p>
+ <p>
+ Own Id: OTP-10083</p>
+ </item>
+ <item>
+ <p>
+ Changes in comments and minor code cleanups</p>
+ <p>
+ Thanks to Kostis Sagonas.</p>
+ <p>
+ Own Id: OTP-10230</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Remove all code, documentation, options and diagnostic
+ functions which were related to the experimental hybrid
+ heap implementation.</p>
+ <p>
+ Own Id: OTP-10105</p>
+ </item>
+ <item>
+ <p> Bugs in <c>erl_types:t_inf()</c> (HiPE) and in
+ <c>dialyzer_dataflow</c> (Dialyzer) have been fixed. </p>
+ <p>
+ Own Id: OTP-10191</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Hipe 3.9.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/hipe/icode/hipe_beam_to_icode.erl b/lib/hipe/icode/hipe_beam_to_icode.erl
index a44171b2f5..81249c958e 100644
--- a/lib/hipe/icode/hipe_beam_to_icode.erl
+++ b/lib/hipe/icode/hipe_beam_to_icode.erl
@@ -41,6 +41,9 @@
%%
%%-ifndef(DEBUG).
%%-define(DEBUG,6).
+%% Choose one of two tracing methods
+%%-define(DEBUG_BIF_CALL_TRACE,true).
+%%-define(IO_FORMAT_CALL_TRACE,true).
%%-endif.
-include("../main/hipe.hrl").
@@ -51,8 +54,27 @@
-define(no_debug_msg(Str,Xs),ok).
%%-define(no_debug_msg(Str,Xs),msg(Str,Xs)).
--define(mk_debugcode(MFA, Env, Code),
- case MFA of
+-ifdef(DEBUG_BIF_CALL_TRACE).
+
+%% Use BIF hipe_bifs_debug_native_called_2 to trace function calls
+mk_debug_calltrace({_M,_F,A}=MFA, Env, Code) ->
+ MFAVar = mk_var(new),
+ Ignore = mk_var(new),
+ MkMfa = hipe_icode:mk_move(MFAVar,hipe_icode:mk_const(MFA)),
+ Args = [mk_var({x,I-1}) || I <- lists:seq(1,A)],
+ ArgTup = mk_var(new),
+ MkArgTup = hipe_icode:mk_primop([ArgTup], mktuple, Args),
+ Call = hipe_icode:mk_primop([Ignore], debug_native_called,
+ [MFAVar,ArgTup]),
+ {[MkMfa,MkArgTup,Call | Code], Env}.
+
+-endif.
+
+-ifdef(IO_FORMAT_CALL_TRACE).
+
+%% Use io:format to trace function calls
+mk_debug_calltrace(MFA, Env, Code) ->
+ case MFA of
{io,_,_} ->
%% We do not want to loop infinitely if we are compiling
%% the module io.
@@ -69,15 +91,16 @@
Call =
hipe_icode:mk_call([Ignore],io,format,[StringVar,MFAVar],remote),
{[MkMfa,MkString,Call | Code], Env}
- end).
+ end.
+-endif.
+
%%-----------------------------------------------------------------------
-%% Exported types
+%% Types
%%-----------------------------------------------------------------------
-type hipe_beam_to_icode_ret() :: [{mfa(),#icode{}}].
-
%%-----------------------------------------------------------------------
%% Internal data structures
%%-----------------------------------------------------------------------
@@ -127,7 +150,7 @@ trans_mfa_code(M,F,A, FunBeamCode, ClosureInfo) ->
MFA = {M,F,A},
%% Debug code
?IF_DEBUG_LEVEL(5,
- {Code3,_Env3} = ?mk_debugcode(MFA, Env2, Code2),
+ {Code3,_Env3} = mk_debug_calltrace(MFA, Env1, Code2),
{Code3,_Env3} = {Code2,Env1}),
%% For stack optimization
Leafness = leafness(Code3),
@@ -315,19 +338,19 @@ trans_fun([{call_ext_last,_N,{extfunc,M,F,A},_}|Instructions], Env) ->
%%--- bif0 ---
trans_fun([{bif,BifName,nofail,[],Reg}|Instructions], Env) ->
BifInst = trans_bif0(BifName,Reg),
- [hipe_icode:mk_comment({bif0,BifName}),BifInst|trans_fun(Instructions,Env)];
+ [BifInst|trans_fun(Instructions,Env)];
%%--- bif1 ---
trans_fun([{bif,BifName,{f,Lbl},[_] = Args,Reg}|Instructions], Env) ->
{BifInsts,Env1} = trans_bif(1,BifName,Lbl,Args,Reg,Env),
- [hipe_icode:mk_comment({bif1,BifName})|BifInsts] ++ trans_fun(Instructions,Env1);
+ BifInsts ++ trans_fun(Instructions,Env1);
%%--- bif2 ---
trans_fun([{bif,BifName,{f,Lbl},[_,_] = Args,Reg}|Instructions], Env) ->
{BifInsts,Env1} = trans_bif(2,BifName,Lbl,Args,Reg,Env),
- [hipe_icode:mk_comment({bif2,BifName})|BifInsts] ++ trans_fun(Instructions,Env1);
+ BifInsts ++ trans_fun(Instructions,Env1);
%%--- bif3 ---
trans_fun([{bif,BifName,{f,Lbl},[_,_,_] = Args,Reg}|Instructions], Env) ->
{BifInsts,Env1} = trans_bif(3,BifName,Lbl,Args,Reg,Env),
- [hipe_icode:mk_comment({bif3,BifName})|BifInsts] ++ trans_fun(Instructions,Env1);
+ BifInsts ++ trans_fun(Instructions,Env1);
%%--- allocate
trans_fun([{allocate,StackSlots,_}|Instructions], Env) ->
trans_allocate(StackSlots) ++ trans_fun(Instructions,Env);
@@ -816,7 +839,7 @@ trans_fun([{test,bs_test_tail2,{f,Lbl},[Ms,Numbits]}| Instructions], Env) ->
trans_op_call({hipe_bs_primop,{bs_test_tail,Numbits}},
Lbl, [MsVar], [], Env, Instructions);
%%--------------------------------------------------------------------
-%% New bit syntax instructions added in February 2004 (R10B).
+%% bit syntax instructions added in February 2004 (R10B).
%%--------------------------------------------------------------------
trans_fun([{bs_init2,{f,Lbl},Size,_Words,_LiveRegs,{field_flags,Flags0},X}|
Instructions], Env) ->
@@ -1031,7 +1054,7 @@ trans_fun([{arithfbif,fnegate,Lab,[SrcR],DestR}|Instructions], Env) ->
trans_fun([{arithbif,'-',Lab,[{float,0.0},SrcR],DestR}|Instructions], Env)
end;
%%--------------------------------------------------------------------
-%% New apply instructions added in April 2004 (R10B).
+%% apply instructions added in April 2004 (R10B).
%%--------------------------------------------------------------------
trans_fun([{apply,Arity}|Instructions], Env) ->
BeamArgs = extract_fun_args(Arity+2), %% +2 is for M and F
@@ -1047,21 +1070,21 @@ trans_fun([{apply_last,Arity,_N}|Instructions], Env) -> % N is StackAdjustment?
hipe_icode:mk_enter_primop(#apply_N{arity=Arity}, [M,F|Args])
| trans_fun(Instructions,Env)];
%%--------------------------------------------------------------------
-%% New test instruction added in April 2004 (R10B).
+%% test for boolean added in April 2004 (R10B).
%%--------------------------------------------------------------------
%%--- is_boolean ---
trans_fun([{test,is_boolean,{f,Lbl},[Arg]}|Instructions], Env) ->
{Code,Env1} = trans_type_test(boolean,Lbl,Arg,Env),
[Code | trans_fun(Instructions,Env1)];
%%--------------------------------------------------------------------
-%% New test instruction added in June 2005 for R11
+%% test for function with specific arity added in June 2005 (R11).
%%--------------------------------------------------------------------
%%--- is_function2 ---
trans_fun([{test,is_function2,{f,Lbl},[Arg,Arity]}|Instructions], Env) ->
{Code,Env1} = trans_type_test2(function2,Lbl,Arg,Arity,Env),
[Code | trans_fun(Instructions,Env1)];
%%--------------------------------------------------------------------
-%% New garbage-collecting BIFs added in January 2006 for R11B.
+%% garbage collecting BIFs added in January 2006 (R11B).
%%--------------------------------------------------------------------
trans_fun([{gc_bif,'-',Fail,_Live,[SrcR],DstR}|Instructions], Env) ->
%% Unary minus. Change this to binary minus.
@@ -1079,21 +1102,21 @@ trans_fun([{gc_bif,Name,Fail,_Live,SrcRs,DstR}|Instructions], Env) ->
trans_fun([{bif,Name,Fail,SrcRs,DstR}|Instructions], Env)
end;
%%--------------------------------------------------------------------
-%% New test instruction added in July 2007 for R12.
+%% test for bitstream added in July 2007 (R12).
%%--------------------------------------------------------------------
%%--- is_bitstr ---
trans_fun([{test,is_bitstr,{f,Lbl},[Arg]}|Instructions], Env) ->
{Code,Env1} = trans_type_test(bitstr, Lbl, Arg, Env),
[Code | trans_fun(Instructions, Env1)];
%%--------------------------------------------------------------------
-%% New stack triming instruction added in October 2007 for R12.
+%% stack triming instruction added in October 2007 (R12).
%%--------------------------------------------------------------------
trans_fun([{trim,N,NY}|Instructions], Env) ->
%% trim away N registers leaving NY registers
Moves = trans_trim(N, NY),
Moves ++ trans_fun(Instructions, Env);
%%--------------------------------------------------------------------
-%% New line/1 instruction in R15.
+%% line instruction added in Fall 2012 (R15).
%%--------------------------------------------------------------------
trans_fun([{line,_}|Instructions], Env) ->
trans_fun(Instructions,Env);
@@ -1297,7 +1320,7 @@ trans_bin([{bs_put_integer,{f,Lbl},Size,Unit,{field_flags,Flags0},Source}|
SrcInstrs ++ trans_bin_call({hipe_bs_primop, Name},
Lbl, [Src|Args], [Offset], Base, Offset, Env2, Instructions);
%%----------------------------------------------------------------
-%% New binary construction instructions added in R12B-5 (Fall 2008).
+%% binary construction instructions added in Fall 2008 (R12B-5).
%%----------------------------------------------------------------
trans_bin([{bs_put_utf8,{f,Lbl},_FF,A3}|Instructions], Base, Offset, Env) ->
Src = trans_arg(A3),
@@ -1348,7 +1371,7 @@ trans_bs_get_or_skip_utf32(Lbl, Ms, Flags0, X, Instructions, Env) ->
Lbl, [Dst,MsVar], [MsVar], Env1, Instructions).
%%-----------------------------------------------------------------------
-%% trans_arith(Op, SrcVars, Des, Lab, Env) -> { Icode, NewEnv }
+%% trans_arith(Op, SrcVars, Des, Lab, Env) -> {Icode, NewEnv}
%% A failure label of type {f,0} means in a body.
%% A failure label of type {f,L} where L>0 means in a guard.
%% Within a guard a failure should branch to the next guard and
@@ -1454,7 +1477,7 @@ clone_dst(Dest) ->
%%-----------------------------------------------------------------------
-%% trans_type_test(Test, Lbl, Arg, Env) -> { Icode, NewEnv }
+%% trans_type_test(Test, Lbl, Arg, Env) -> {Icode, NewEnv}
%% Handles all unary type tests like is_integer etc.
%%-----------------------------------------------------------------------
@@ -1466,7 +1489,7 @@ trans_type_test(Test, Lbl, Arg, Env) ->
{[Move,I,True],Env1}.
%%
-%% This handles binary type tests. Currently, the only such is the new
+%% This handles binary type tests. Currently, the only such is the
%% is_function/2 BIF.
%%
trans_type_test2(function2, Lbl, Arg, Arity, Env) ->
@@ -1479,7 +1502,7 @@ trans_type_test2(function2, Lbl, Arg, Arity, Env) ->
%%-----------------------------------------------------------------------
%% trans_puts(Code, Environment) ->
-%% { Movs, Code, Vars, NewEnv }
+%% {Movs, Code, Vars, NewEnv}
%%-----------------------------------------------------------------------
trans_puts(Code, Env) ->
diff --git a/lib/hipe/icode/hipe_icode.hrl b/lib/hipe/icode/hipe_icode.hrl
index 65deaf6d7c..d76eebf78d 100644
--- a/lib/hipe/icode/hipe_icode.hrl
+++ b/lib/hipe/icode/hipe_icode.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. 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
@@ -64,9 +64,9 @@
-type icode_if_op() :: '>' | '<' | '>=' | '=<' | '=:=' | '=/=' | '==' | '/='
| 'fixnum_eq' | 'fixnum_neq' | 'fixnum_lt'
| 'fixnum_le' | 'fixnum_ge' | 'fixnum_gt'
- | 'suspend_msg_timeout'.
+ | 'op_exact_eqeq_2' | 'suspend_msg_timeout'.
--type icode_type_test() :: 'atom' | 'bignum' | 'binary' | 'bitrst' | 'boolean'
+-type icode_type_test() :: 'atom' | 'bignum' | 'binary' | 'bitstr' | 'boolean'
| 'cons' | 'constant' | 'fixnum' | 'float'
| 'function' | 'function2' | 'integer' | 'list' | 'nil'
| 'number' | 'pid' | 'port' | 'reference' | 'tuple'
@@ -88,7 +88,7 @@
-type icode_call_type() :: 'local' | 'primop' | 'remote'.
-type icode_exit_class() :: 'error' | 'exit' | 'rethrow' | 'throw'.
--type icode_comment_text() :: atom() | string() | {atom(), term()}.
+-type icode_comment_text() :: atom() | string().
-type icode_info() :: [{'arg_types', [erl_types:erl_type()]}].
diff --git a/lib/hipe/icode/hipe_icode_primops.erl b/lib/hipe/icode/hipe_icode_primops.erl
index a413531c07..b0113fc556 100644
--- a/lib/hipe/icode/hipe_icode_primops.erl
+++ b/lib/hipe/icode/hipe_icode_primops.erl
@@ -137,7 +137,8 @@ is_safe({hipe_bs_primop, {bs_private_append, _, _}}) -> false;
is_safe({hipe_bs_primop, bs_init_writable}) -> true;
is_safe(#mkfun{}) -> true;
is_safe(#unsafe_element{}) -> true;
-is_safe(#unsafe_update_element{}) -> true.
+is_safe(#unsafe_update_element{}) -> true;
+is_safe(debug_native_called) -> false.
-spec fails(icode_funcall()) -> boolean().
@@ -237,6 +238,7 @@ fails({hipe_bs_primop, bs_init_writable}) -> true;
fails(#mkfun{}) -> false;
fails(#unsafe_element{}) -> false;
fails(#unsafe_update_element{}) -> false;
+fails(debug_native_called) -> false;
%% Apparently, we are calling fails/1 for all MFAs which are compiled.
%% This is weird and we should restructure the compiler to avoid
%% calling fails/1 for things that are not primops.
@@ -721,6 +723,8 @@ type(Primop, Args) ->
erl_types:t_any();
redtest ->
erl_types:t_any();
+ debug_native_called ->
+ erl_types:t_any();
{M, F, A} ->
erl_bif_types:type(M, F, A, Args)
end.
@@ -893,6 +897,8 @@ type(Primop) ->
erl_types:t_any();
redtest ->
erl_types:t_any();
+ debug_native_called ->
+ erl_types:t_any();
{M, F, A} ->
erl_bif_types:type(M, F, A)
end.
diff --git a/lib/hipe/icode/hipe_icode_type.erl b/lib/hipe/icode/hipe_icode_type.erl
index f98d859822..046949d2f2 100644
--- a/lib/hipe/icode/hipe_icode_type.erl
+++ b/lib/hipe/icode/hipe_icode_type.erl
@@ -79,13 +79,13 @@
-import(erl_types, [number_min/1, number_max/1,
t_any/0, t_atom/1, t_atom/0, t_atom_vals/1,
t_binary/0, t_bitstr/0, t_bitstr_base/1, t_bitstr_unit/1,
- t_boolean/0, t_cons/0, t_constant/0,
+ t_boolean/0, t_cons/0,
t_float/0, t_from_term/1, t_from_range/2,
t_fun/0, t_fun/1, t_fun/2, t_fun_args/1, t_fun_arity/1,
t_inf/2, t_inf_lists/2, t_integer/0,
t_integer/1, t_is_atom/1, t_is_any/1,
t_is_binary/1, t_is_bitstr/1, t_is_bitwidth/1, t_is_boolean/1,
- t_is_fixnum/1, t_is_cons/1, t_is_constant/1,
+ t_is_fixnum/1, t_is_cons/1,
t_is_maybe_improper_list/1, t_is_equal/2, t_is_float/1,
t_is_fun/1, t_is_integer/1, t_is_non_neg_integer/1,
t_is_number/1, t_is_matchstate/1,
@@ -587,7 +587,6 @@ do_type(I, Info, Var) ->
TrueLab = hipe_icode:type_true_label(I),
FalseLab = hipe_icode:type_false_label(I),
None = t_none(),
-
case lookup(Var, Info) of
None ->
[{TrueLab, Info}, {FalseLab, Info}];
@@ -1699,7 +1698,6 @@ lookup_list0([H|T], Info, Acc) ->
lookup_list0([], _, Acc) ->
lists:reverse(Acc).
-
%% safe_lookup treats anything that is neither in the map nor a
%% constant as t_any(). Use this during transformations.
diff --git a/lib/hipe/misc/Makefile b/lib/hipe/misc/Makefile
index 1204ab7c0b..16166f552c 100644
--- a/lib/hipe/misc/Makefile
+++ b/lib/hipe/misc/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2010. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. 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/lib/hipe/rtl/hipe_rtl_primops.erl b/lib/hipe/rtl/hipe_rtl_primops.erl
index 5f273d8251..53aaa72aa6 100644
--- a/lib/hipe/rtl/hipe_rtl_primops.erl
+++ b/lib/hipe/rtl/hipe_rtl_primops.erl
@@ -396,6 +396,8 @@ gen_primop({Op,Dst,Args,Cont,Fail}, IsGuard, ConstTab) ->
[Dst1]->
hipe_tagscheme:unsafe_tag_float(Dst1, Arg)
end;
+ debug_native_called ->
+ [hipe_rtl:mk_call(Dst, Op, Args, Cont, Fail, not_remote)];
%% Only names listed above are accepted! MFA:s are not primops!
_ ->
diff --git a/lib/hipe/sparc/hipe_rtl_to_sparc.erl b/lib/hipe/sparc/hipe_rtl_to_sparc.erl
index df5e2b0077..dc001f865e 100644
--- a/lib/hipe/sparc/hipe_rtl_to_sparc.erl
+++ b/lib/hipe/sparc/hipe_rtl_to_sparc.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. 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
@@ -19,6 +19,7 @@
%%
-module(hipe_rtl_to_sparc).
+
-export([translate/1]).
-include("../rtl/hipe_rtl.hrl").
@@ -142,8 +143,7 @@ mk_fload_rr(Base1, Base2, Dst) ->
mk_fload_ii(Base1, Base2, Dst) ->
io:format("~w: RTL fload with two immediates\n", [?MODULE]),
Tmp = new_untagged_temp(),
- mk_set(Base1, Tmp,
- mk_fload_ri(Tmp, Base2, Dst)).
+ mk_set(Base1, Tmp, mk_fload_ri(Tmp, Base2, Dst)).
mk_fload_ri(Base, Disp, Dst) ->
hipe_sparc:mk_fload(Base, Disp, Dst, 'new').
@@ -239,8 +239,7 @@ mk_alu_ii(XAluOp, Src1, Src2, Dst) ->
io:format("~w: ALU with two immediates (~w ~w ~w ~w)\n",
[?MODULE, XAluOp, Src1, Src2, Dst]),
Tmp = new_untagged_temp(),
- mk_set(Src1, Tmp,
- mk_alu_ri(XAluOp, Tmp, Src2, Dst)).
+ mk_set(Src1, Tmp, mk_alu_ri(XAluOp, Tmp, Src2, Dst)).
mk_alu_ir(XAluOp, Src1, Src2, Dst) ->
case xaluop_commutes(XAluOp) of
@@ -249,8 +248,7 @@ mk_alu_ir(XAluOp, Src1, Src2, Dst) ->
true};
_ ->
Tmp = new_untagged_temp(),
- {mk_set(Src1, Tmp,
- mk_alu_rs(XAluOp, Tmp, Src2, Dst)),
+ {mk_set(Src1, Tmp, mk_alu_rs(XAluOp, Tmp, Src2, Dst)),
false}
end.
@@ -274,8 +272,7 @@ mk_arith_ri(XAluOp, Src1, Src2, Dst) when is_integer(Src2) ->
mk_alu_rs(XAluOp, Src1, hipe_sparc:mk_simm13(Src2), Dst);
true ->
Tmp = new_untagged_temp(),
- mk_set(Src2, Tmp,
- mk_alu_rs(XAluOp, Src1, Tmp, Dst))
+ mk_set(Src2, Tmp, mk_alu_rs(XAluOp, Src1, Tmp, Dst))
end.
mk_alu_rs(XAluOp, Src1, Src2, Dst) ->
@@ -623,8 +620,7 @@ mk_move(Src, Dst, Tail) ->
conv_return(I, Map, Data) ->
%% TODO: multiple-value returns
{[Arg], Map0} = conv_src_list(hipe_rtl:return_varlist(I), Map),
- I2 = mk_move(Arg, hipe_sparc:mk_rv(),
- [hipe_sparc:mk_pseudo_ret()]),
+ I2 = mk_move(Arg, hipe_sparc:mk_rv(), [hipe_sparc:mk_pseudo_ret()]),
{I2, Map0, Data}.
conv_store(I, Map, Data) ->
@@ -648,8 +644,7 @@ mk_store(StOp, Src, Base1, Base2) ->
mk_store2(StOp, Src, Base1, Base2);
_ ->
Tmp = new_untagged_temp(),
- mk_set(Src, Tmp,
- mk_store2(StOp, Tmp, Base1, Base2))
+ mk_set(Src, Tmp, mk_store2(StOp, Tmp, Base1, Base2))
end.
mk_store2(StOp, Src, Base1, Base2) ->
@@ -674,8 +669,7 @@ conv_switch(I, Map, Data) ->
[] ->
hipe_consttab:insert_block(Data, word, LMap);
SortOrder ->
- hipe_consttab:insert_sorted_block(
- Data, word, LMap, SortOrder)
+ hipe_consttab:insert_sorted_block(Data, word, LMap, SortOrder)
end,
%% no immediates allowed here
{IndexR, Map1} = conv_dst(hipe_rtl:switch_src(I), Map),
@@ -722,7 +716,7 @@ conv_aluop(RtlAluOp) ->
xaluop_commutes(XAluOp) ->
case XAluOp of
- 'cmp' -> true;
+ %% 'cmp' -> true;
'cmpcc' -> true;
'add' -> true;
'addcc' -> true;
@@ -739,16 +733,16 @@ xaluop_commutes(XAluOp) ->
'sll' -> false;
'srl' -> false;
'sra' -> false;
- 'sllx' -> false;
- 'srlx' -> false;
- 'srax' -> false;
+ %% 'sllx' -> false;
+ %% 'srlx' -> false;
+ %% 'srax' -> false;
'ldsb' -> true;
'ldsh' -> true;
- 'ldsw' -> true;
+ %% 'ldsw' -> true;
'ldub' -> true;
'lduh' -> true;
- 'lduw' -> true;
- 'ldx' -> true
+ 'lduw' -> true
+ %% 'ldx' -> true
end.
%%% Check if an extended SPARC AluOp is a shift.
diff --git a/lib/hipe/tools/Makefile b/lib/hipe/tools/Makefile
index 6761f11b01..3ce8ad5dd7 100644
--- a/lib/hipe/tools/Makefile
+++ b/lib/hipe/tools/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2012. 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/lib/hipe/vsn.mk b/lib/hipe/vsn.mk
index e296997466..144167f5d1 100644
--- a/lib/hipe/vsn.mk
+++ b/lib/hipe/vsn.mk
@@ -1 +1 @@
-HIPE_VSN = 3.9.1
+HIPE_VSN = 3.9.2
diff --git a/lib/ic/c_src/Makefile.in b/lib/ic/c_src/Makefile.in
index d8b8c85a8e..de46eadb3b 100644
--- a/lib/ic/c_src/Makefile.in
+++ b/lib/ic/c_src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2011. All Rights Reserved.
+# Copyright Ericsson AB 1998-2012. 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/lib/ic/doc/src/Makefile b/lib/ic/doc/src/Makefile
index 0ee242b234..af76cdeaaa 100644
--- a/lib/ic/doc/src/Makefile
+++ b/lib/ic/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2011. All Rights Reserved.
+# Copyright Ericsson AB 1998-2012. 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/lib/ic/doc/src/ic_clib.xml b/lib/ic/doc/src/ic_clib.xml
index b557c4b5f6..ebeaabae91 100644
--- a/lib/ic/doc/src/ic_clib.xml
+++ b/lib/ic/doc/src/ic_clib.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -41,7 +41,7 @@
</section>
<funcs>
<func>
- <name><ret>CORBA_Environment*</ret><nametext>CORBA_Environment_alloc(int inbufsz, int outbufsz)</nametext></name>
+ <name><ret>CORBA_Environment *</ret><nametext>CORBA_Environment_alloc(int inbufsz, int outbufsz)</nametext></name>
<fsummary>Allocate environment data.</fsummary>
<desc>
<p>This function is used to allocate and initiate the
@@ -79,14 +79,14 @@
</desc>
</func>
<func>
- <name><ret>CORBA_char*</ret><nametext>CORBA_string_alloc(CORBA_unsigned_long len)</nametext></name>
+ <name><ret>CORBA_char *</ret><nametext>CORBA_string_alloc(CORBA_unsigned_long len)</nametext></name>
<fsummary>Allocate a string.</fsummary>
<desc>
<p>Allocates a (simple) CORBA character string of length <c>len + 1</c>.</p>
</desc>
</func>
<func>
- <name><ret>CORBA_wchar*</ret><nametext>CORBA_wstring_alloc(CORBA_unsigned_long len)</nametext></name>
+ <name><ret>CORBA_wchar *</ret><nametext>CORBA_wstring_alloc(CORBA_unsigned_long len)</nametext></name>
<fsummary>Allocate a wide string.</fsummary>
<desc>
<p>Allocates a CORBA wide string of length <c>len + 1</c>.</p>
@@ -101,7 +101,7 @@
</section>
<funcs>
<func>
- <name><ret>CORBA_char*</ret><nametext>CORBA_exception_id(CORBA_Environment *env)</nametext></name>
+ <name><ret>CORBA_char *</ret><nametext>CORBA_exception_id(CORBA_Environment *env)</nametext></name>
<fsummary>Get exception identity.</fsummary>
<desc>
<p>Returns the exception identity if an exception is set, otherwise
@@ -109,7 +109,7 @@
</desc>
</func>
<func>
- <name><ret>void*</ret><nametext>CORBA_exception_value(CORBA_Environment *env)</nametext></name>
+ <name><ret>void *</ret><nametext>CORBA_exception_value(CORBA_Environment *env)</nametext></name>
<fsummary>Get exception value.</fsummary>
<desc>
<p>Returns the exception value, if an exception is set, otherwise
@@ -160,7 +160,7 @@
</desc>
</func>
<func>
- <name><ret>oe_map_t*</ret><nametext>oe_merge_maps(oe_map_t *maps, int size)</nametext></name>
+ <name><ret>oe_map_t *</ret><nametext>oe_merge_maps(oe_map_t *maps, int size)</nametext></name>
<fsummary>Merge an array of server maps to one single map.</fsummary>
<desc>
<p>Merge an array of server maps to one single map.</p>
diff --git a/lib/ic/doc/src/notes.xml b/lib/ic/doc/src/notes.xml
index eac932fc06..3ac35aa0e5 100644
--- a/lib/ic/doc/src/notes.xml
+++ b/lib/ic/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1998</year><year>2011</year>
+ <year>1998</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,7 +30,29 @@
<file>notes.xml</file>
</header>
- <section><title>IC 4.2.30</title>
+ <section><title>IC 4.2.31</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bug where the ic pre-processor would ignore
+ whitespace quoting.</p>
+ <p>
+ Own Id: OTP-10109</p>
+ </item>
+ <item>
+ <p> A bug regarding spaces in C function prototypes has
+ been fixed. (Thanks to Richard O'Keefe.) </p>
+ <p>
+ Own Id: OTP-10138</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>IC 4.2.30</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/ic/examples/pre_post_condition/Makefile b/lib/ic/examples/pre_post_condition/Makefile
index 8ae37394ed..d18f81fec9 100644
--- a/lib/ic/examples/pre_post_condition/Makefile
+++ b/lib/ic/examples/pre_post_condition/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/ic/java_src/com/ericsson/otp/ic/Makefile b/lib/ic/java_src/com/ericsson/otp/ic/Makefile
index 1b14d4430f..cf4c353f3f 100644
--- a/lib/ic/java_src/com/ericsson/otp/ic/Makefile
+++ b/lib/ic/java_src/com/ericsson/otp/ic/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/ic/src/Makefile b/lib/ic/src/Makefile
index c830ebec95..280d86a8a4 100644
--- a/lib/ic/src/Makefile
+++ b/lib/ic/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+# Copyright Ericsson AB 1998-2012. 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/lib/ic/src/ic.erl b/lib/ic/src/ic.erl
index bc7ec8ba91..c0742cf7bd 100644
--- a/lib/ic/src/ic.erl
+++ b/lib/ic/src/ic.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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/lib/ic/src/ic_pp.erl b/lib/ic/src/ic_pp.erl
index 50eeeab48c..54701f7438 100644
--- a/lib/ic/src/ic_pp.erl
+++ b/lib/ic/src/ic_pp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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/lib/ic/test/Makefile b/lib/ic/test/Makefile
index 65003d703b..54ac186c16 100644
--- a/lib/ic/test/Makefile
+++ b/lib/ic/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2011. All Rights Reserved.
+# Copyright Ericsson AB 1998-2012. 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/lib/ic/test/java_client_erl_server_SUITE.erl b/lib/ic/test/java_client_erl_server_SUITE.erl
index d4523dc2ad..c2bec94697 100644
--- a/lib/ic/test/java_client_erl_server_SUITE.erl
+++ b/lib/ic/test/java_client_erl_server_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. 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/lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src b/lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src
index c106261efd..ac8f2e619f 100644
--- a/lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src
+++ b/lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src
@@ -66,8 +66,11 @@ CLASS_FILES = $(JAVA_FILES:.java=.class)
ERL_FILES = $(GEN_ERL_FILES) m_i_impl.erl
EBINS = $(ERL_FILES:.erl=.@EMULATOR@)
-
+@IFEQ@ (@jinterface_classpath@,)
+all:
+@ELSE
all: $(CLASS_FILES) $(EBINS)
+@ENDIF@
$(GEN_ERL_FILES) $(GEN_HRL_FILES): java_erl_test.built_erl
$(GEN_JAVA_FILES): java_erl_test.built_java
diff --git a/lib/ic/vsn.mk b/lib/ic/vsn.mk
index fc1475974e..0dbf5a1f52 100644
--- a/lib/ic/vsn.mk
+++ b/lib/ic/vsn.mk
@@ -1 +1 @@
-IC_VSN = 4.2.30
+IC_VSN = 4.2.31
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index 70c845bade..14ce3cbe7f 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -480,66 +480,69 @@ apply(Module, Function, [ReplyInfo | Args])
<d>ex: "134.138" or "[FEDC:BA98" (all IP-addresses starting with 134.138 or FEDC:BA98), "66.35.250.150" or "[2010:836B:4179::836B:4179]" (a complete IP-address).</d>
<v>MaxSessions = integer() </v>
<d>Default is <c>2</c>.
- Maximum number of persistent connections to a host.</d>
+ Maximum number of persistent connections to a host.</d>
<v>MaxKeepAlive = integer() </v>
- <d>Default is <c>5</c>.
- Maximum number of outstanding requests on the same connection to
- a host.</d>
- <v>KeepAliveTimeout = integer() </v>
- <d>Default is <c>120000</c> (= 2 min).
- If a persistent connection is idle longer than the
- <c>keep_alive_timeout</c> the client will close the connection.
- The server may also have such a time out but you should
- not count on it!</d>
+ <d>Default is <c>5</c>.
+ Maximum number of outstanding requests on the same connection to
+ a host.</d>
+ <v>KeepAliveTimeout = integer() </v>
+ <d>Default is <c>120000</c> (= 2 min).
+ If a persistent connection is idle longer than the
+ <c>keep_alive_timeout</c> in milliseconds,
+ the client will close the connection.
+ The server may also have such a time out but you should
+ not count on it!</d>
<v>MaxPipeline = integer() </v>
- <d>Default is <c>2</c>.
- Maximum number of outstanding requests on a pipelined connection to a host.</d>
- <v>PipelineTimeout = integer() </v>
- <d>Default is <c>0</c>,
- which will result in pipelining not being used.
- If a persistent connection is idle longer than the
- <c>pipeline_timeout</c> the client will close the connection. </d>
+ <d>Default is <c>2</c>.
+ Maximum number of outstanding requests on a pipelined connection
+ to a host.</d>
+ <v>PipelineTimeout = integer() </v>
+ <d>Default is <c>0</c>,
+ which will result in pipelining not being used.
+ If a persistent connection is idle longer than the
+ <c>pipeline_timeout</c> in milliseconds,
+ the client will close the connection. </d>
<v>CookieMode = enabled | disabled | verify </v>
<d>Default is <c>disabled</c>.
- If Cookies are enabled all valid cookies will automatically be
- saved in the client manager's cookie database.
- If the option <c>verify</c> is used the function <c>store_cookies/2</c>
- has to be called for the cookies to be saved.</d>
- <v>IpFamily = inet | inet6 | inet6fb4 </v>
- <d>By default <c>inet</c>.
- When it is set to <c>inet6fb4</c> you can use both ipv4 and ipv6.
- It first tries <c>inet6</c> and if that does not works falls back to <c>inet</c>.
- The option is here to provide a workaround for buggy ipv6 stacks to ensure that
- ipv4 will always work.</d>
+ If Cookies are enabled all valid cookies will automatically be
+ saved in the client manager's cookie database.
+ If the option <c>verify</c> is used the function <c>store_cookies/2</c>
+ has to be called for the cookies to be saved.</d>
+ <v>IpFamily = inet | inet6 | inet6fb4 </v>
+ <d>By default <c>inet</c>.
+ When it is set to <c>inet6fb4</c> you can use both ipv4 and ipv6.
+ It first tries <c>inet6</c> and if that does not works falls back to <c>inet</c>.
+ The option is here to provide a workaround for buggy ipv6 stacks to ensure that
+ ipv4 will always work.</d>
<v>IpAddress = ip_address() </v>
- <d>If the host has several network interfaces, this option specifies which one to use.
- See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d>
+ <d>If the host has several network interfaces, this option specifies which one to use.
+ See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d>
<v>Port = integer() </v>
- <d>Specify which local port number to use.
- See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d>
- <v>VerboseMode = false | verbose | debug | trace </v>
- <d>Default is <c>false</c>.
- This option is used to switch on (or off)
- different levels of erlang trace on the client.
- It is a debug feature.</d>
+ <d>Specify which local port number to use.
+ See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d>
+ <v>VerboseMode = false | verbose | debug | trace </v>
+ <d>Default is <c>false</c>.
+ This option is used to switch on (or off)
+ different levels of erlang trace on the client.
+ It is a debug feature.</d>
<v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
</type>
<desc>
- <p>Sets options to be used for subsequent requests.</p>
- <note>
- <p>If possible the client will keep its connections
- alive and use persistent connections
- with or without pipeline depending on configuration
- and current circumstances. The HTTP/1.1 specification does not
- provide a guideline for how many requests would be
- ideal to be sent on a persistent connection,
- this very much depends on the
- application. Note that a very long queue of requests may cause a
- user perceived delay as earlier requests may take a long time
- to complete. The HTTP/1.1 specification does suggest a
- limit of 2 persistent connections per server, which is the
- default value of the <c>max_sessions</c> option. </p>
- </note>
+ <p>Sets options to be used for subsequent requests.</p>
+ <note>
+ <p>If possible the client will keep its connections
+ alive and use persistent connections
+ with or without pipeline depending on configuration
+ and current circumstances. The HTTP/1.1 specification does not
+ provide a guideline for how many requests would be
+ ideal to be sent on a persistent connection,
+ this very much depends on the
+ application. Note that a very long queue of requests may cause a
+ user perceived delay as earlier requests may take a long time
+ to complete. The HTTP/1.1 specification does suggest a
+ limit of 2 persistent connections per server, which is the
+ default value of the <c>max_sessions</c> option. </p>
+ </note>
<marker id="get_options"></marker>
</desc>
@@ -648,6 +651,8 @@ apply(Module, Function, [ReplyInfo | Args])
<p>Resets (clears) the cookie database for the specified
<c>Profile</c>. If no profile is specified the default profile
will be used. </p>
+
+ <marker id="which_cookies"></marker>
</desc>
</func>
@@ -667,6 +672,42 @@ apply(Module, Function, [ReplyInfo | Args])
<p>This function produces a list of the entire cookie database.
It is intended for debugging/testing purposes.
If no profile is specified the default profile will be used. </p>
+
+ <marker id="which_sessions"></marker>
+ </desc>
+ </func>
+
+ <func>
+ <name>which_sessions() -> session_info()</name>
+ <name>which_sessions(Profile) -> session_info()</name>
+ <fsummary>Produces a slightly processed dump of the sessions database.</fsummary>
+ <type>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
+ <v>session_info() = {GoodSessions, BadSessions, NonSessions}</v>
+ <v>GoodSessions = session()</v>
+ <v>BadSessions = tuple()</v>
+ <v>NonSessions = term()</v>
+ </type>
+ <desc>
+ <p>This function produces a slightly processed dump of the session
+ database. It is intended for debugging.
+ If no profile is specified the default profile will be used. </p>
+
+ <marker id="info"></marker>
+ </desc>
+ </func>
+
+ <func>
+ <name>info() -> list()</name>
+ <name>info(Profile) -> list()</name>
+ <fsummary>Produces a list of miscelleneous info</fsummary>
+ <type>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
+ </type>
+ <desc>
+ <p>This function produces a list of miscelleneous info.
+ It is intended for debugging.
+ If no profile is specified the default profile will be used. </p>
</desc>
</func>
</funcs>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index dfdeb4016c..3aae1ff70a 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>Inets Release Notes</title>
@@ -32,16 +32,93 @@
<file>notes.xml</file>
</header>
+
+ <section>
+ <title>Inets 5.9.1</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <!--
+ <p>-</p>
+ -->
+
+ <list>
+ <item>
+ <p>Better handling of errorI(s) during update of the session
+ database. </p>
+ <p>Also added and updated some debugging functions
+ <seealso marker="httpc#which_sessions">which_sessions/10,1</seealso>
+ and
+ <seealso marker="httpc#info">info/0</seealso>. </p>
+ <p>Own Id: OTP-10093</p>
+ <p>Aux Id: Seq 12062</p>
+ </item>
- <section><title>Inets 5.9</title>
-
- <section><title>Improvements and New Features</title>
-<!--
- <p>-</p>
--->
+ <item>
+ <p>Removed R14B compatible version of (inets-service and
+ tftp) behaviour definition. </p>
+ <p>Own Id: OTP-10095</p>
+ </item>
+
+ <item>
+ <p>[httpc] Documentation of KeepAlive and Pipeline timeout
+ options have been improved. </p>
+ <p>Own Id: OTP-10114</p>
+ </item>
+ </list>
+
+ </section>
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <!--
+ <p>-</p>
+ -->
+
<list>
<item>
+ <p>[httpc] Cancel request does not work due to incorrect
+ handler table creation (wrong keypos). </p>
+ <p>Vyacheslav Vorobyov</p>
+ <p>Own Id: OTP-10092</p>
+ </item>
+
+ </list>
+
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+
+ <!--
+ <list>
+ <item>
+ <p>[httpc|httpd] The old ssl implementation (based on OpenSSL),
+ has been deprecated. The config option that specified usage of
+ this version of the ssl app, <c>ossl</c>, has been removed. </p>
+ <p>Own Id: OTP-9522</p>
+ </item>
+
+ </list>
+ -->
+
+ </section>
+
+ </section> <!-- 5.9.1 -->
+
+
+ <section>
+ <title>Inets 5.9</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <!--
+ <p>-</p>
+ -->
+
+ <list>
+ <item>
<p>[httpd] Make the server header configurable with new config
option
<seealso marker="httpd#prop_server_tokens">server_tokens</seealso>.
@@ -102,11 +179,11 @@
</section>
+<!--
<section>
<title>Incompatibilities</title>
<p>-</p>
-<!--
<list>
<item>
<p>[httpc|httpd] The old ssl implementation (based on OpenSSL),
@@ -116,9 +193,9 @@
</item>
</list>
--->
</section>
+-->
</section> <!-- 5.9 -->
@@ -285,31 +362,6 @@
</section>
- <section>
- <title>Incompatibilities</title>
-<!--
- <p>-</p>
--->
-
- <list>
- <item>
- <p>[httpc] Deprecated interface module <c>http</c> has been removed.
- It has (long) been replaced by http client interface module
- <seealso marker="httpc#">httpc</seealso>. </p>
- <p>Own Id: OTP-9359</p>
- </item>
-
- <item>
- <p>[httpc|httpd] The old ssl implementation (based on OpenSSL),
- has been deprecated. The config option that specified usage of
- this version of the ssl app, <c>ossl</c>, has been removed. </p>
- <p>Own Id: OTP-9522</p>
- </item>
-
- </list>
-
- </section>
-
<section><title>Fixed Bugs and Malfunctions</title>
<!--
<p>-</p>
@@ -332,6 +384,24 @@
</list>
</section>
+<!--
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+
+ <list>
+ <item>
+ <p>[httpc] Deprecated interface module <c>http</c> has been removed.
+ It has (long) been replaced by http client interface module
+ <seealso marker="httpc#">httpc</seealso>. </p>
+ <p>Own Id: OTP-9359</p>
+ </item>
+
+ </list>
+
+ </section>
+-->
+
</section> <!-- 5.7.2 -->
diff --git a/lib/inets/examples/httpd_load_test/Makefile b/lib/inets/examples/httpd_load_test/Makefile
index de1b2a289f..abd2d8c35d 100644
--- a/lib/inets/examples/httpd_load_test/Makefile
+++ b/lib/inets/examples/httpd_load_test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. 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/lib/inets/examples/server_root/Makefile b/lib/inets/examples/server_root/Makefile
index 2230a7eafc..7a0ee78ecf 100644
--- a/lib/inets/examples/server_root/Makefile
+++ b/lib/inets/examples/server_root/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/inets/priv/Makefile b/lib/inets/priv/Makefile
index 4dc521f618..acca20f076 100644
--- a/lib/inets/priv/Makefile
+++ b/lib/inets/priv/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/inets/src/ftp/Makefile b/lib/inets/src/ftp/Makefile
index 426286f254..2c3d2b6d13 100644
--- a/lib/inets/src/ftp/Makefile
+++ b/lib/inets/src/ftp/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2010. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. 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/lib/inets/src/http_client/Makefile b/lib/inets/src/http_client/Makefile
index 850c790bd1..f0d4ce139e 100644
--- a/lib/inets/src/http_client/Makefile
+++ b/lib/inets/src/http_client/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2011. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. 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/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl
index f4802fb96d..b6e7708353 100644
--- a/lib/inets/src/http_client/httpc.erl
+++ b/lib/inets/src/http_client/httpc.erl
@@ -39,6 +39,7 @@
cookie_header/1, cookie_header/2, cookie_header/3,
which_cookies/0, which_cookies/1,
reset_cookies/0, reset_cookies/1,
+ which_sessions/0, which_sessions/1,
stream_next/1,
default_profile/0,
profile_name/1, profile_name/2,
@@ -267,6 +268,7 @@ set_option(Key, Value, Profile) ->
%% Reason - term()
%% Description: Retrieves the current options.
%%-------------------------------------------------------------------------
+
get_options() ->
record_info(fields, options).
@@ -373,8 +375,6 @@ cookie_header(Url, Opts, Profile)
{error, {not_started, Profile}}
end.
-
-
%%--------------------------------------------------------------------------
%% which_cookies() -> [cookie()]
@@ -398,6 +398,28 @@ which_cookies(Profile) ->
%%--------------------------------------------------------------------------
+%% which_sessions() -> {GoodSession, BadSessions, NonSessions}
+%% which_sessions(Profile) -> {GoodSession, BadSessions, NonSessions}
+%%
+%% Description: Debug function, dumping the sessions database, sorted
+%% into three groups (Good-, Bad- and Non-sessions).
+%%-------------------------------------------------------------------------
+which_sessions() ->
+ which_sessions(default_profile()).
+
+which_sessions(Profile) ->
+ ?hcrt("which sessions", [{profile, Profile}]),
+ try
+ begin
+ httpc_manager:which_sessions(profile_name(Profile))
+ end
+ catch
+ exit:{noproc, _} ->
+ {[], [], []}
+ end.
+
+
+%%--------------------------------------------------------------------------
%% info() -> list()
%% info(Profile) -> list()
%%
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index b8c34bd99b..923213d34d 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -55,7 +55,7 @@
status_line, % {Version, StatusCode, ReasonPharse}
headers, % #http_response_h{}
body, % binary()
- mfa, % {Moduel, Function, Args}
+ mfa, % {Module, Function, Args}
pipeline = queue:new(), % queue()
keep_alive = queue:new(), % queue()
status, % undefined | new | pipeline | keep_alive | close | ssl_tunnel
@@ -1713,7 +1713,32 @@ update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) ->
catch
error:undef -> % This could happen during code upgrade
Session2 = erlang:setelement(Pos, Session, Value),
- insert_session(Session2, ProfileName)
+ insert_session(Session2, ProfileName);
+ T:E ->
+ error_logger:error_msg("Failed updating session: "
+ "~n ProfileName: ~p"
+ "~n SessionId: ~p"
+ "~n Pos: ~p"
+ "~n Value: ~p"
+ "~nwhen"
+ "~n Session (db) info: ~p"
+ "~n Session (db): ~p"
+ "~n Session (record): ~p"
+ "~n T: ~p"
+ "~n E: ~p",
+ [ProfileName, SessionId, Pos, Value,
+ (catch httpc_manager:which_session_info(ProfileName)),
+ Session,
+ (catch httpc_manager:lookup_session(ProfileName, SessionId)),
+ T, E]),
+ exit({failed_updating_session,
+ [{profile, ProfileName},
+ {session_id, SessionId},
+ {pos, Pos},
+ {value, Value},
+ {etype, T},
+ {error, E},
+ {stacktrace, erlang:get_stacktrace()}]})
end.
diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl
index b225b43214..3612b331e7 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -34,8 +34,11 @@
retry_request/2,
redirect_request/2,
insert_session/2,
+ lookup_session/2,
update_session/4,
delete_session/2,
+ which_sessions/1,
+ which_session_info/1,
set_options/2,
get_options/2,
store_cookies/3,
@@ -59,17 +62,9 @@
options = #options{}
}).
--record(handler_info,
- {
- id, % Id of the request: request_id()
- starter, % Pid of the handler starter process (temp): pid()
- handler, % Pid of the handler process: pid()
- from, % From for the request: from()
- state % State of the handler: initiating | started | operational | canceled
- }).
-
-define(DELAY, 500).
+
%%====================================================================
%% Internal Application API
%%====================================================================
@@ -195,13 +190,28 @@ insert_session(Session, ProfileName) ->
%%--------------------------------------------------------------------
+%% Function: lookup_session(SessionId, ProfileName) -> _
+%% SessionId - term()
+%% ProfileName - atom()
+%%
+%% Description: Looks up a session record in the httpc manager
+%% table <ProfileName>__session_db.
+%%--------------------------------------------------------------------
+
+lookup_session(SessionId, ProfileName) ->
+ SessionDbName = session_db_name(ProfileName),
+ ?hcrt("lookup session", [{session_id, SessionId}, {profile, ProfileName}]),
+ ets:lookup(SessionDbName, SessionId).
+
+
+%%--------------------------------------------------------------------
%% Function: update_session(ProfileName, SessionId, Pos, Value) -> _
%% Session - #session{}
%% ProfileName - atom()
%%
%% Description: Update, only one field (Pos) of the session record
%% identified by the SessionId, the session information
-%% of the httpc manager table <ProfileName>_session_db.
+%% of the httpc manager table <ProfileName>__session_db.
%% Intended to be called by the httpc request handler process.
%%--------------------------------------------------------------------
@@ -216,12 +226,12 @@ update_session(ProfileName, SessionId, Pos, Value) ->
%%--------------------------------------------------------------------
-%% Function: delete_session(SessionId, ProfileName) -> _
+%% Function: delete_session(SessionId, ProfileName) -> void()
%% SessionId - {{Host, Port}, HandlerPid}
%% ProfileName - atom()
%%
%% Description: Deletes session information from the httpc manager
-%% table httpc_manager_session_db_<Profile>. Intended to be called by
+%% table <ProfileName>__session_db. Intended to be called by
%% the httpc request handler process.
%%--------------------------------------------------------------------
@@ -232,6 +242,57 @@ delete_session(SessionId, ProfileName) ->
%%--------------------------------------------------------------------
+%% Function: which sessions(ProfileName) -> SessionsInfo
+%% ProfileName - atom()
+%% SessionsInfo - {GoodSessions, BadSessions, NonSessions}
+%% GoodSessions - [#session{}]
+%% BadSessions - [tuple()]
+%% NonSessions - [term()]
+%%
+%% Description: Produces a list of all sessions in the session db.
+%% Used for debugging and since that is the intent, there is some
+%% checking and transforming done, which produces the results.
+%%--------------------------------------------------------------------
+
+which_sessions(ProfileName) ->
+ ?hcrt("which_sessions", [{profile, ProfileName}]),
+ SessionDbName = session_db_name(ProfileName),
+ which_sessions2(SessionDbName).
+
+which_sessions2(SessionDbName) ->
+ Sessions = which_sessions_order(ets:tab2list(SessionDbName)),
+ GoodSessions = [GoodSession || {good_session, GoodSession} <- Sessions],
+ BadSessions = [BadSession || {bad_session, BadSession} <- Sessions],
+ NonSessions = [NonSession || {non_session, NonSession} <- Sessions],
+ {lists:keysort(#session.id, GoodSessions),
+ lists:keysort(#session.id, BadSessions),
+ lists:sort(NonSessions)}.
+
+which_sessions_order([]) ->
+ [];
+which_sessions_order([Session|Sessions]) when is_record(Session, session) ->
+ [{good_session, Session} | which_sessions_order(Sessions)];
+which_sessions_order([BadSession|Sessions])
+ when is_tuple(BadSession) andalso
+ (element(1, BadSession) =:= session) ->
+ [{bad_session, BadSession} | which_sessions_order(Sessions)];
+which_sessions_order([NonSession|Sessions]) ->
+ [{non_session, NonSession} | which_sessions_order(Sessions)].
+
+
+%%--------------------------------------------------------------------
+%% Function: which session_info(ProfileName) -> list()
+%%
+%% Description: Produces a ets table info list of the sessions table
+%%--------------------------------------------------------------------
+
+which_session_info(ProfileName) ->
+ SessionDbName = session_db_name(ProfileName),
+ ?hcrt("which_session_info", [{profile, ProfileName}]),
+ ets:info(SessionDbName).
+
+
+%%--------------------------------------------------------------------
%% Function: set_options(Options, ProfileName) -> ok
%%
%% Options = [Option]
@@ -379,8 +440,7 @@ do_init(ProfileName, CookiesDir) ->
%% Create handler db
?hcrt("create handler/request db", []),
HandlerDbName = handler_db_name(ProfileName),
- ets:new(HandlerDbName,
- [protected, set, named_table, {keypos, #handler_info.id}]),
+ ets:new(HandlerDbName, [protected, set, named_table, {keypos, 1}]),
%% Cookie DB
?hcrt("create cookie db", []),
@@ -414,9 +474,10 @@ handle_call({request, Request}, _, State) ->
{stop, Error, httpc_response:error(Request, Error), State}
end;
-handle_call({cancel_request, RequestId}, From, State) ->
+handle_call({cancel_request, RequestId}, From,
+ #state{handler_db = HandlerDb} = State) ->
?hcri("cancel_request", [{request_id, RequestId}]),
- case ets:lookup(State#state.handler_db, RequestId) of
+ case ets:lookup(HandlerDb, RequestId) of
[] ->
%% The request has allready compleated make sure
%% it is deliverd to the client process queue so
@@ -428,9 +489,9 @@ handle_call({cancel_request, RequestId}, From, State) ->
{noreply, State};
[{_, Pid, _}] ->
httpc_handler:cancel(RequestId, Pid, From),
- {noreply, State#state{cancel =
- [{RequestId, Pid, From} |
- State#state.cancel]}}
+ {noreply,
+ State#state{cancel =
+ [{RequestId, Pid, From} | State#state.cancel]}}
end;
handle_call(reset_cookies, _, #state{cookie_db = CookieDb} = State) ->
@@ -457,8 +518,8 @@ handle_call({which_cookies, Url, Options}, _,
handle_call({get_options, OptionItems}, _, #state{options = Options} = State) ->
?hcrv("get options", [{option_items, OptionItems}]),
- Reply = [{OptionItem, get_option(OptionItem, Options)} || OptionItem <-
- OptionItems],
+ Reply = [{OptionItem, get_option(OptionItem, Options)} ||
+ OptionItem <- OptionItems],
{reply, Reply, State};
handle_call(info, _, State) ->
@@ -645,7 +706,7 @@ code_change(_,
code_change(_, State, _) ->
{ok, State}.
-%% This function is to catch everything that calls through the cracks...
+%% This function is used to catch everything that falls through the cracks...
update_session_table(SessionDB, Transform) ->
ets:safe_fixtable(SessionDB, true),
update_session_table(SessionDB, ets:first(SessionDB), Transform),
@@ -673,40 +734,56 @@ update_session_table(SessionDB, Key, Transform) ->
%%--------------------------------------------------------------------
get_manager_info(#state{handler_db = HDB,
- cookie_db = CDB} = _State) ->
+ session_db = SDB,
+ cookie_db = CDB,
+ options = Options} = _State) ->
HandlerInfo = get_handler_info(HDB),
+ SessionInfo = which_sessions2(SDB),
+ OptionsInfo =
+ [{Item, get_option(Item, Options)} ||
+ Item <- record_info(fields, options)],
CookieInfo = httpc_cookie:which_cookies(CDB),
- [{handlers, HandlerInfo}, {cookies, CookieInfo}].
+ [{handlers, HandlerInfo},
+ {sessions, SessionInfo},
+ {options, OptionsInfo},
+ {cookies, CookieInfo}].
+
+sort_handlers(Unsorted) ->
+ sort_handlers2(lists:keysort(1, Unsorted)).
+
+sort_handlers2([]) ->
+ [];
+sort_handlers2([{HandlerPid, RequestId}|L]) ->
+ {Handler, Rest} = sort_handlers2(HandlerPid, [RequestId], L),
+ [Handler | sort_handlers2(Rest)].
+
+sort_handlers2(HandlerPid, Reqs, []) ->
+ {{HandlerPid, lists:sort(Reqs)}, []};
+sort_handlers2(HandlerPid, Reqs, [{HandlerPid, ReqId}|Rest]) ->
+ sort_handlers2(HandlerPid, [ReqId|Reqs], Rest);
+sort_handlers2(HandlerPid1, Reqs, [{HandlerPid2, _}|_] = Rest)
+ when HandlerPid1 =/= HandlerPid2 ->
+ {{HandlerPid1, lists:sort(Reqs)}, Rest}.
get_handler_info(Tab) ->
- Pattern = #handler_info{handler = '$1',
- state = '$2',
- _ = '_'},
- Handlers1 = [{Pid, State} || [Pid, State] <- ets:match(Tab, Pattern)],
- F = fun({Pid, State} = Elem, Acc) when State =/= canceled ->
- case lists:keymember(Pid, 1, Acc) of
- true ->
- Acc;
- false ->
- [Elem | Acc]
- end;
- (_, Acc) ->
- Acc
- end,
- Handlers2 = lists:foldl(F, [], Handlers1),
- Handlers3 = [{Pid, State,
- case (catch httpc_handler:info(Pid)) of
- {'EXIT', _} ->
+ Pattern = {'$2', '$1', '_'},
+ Handlers1 = [{Pid, Id} || [Pid, Id] <- ets:match(Tab, Pattern)],
+ Handlers2 = sort_handlers(Handlers1),
+ Handlers3 = [{Pid, Reqs,
+ try
+ begin
+ httpc_handler:info(Pid)
+ end
+ catch
+ _:_ ->
%% Why would this crash?
%% Only if the process has died, but we don't
%% know about it?
- [];
- Else ->
- Else
- end} ||
- {Pid, State} <- Handlers2],
+ []
+ end} || {Pid, Reqs} <- Handlers2],
Handlers3.
+
handle_request(#request{settings =
#http_options{version = "HTTP/0.9"}} = Request,
State) ->
@@ -758,19 +835,21 @@ handle_request(Request, State = #state{options = Options}) ->
{reply, {ok, NewRequest#request.id}, State}.
-start_handler(Request, State) ->
+start_handler(#request{id = Id,
+ from = From} = Request,
+ #state{profile_name = ProfileName,
+ handler_db = HandlerDb,
+ options = Options}) ->
{ok, Pid} =
case is_inets_manager() of
true ->
httpc_handler_sup:start_child([whereis(httpc_handler_sup),
- Request, State#state.options,
- State#state.profile_name]);
+ Request, Options, ProfileName]);
false ->
- httpc_handler:start_link(self(), Request, State#state.options,
- State#state.profile_name)
+ httpc_handler:start_link(self(), Request, Options, ProfileName)
end,
- ets:insert(State#state.handler_db, {Request#request.id,
- Pid, Request#request.from}),
+ HandlerInfo = {Id, Pid, From},
+ ets:insert(HandlerDb, HandlerInfo),
erlang:monitor(process, Pid).
@@ -827,12 +906,14 @@ select_session(Candidates, Max) ->
{ok, HandlerPid}
end.
-pipeline_or_keep_alive(Request, HandlerPid, State) ->
+pipeline_or_keep_alive(#request{id = Id,
+ from = From} = Request,
+ HandlerPid,
+ #state{handler_db = HandlerDb} = State) ->
case (catch httpc_handler:send(Request, HandlerPid)) of
ok ->
- ets:insert(State#state.handler_db, {Request#request.id,
- HandlerPid,
- Request#request.from});
+ HandlerInfo = {Id, HandlerPid, From},
+ ets:insert(HandlerDb, HandlerInfo);
_ -> % timeout pipelining failed
start_handler(Request, State)
end.
diff --git a/lib/inets/src/http_lib/Makefile b/lib/inets/src/http_lib/Makefile
index 3ec6ce7436..51167b34fa 100644
--- a/lib/inets/src/http_lib/Makefile
+++ b/lib/inets/src/http_lib/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2010. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. 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/lib/inets/src/http_server/httpd_log.erl b/lib/inets/src/http_server/httpd_log.erl
index 60ab326a20..a34435e0e8 100644
--- a/lib/inets/src/http_server/httpd_log.erl
+++ b/lib/inets/src/http_server/httpd_log.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. 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
@@ -36,8 +36,8 @@
AuthUser :: string(),
Date :: string(),
StatusCode :: pos_integer(),
- Size :: pos_integer() | string()) ->
- {Log :: atom() | pid(), Entry :: string()}.
+ Size :: 0 | pos_integer() | string()) ->
+ {Log :: atom() | pid(), Entry :: string()} | term() .
access_entry(Log, NoLog, Info, RFC931, AuthUser, Date, StatusCode, SizeStr)
when is_list(SizeStr) ->
@@ -69,7 +69,7 @@ access_entry(Log, NoLog,
Info :: #mod{},
Date :: string(),
Reason :: term()) ->
- {Log :: atom() | pid(), Entry :: string()}.
+ {Log :: atom() | pid(), Entry :: string()} | term().
error_entry(Log, NoLog,
#mod{config_db = ConfigDB,
@@ -87,7 +87,7 @@ error_entry(Log, NoLog,
ConfigDB :: term(),
Date :: string(),
ErrroStr :: string()) ->
- {Log :: atom() | pid(), Entry :: string()}.
+ {Log :: atom() | pid(), Entry :: string()} | term().
error_report_entry(Log, NoLog, ConfigDb, Date, ErrorStr) ->
MakeEntry = fun() -> io_lib:format("[~s], ~s~n", [Date, ErrorStr]) end,
@@ -99,7 +99,7 @@ error_report_entry(Log, NoLog, ConfigDb, Date, ErrorStr) ->
ConfigDB :: term(),
Date :: string(),
Reason :: term()) ->
- {Log :: atom() | pid(), Entry :: string()}.
+ {Log :: atom() | pid(), Entry :: string()} | term().
security_entry(Log, NoLog, #mod{config_db = ConfigDB}, Date, Reason) ->
MakeEntry = fun() -> io_lib:format("[~s] ~s~n", [Date, Reason]) end,
diff --git a/lib/inets/src/inets_app/Makefile b/lib/inets/src/inets_app/Makefile
index b8d923edd8..7d68145287 100644
--- a/lib/inets/src/inets_app/Makefile
+++ b/lib/inets/src/inets_app/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2011. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. 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/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index c7029f7b31..2adb2a0fc8 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -18,14 +18,25 @@
{"%VSN%",
[
+ {"5.9",
+ [
+ {load_module, tftp, soft_purge, soft_purge, [inets_service]},
+ {load_module, inets_service, soft_purge, soft_purge, []},
+ {load_module, httpc, soft_purge, soft_purge, [httpc_manager]},
+ {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]},
+ {update, httpc_manager, soft, soft_purge, soft_purge, []}
+ ]
+ },
{"5.8.1",
[
+ {load_module, tftp, soft_purge, soft_purge, [inets_service]},
+ {load_module, inets_service, soft_purge, soft_purge, []},
+
{load_module, http_uri, soft_purge, soft_purge, []},
{load_module, httpc_response, soft_purge, soft_purge, [http_uri]},
{load_module, httpc, soft_purge, soft_purge,
[http_uri, httpc_manager]},
- {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]},
{load_module, inets_app, soft_purge, soft_purge, [inets_sup]},
{update, inets_sup, soft, soft_purge, soft_purge, []},
@@ -35,36 +46,15 @@
{load_module, httpd_script_env, soft_purge, soft_purge, []},
{load_module, inets, soft_purge, soft_purge, [inets_trace]},
- {update, httpc_handler, soft, soft_purge, soft_purge, []},
+ {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]},
+ {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]},
{update, httpd_sup, soft, soft_purge, soft_purge, []},
{add_module, inets_trace}
]
},
- {"5.8",
+ {"5.8",
[
- {load_module, http_uri, soft_purge, soft_purge, []},
- {load_module, httpc_response, soft_purge, soft_purge, [http_uri]},
-
- {load_module, httpc, soft_purge, soft_purge,
- [http_uri, httpc_manager]},
-
- {load_module, inets_app, soft_purge, soft_purge, [inets_sup]},
- {update, inets_sup, soft, soft_purge, soft_purge, []},
-
- {load_module, inets, soft_purge, soft_purge, [inets_trace]},
-
- {load_module, httpd_conf, soft_purge, soft_purge, []},
- {load_module, httpd_response, soft_purge, soft_purge, []},
- {load_module, httpd_script_env, soft_purge, soft_purge, []},
-
- {load_module, ftp, soft_purge, soft_purge, []},
- {update, httpc_handler, {advanced, upgrade_from_pre_5_8_1},
- soft_purge, soft_purge, []},
- {update, httpc_manager, {advanced, upgrade_from_pre_5_8_1},
- soft_purge, soft_purge, [http_uri, httpc_handler]},
- {update, httpd_sup, soft, soft_purge, soft_purge, []},
-
- {add_module, inets_trace}
+ {restart_application, inets}
]
},
{"5.7.2",
@@ -74,14 +64,25 @@
}
],
[
+ {"5.9",
+ [
+ {load_module, tftp, soft_purge, soft_purge, [inets_service]},
+ {load_module, inets_service, soft_purge, soft_purge, []},
+ {load_module, httpc, soft_purge, soft_purge, [httpc_manager]},
+ {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]},
+ {update, httpc_manager, soft, soft_purge, soft_purge, []}
+ ]
+ },
{"5.8.1",
[
+ {load_module, tftp, soft_purge, soft_purge, [inets_service]},
+ {load_module, inets_service, soft_purge, soft_purge, []},
+
{load_module, http_uri, soft_purge, soft_purge, []},
{load_module, httpc_response, soft_purge, soft_purge, [http_uri]},
{load_module, httpc, soft_purge, soft_purge,
[http_uri, httpc_manager]},
- {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]},
{load_module, inets_app, soft_purge, soft_purge, [inets_sup]},
{update, inets_sup, soft, soft_purge, soft_purge, []},
@@ -91,36 +92,15 @@
{load_module, httpd_script_env, soft_purge, soft_purge, []},
{load_module, inets, soft_purge, soft_purge, []},
- {update, httpc_handler, soft, soft_purge, soft_purge, []},
+ {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]},
+ {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]},
{update, httpd_sup, soft, soft_purge, soft_purge, []},
{remove, {inets_trace, soft_purge, brutal_purge}}
]
},
{"5.8",
[
- {load_module, http_uri, soft_purge, soft_purge, []},
- {load_module, httpc_response, soft_purge, soft_purge, [http_uri]},
-
- {load_module, httpc, soft_purge, soft_purge,
- [http_uri, httpc_manager]},
-
- {load_module, inets_app, soft_purge, soft_purge, [inets_sup]},
- {update, inets_sup, soft, soft_purge, soft_purge, []},
-
- {load_module, inets, soft_purge, soft_purge, []},
-
- {load_module, httpd_conf, soft_purge, soft_purge, []},
- {load_module, httpd_response, soft_purge, soft_purge, []},
- {load_module, httpd_script_env, soft_purge, soft_purge, []},
-
- {load_module, ftp, soft_purge, soft_purge, []},
- {update, httpc_handler, {advanced, upgrade_from_pre_5_8_1},
- soft_purge, soft_purge, []},
- {update, httpc_manager, {advanced, upgrade_from_pre_5_8_1},
- soft_purge, soft_purge, [http_uri, httpc_handler]},
- {update, httpd_sup, soft, soft_purge, soft_purge, []},
-
- {remove, {inets_trace, soft_purge, brutal_purge}}
+ {restart_application, inets}
]
},
{"5.7.2",
diff --git a/lib/inets/src/inets_app/inets.mk b/lib/inets/src/inets_app/inets.mk
index d24cc0aea3..adef32dc19 100644
--- a/lib/inets/src/inets_app/inets.mk
+++ b/lib/inets/src/inets_app/inets.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2011. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. 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
@@ -33,10 +33,6 @@ ifeq ($(WARN_UNUSED_WARS), true)
ERL_COMPILE_FLAGS += +warn_unused_vars
endif
-ifeq ($(shell erl -noshell -eval 'io:format("~4s", [erlang:system_info(otp_release)])' -s init stop), R14B)
-INETS_ERL_COMPILE_FLAGS += -D'OTP-R14B-COMPILER'
-endif
-
INETS_APP_VSN_COMPILE_FLAGS = \
+'{parse_transform,sys_pre_attributes}' \
+'{attribute,insert,app_vsn,$(APP_VSN)}'
diff --git a/lib/inets/src/inets_app/inets_service.erl b/lib/inets/src/inets_app/inets_service.erl
index a057a51e2c..d17fdfe13e 100644
--- a/lib/inets/src/inets_app/inets_service.erl
+++ b/lib/inets/src/inets_app/inets_service.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -20,20 +20,6 @@
-module(inets_service).
--ifdef('OTP-R14B-COMPILER').
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [{start_standalone, 1},
- {start_service, 1},
- {stop_service, 1},
- {services, 0},
- {service_info, 1}];
-behaviour_info(_) ->
- undefined.
-
--else.
%% Starts service stand-alone
%% start_standalone(Config) -> % {ok, Pid} | {error, Reason}
@@ -83,4 +69,3 @@ behaviour_info(_) ->
-callback service_info(Service :: term()) ->
{ok, [{Property :: term(), Value :: term()}]} | {error, Reason :: term()}.
--endif.
diff --git a/lib/inets/src/tftp/Makefile b/lib/inets/src/tftp/Makefile
index a9df8d0319..b368b12462 100644
--- a/lib/inets/src/tftp/Makefile
+++ b/lib/inets/src/tftp/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2010. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. 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/lib/inets/src/tftp/tftp.erl b/lib/inets/src/tftp/tftp.erl
index 0d7ae1a89e..1621add246 100644
--- a/lib/inets/src/tftp/tftp.erl
+++ b/lib/inets/src/tftp/tftp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. 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
@@ -224,20 +224,6 @@
service_info/1
]).
--ifdef('OTP-R14B-COMPILER').
-
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [{prepare, 6},
- {open, 6},
- {read, 1},
- {write, 2},
- {abort, 3}];
-behaviour_info(_) ->
- undefined.
-
--else.
-type peer() :: {PeerType :: inet | inet6,
PeerHost :: inet:ip_address(),
@@ -280,8 +266,6 @@ behaviour_info(_) ->
-callback abort(Code :: error_code(), string(), State :: term()) -> 'ok'.
--endif.
-
-include("tftp.hrl").
diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile
index 7befa82272..0fc98eff6f 100644
--- a/lib/inets/test/Makefile
+++ b/lib/inets/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index a116edef77..1cdd96f0b0 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -761,116 +761,158 @@ http_inets_pipe(Config) when is_list(Config) ->
test_pipeline(URL) ->
- p("test_pipeline -> entry with"
- "~n URL: ~p", [URL]),
-
- httpc:set_options([{pipeline_timeout, 50000}]),
-
- p("test_pipeline -> issue (async) request 1"),
- {ok, RequestId1} =
+ p("test_pipeline -> entry with"
+ "~n URL: ~p", [URL]),
+
+ httpc:set_options([{pipeline_timeout, 50000}]),
+
+ p("test_pipeline -> issue (async) request 1"
+ "~n when profile info: ~p", [httpc:info()]),
+ {ok, RequestIdA1} =
httpc:request(get, {URL, []}, [], [{sync, false}]),
- test_server:format("RequestId1: ~p~n", [RequestId1]),
- p("test_pipeline -> RequestId1: ~p", [RequestId1]),
-
- %% Make sure pipeline is initiated
- p("test_pipeline -> sleep some", []),
- test_server:sleep(4000),
-
- p("test_pipeline -> issue (async) request 2"),
- {ok, RequestId2} =
+ tsp("RequestIdA1: ~p", [RequestIdA1]),
+ p("test_pipeline -> RequestIdA1: ~p"
+ "~n when profile info: ~p", [RequestIdA1, httpc:info()]),
+
+ %% Make sure pipeline is initiated
+ p("test_pipeline -> sleep some", []),
+ test_server:sleep(4000),
+
+ p("test_pipeline -> issue (async) request A2, A3 and A4"
+ "~n when profile info: ~p", [httpc:info()]),
+ {ok, RequestIdA2} =
httpc:request(get, {URL, []}, [], [{sync, false}]),
- tsp("RequestId2: ~p", [RequestId2]),
- p("test_pipeline -> RequestId2: ~p", [RequestId2]),
-
- p("test_pipeline -> issue (sync) request 3"),
- {ok, {{_,200,_}, [_ | _], [_ | _]}} =
+ {ok, RequestIdA3} =
+ httpc:request(get, {URL, []}, [], [{sync, false}]),
+ {ok, RequestIdA4} =
+ httpc:request(get, {URL, []}, [], [{sync, false}]),
+ tsp("RequestIdAs => A2: ~p, A3: ~p and A4: ~p",
+ [RequestIdA2, RequestIdA3, RequestIdA4]),
+ p("test_pipeline -> RequestIds => A2: ~p, A3: ~p and A4: ~p"
+ "~n when profile info: ~p",
+ [RequestIdA2, RequestIdA3, RequestIdA4, httpc:info()]),
+
+ p("test_pipeline -> issue (sync) request 3"),
+ {ok, {{_,200,_}, [_ | _], [_ | _]}} =
httpc:request(get, {URL, []}, [], []),
+
+ p("test_pipeline -> expect reply for (async) request A1, A2, A3 and A4"
+ "~n when profile info: ~p", [httpc:info()]),
+ pipeline_await_async_reply([{RequestIdA1, a1, 200},
+ {RequestIdA2, a2, 200},
+ {RequestIdA3, a3, 200},
+ {RequestIdA4, a4, 200}], ?MINS(1)),
- p("test_pipeline -> expect reply for (async) request 1 or 2"),
- receive
- {http, {RequestId1, {{_, 200, _}, _, _}}} ->
- p("test_pipeline -> received reply for (async) request 1 - now wait for 2"),
- receive
- {http, {RequestId2, {{_, 200, _}, _, _}}} ->
- p("test_pipeline -> received reply for (async) request 2"),
- ok;
- {http, Msg1} ->
- tsf(Msg1)
- end;
- {http, {RequestId2, {{_, 200, _}, _, _}}} ->
- io:format("test_pipeline -> received reply for (async) request 2 - now wait for 1"),
- receive
- {http, {RequestId1, {{_, 200, _}, _, _}}} ->
- io:format("test_pipeline -> received reply for (async) request 1"),
- ok;
- {http, Msg2} ->
- tsf(Msg2)
- end;
- {http, Msg3} ->
- tsf(Msg3)
- after 60000 ->
- receive Any1 ->
- tsp("received crap after timeout: ~n ~p", [Any1]),
- tsf({error, {timeout, Any1}})
- end
- end,
-
- p("test_pipeline -> sleep some"),
- test_server:sleep(4000),
-
- p("test_pipeline -> issue (async) request 4"),
- {ok, RequestId3} =
+ p("test_pipeline -> sleep some"
+ "~n when profile info: ~p", [httpc:info()]),
+ test_server:sleep(4000),
+
+ p("test_pipeline -> issue (async) request B1, B2, B3 and B4"
+ "~n when profile info: ~p", [httpc:info()]),
+ {ok, RequestIdB1} =
httpc:request(get, {URL, []}, [], [{sync, false}]),
- tsp("RequestId3: ~p", [RequestId3]),
- p("test_pipeline -> RequestId3: ~p", [RequestId3]),
-
- p("test_pipeline -> issue (async) request 5"),
- {ok, RequestId4} =
+ {ok, RequestIdB2} =
httpc:request(get, {URL, []}, [], [{sync, false}]),
- tsp("RequestId4: ~p~n", [RequestId4]),
- p("test_pipeline -> RequestId4: ~p", [RequestId4]),
-
- p("test_pipeline -> cancel (async) request 4"),
- ok = httpc:cancel_request(RequestId3),
-
- p("test_pipeline -> expect *no* reply for cancelled (async) request 4 (for 3 secs)"),
- receive
- {http, {RequestId3, _}} ->
- tsf(http_cancel_request_failed)
- after 3000 ->
- ok
- end,
-
- p("test_pipeline -> expect reply for (async) request 4"),
- Body =
- receive
- {http, {RequestId4, {{_, 200, _}, _, BinBody4}}} = Res ->
- p("test_pipeline -> received reply for (async) request 5"),
- tsp("Receive : ~p", [Res]),
- BinBody4;
- {http, Msg4} ->
- tsf(Msg4)
- after 60000 ->
- receive Any2 ->
- tsp("received crap after timeout: ~n ~p", [Any2]),
- tsf({error, {timeout, Any2}})
- end
- end,
+ {ok, RequestIdB3} =
+ httpc:request(get, {URL, []}, [], [{sync, false}]),
+ {ok, RequestIdB4} =
+ httpc:request(get, {URL, []}, [], [{sync, false}]),
+ tsp("RequestIdBs => B1: ~p, B2: ~p, B3: ~p and B4: ~p",
+ [RequestIdB1, RequestIdB2, RequestIdB3, RequestIdB4]),
+ p("test_pipeline -> RequestIdBs => B1: ~p, B2: ~p, B3: ~p and B4: ~p"
+ "~n when profile info: ~p",
+ [RequestIdB1, RequestIdB2, RequestIdB3, RequestIdB4, httpc:info()]),
+
+ p("test_pipeline -> cancel (async) request B2"
+ "~n when profile info: ~p", [httpc:info()]),
+ ok = httpc:cancel_request(RequestIdB2),
+
+ p("test_pipeline -> "
+ "expect *no* reply for cancelled (async) request B2 (for 3 secs)"
+ "~n when profile info: ~p", [httpc:info()]),
+ receive
+ {http, {RequestIdB2, _}} ->
+ tsf(http_cancel_request_failed)
+ after 3000 ->
+ ok
+ end,
+
+ p("test_pipeline -> expect reply for (async) request B1, B3 and B4"
+ "~n when profile info: ~p", [httpc:info()]),
+ Bodies = pipeline_await_async_reply([{RequestIdB1, b1, 200},
+ {RequestIdB3, b3, 200},
+ {RequestIdB4, b4, 200}], ?MINS(1)),
+ [{b1, Body}|_] = Bodies,
- p("test_pipeline -> check reply for (async) request 5"),
+ p("test_pipeline -> check reply for (async) request B1"
+ "~n when profile info: ~p", [httpc:info()]),
inets_test_lib:check_body(binary_to_list(Body)),
-
- p("test_pipeline -> ensure no unexpected incomming"),
+
+ p("test_pipeline -> ensure no unexpected incomming"
+ "~n when profile info: ~p", [httpc:info()]),
receive
{http, Any} ->
tsf({unexpected_message, Any})
after 500 ->
ok
end,
-
- p("test_pipeline -> done"),
+
+ p("test_pipeline -> done"
+ "~n when profile info: ~p", [httpc:info()]),
ok.
+pipeline_await_async_reply(ReqIds, Timeout) ->
+ pipeline_await_async_reply(ReqIds, Timeout, []).
+
+pipeline_await_async_reply([], _, Acc) ->
+ lists:keysort(1, Acc);
+pipeline_await_async_reply(ReqIds, Timeout, Acc) when Timeout > 0 ->
+ T1 = inets_test_lib:timestamp(),
+ p("pipeline_await_async_reply -> await replies"
+ "~n ReqIds: ~p"
+ "~n Timeout: ~p", [ReqIds, Timeout]),
+ receive
+ {http, {RequestId, {{_, Status, _}, _, Body}}} ->
+ p("pipeline_await_async_reply -> received reply for"
+ "~n RequestId: ~p"
+ "~n Status: ~p", [RequestId, Status]),
+ case lists:keysearch(RequestId, 1, ReqIds) of
+ {value, {RequestId, N, Status}} ->
+ p("pipeline_await_async_reply -> "
+ "found expected request ~w", [N]),
+ ReqIds2 = lists:keydelete(RequestId, 1, ReqIds),
+ NewTimeout = Timeout - (inets_test_lib:timestamp()-T1),
+ pipeline_await_async_reply(ReqIds2, NewTimeout,
+ [{N, Body} | Acc]);
+ {value, {RequestId, N, WrongStatus}} ->
+ p("pipeline_await_async_reply -> "
+ "found request ~w with wrong status", [N]),
+ tsf({reply_with_unexpected_status,
+ {RequestId, N, WrongStatus}});
+ false ->
+ tsf({unexpected_reply, {RequestId, Status}})
+ end;
+ {http, Msg} ->
+ tsf({unexpected_reply, Msg})
+ after Timeout ->
+ receive
+ Any ->
+ tsp("pipeline_await_async_reply -> "
+ "received unknown data after timeout: "
+ "~n ~p", [Any]),
+ tsf({timeout, {unknown, Any}})
+ end
+ end;
+pipeline_await_async_reply(ReqIds, _, Acc) ->
+ tsp("pipeline_await_async_reply -> "
+ "timeout: "
+ "~n ~p"
+ "~nwhen"
+ "~n ~p", [ReqIds, Acc]),
+ tsf({timeout, ReqIds, Acc}).
+
+
+
%%-------------------------------------------------------------------------
http_trace(doc) ->
["Perform a TRACE request that goes through a proxy."];
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index 41e4188e5f..58f7d4fa25 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -762,14 +762,9 @@ ip_mod_cgi(doc) ->
ip_mod_cgi(suite) ->
[];
ip_mod_cgi(Config) when is_list(Config) ->
- case test_server:os_type() of
- vxworks ->
- {skip, cgi_not_supported_on_vxwoks};
- _ ->
- httpd_mod:cgi(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
- ok
- end.
+ httpd_mod:cgi(ip_comm, ?IP_PORT,
+ ?config(host, Config), ?config(node, Config)),
+ ok.
%%-------------------------------------------------------------------------
ip_mod_esi(doc) ->
["Module test: mod_esi"];
@@ -1275,16 +1270,11 @@ essl_mod_cgi(Config) when is_list(Config) ->
ssl_mod_cgi(essl, Config).
ssl_mod_cgi(Tag, Config) ->
- case test_server:os_type() of
- vxworks ->
- {skip, cgi_not_supported_on_vxwoks};
- _ ->
- httpd_mod:cgi(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok
- end.
+ httpd_mod:cgi(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
+ ?config(node, Config)),
+ ok.
%%-------------------------------------------------------------------------
@@ -2698,11 +2688,6 @@ dos_hostname_request(Host) ->
get_nof_clients(Mode, Load) ->
get_nof_clients(test_server:os_type(), Mode, Load).
-get_nof_clients(vxworks, _, light) -> 1;
-get_nof_clients(vxworks, ip_comm, medium) -> 3;
-get_nof_clients(vxworks, ssl, medium) -> 3;
-get_nof_clients(vxworks, ip_comm, heavy) -> 5;
-get_nof_clients(vxworks, ssl, heavy) -> 5;
get_nof_clients(_, ip_comm, light) -> 5;
get_nof_clients(_, ssl, light) -> 2;
get_nof_clients(_, ip_comm, medium) -> 10;
diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl
index cb1214b7fb..387263ce58 100644
--- a/lib/inets/test/httpd_mod.erl
+++ b/lib/inets/test/httpd_mod.erl
@@ -82,19 +82,23 @@ actions(Type, Port, Host, Node) ->
[{statuscode, 200},
{version, "HTTP/1.0"}]).
+
%%-------------------------------------------------------------------------
security(ServerRoot, Type, Port, Host, Node) ->
- %% io:format(user, "~w:security -> entry with"
- %% "~n ServerRoot: ~p"
- %% "~n Type: ~p"
- %% "~n Port: ~p"
- %% "~n Host: ~p"
- %% "~n Node: ~p"
- %% "~n", [?MODULE, ServerRoot, Type, Port, Host, Node]),
-
-%% io:format(user, "~w:security -> register~n", [?MODULE]),
+ tsp("security -> "
+ "entry with"
+ "~n ServerRoot: ~p"
+ "~n Type: ~p"
+ "~n Port: ~p"
+ "~n Host: ~p"
+ "~n Node: ~p", [ServerRoot, Type, Port, Host, Node]),
+
+ tsp("security -> "
+ "register - receive security events"),
global:register_name(mod_security_test, self()), % Receive events
+ tsp("security -> "
+ "sleep"),
test_server:sleep(5000),
OpenDir = filename:join([ServerRoot, "htdocs", "open"]),
@@ -102,133 +106,240 @@ security(ServerRoot, Type, Port, Host, Node) ->
%% Test blocking / unblocking of users.
%% /open, require user one Aladdin
-%% io:format(user, "~w:security -> remove user~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "remove all existing users"),
remove_users(Node, ServerRoot, Host, Port, "open"),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "auth request for nonex user 'one' - expect 401"),
auth_request(Type, Host, Port, Node, "/open/", "one", "onePassword",
[{statuscode, 401}]),
-%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]),
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "one"}, {password, "onePassword"}]},
Node, Port),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "auth request for nonex user 'two' - expect 401"),
auth_request(Type,Host,Port,Node,"/open/", "two", "twoPassword",
[{statuscode, 401}]),
-%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]),
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "two"}, {password, "twoPassword"}]},
Node, Port),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "auth request for nonex user 'Alladin' - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "Aladdin",
"AladdinPassword", [{statuscode, 401}]),
-%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]),
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "Aladdin"},
{password, "AladdinPassword"}]},
Node, Port),
-%% io:format(user, "~w:security -> add users~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "add user 'one'"),
add_user(Node, ServerRoot, Port, "open", "one", "onePassword", []),
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "add user 'two'"),
add_user(Node, ServerRoot, Port, "open", "two", "twoPassword", []),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "auth request 1 for user 'one' with wrong password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
[{statuscode, 401}]),
-%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]),
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "one"}, {password, "WrongPassword"}]},
Node, Port),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "auth request 2 for user 'one' with wrong password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
[{statuscode, 401}]),
-%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]),
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "one"}, {password, "WrongPassword"}]},
Node, Port),
-%% io:format(user, "~w:security -> await block security event~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "await block security event (two failed attempts)"),
receive_security_event({event, user_block, Port, OpenDir,
[{user, "one"}]}, Node, Port),
-%% io:format(user, "~w:security -> unregister~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "unregister - no more security events"),
global:unregister_name(mod_security_test), % No more events.
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "auth request for user 'one' with wrong password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
[{statuscode, 401}]),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "auth request for user 'one' with correct password - expect 403"),
auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword",
[{statuscode, 403}]),
%% User "one" should be blocked now..
- %% [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node,Port),
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "list blocked users - 'one' should be the only one"),
case list_blocked_users(Node, Port) of
[{"one",_, Port, OpenDir,_}] ->
ok;
Blocked ->
- %% io:format(user, "~w:security -> Blocked: ~p"
- %% "~n", [?MODULE, Blocked]),
+ tsp(" *** unexpected blocked users ***"
+ "~n Blocked: ~p", [Blocked]),
exit({unexpected_blocked, Blocked})
end,
-
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
- [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node,Port,OpenDir),
-%% io:format(user, "~w:security -> unblock user~n", [?MODULE]),
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "list users blocked for dir '~p' - "
+ "user 'one' should be the only one", [OpenDir]),
+ [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node, Port, OpenDir),
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "unblock user 'one' for dir '~p'", [OpenDir]),
true = unblock_user(Node, "one", Port, OpenDir),
- %% User "one" should not be blocked any more..
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
+ %% User "one" should not be blocked any more.
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "ensure user 'one' is no longer blocked"),
[] = list_blocked_users(Node, Port),
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
- [] = list_blocked_users(Node, Port, OpenDir),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+
+
+ tsp("security -> "
+ "blocking and unblocking of users - "
+ "auth request for user 'one' with correct password - expect 200"),
auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword",
[{statuscode, 200}]),
+
+
%% Test list_auth_users & auth_timeout
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "list auth users - expect user 'one'"),
["one"] = list_auth_users(Node, Port),
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
- ["one"] = list_auth_users(Node, Port, OpenDir),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "auth request for user 'two' with wrong password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "two", "onePassword",
[{statuscode, 401}]),
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "list auth users - expect user 'one'"),
["one"] = list_auth_users(Node, Port),
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "list auth users for dir '~p' - expect user 'one'", [OpenDir]),
["one"] = list_auth_users(Node, Port, OpenDir),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "auth request for user 'two' with correct password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword",
[{statuscode, 401}]),
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "list auth users - expect user 'one'"),
["one"] = list_auth_users(Node, Port),
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "list auth users for dir '~p' - expect user 'one'", [OpenDir]),
["one"] = list_auth_users(Node, Port, OpenDir),
+
%% Wait for successful auth to timeout.
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "wait for successful auth to timeout"),
test_server:sleep(?AUTH_TIMEOUT*1001),
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "list auth users - expect none"),
[] = list_auth_users(Node, Port),
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "list auth users for dir '~p'~n - expect none", [OpenDir]),
[] = list_auth_users(Node, Port, OpenDir),
+
%% "two" is blocked.
-%% io:format(user, "~w:security -> unblock user~n", [?MODULE]),
+
+ tsp("security -> "
+ "list-auth-users and auth-timeout - "
+ "unblock user 'two' for dir '~p'", [OpenDir]),
true = unblock_user(Node, "two", Port, OpenDir),
+
+
%% Test explicit blocking. Block user 'two'.
-%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
+
+ tsp("security -> "
+ "explicit blocking - list blocked users - should be none"),
[] = list_blocked_users(Node,Port,OpenDir),
-%% io:format(user, "~w:security -> block user~n", [?MODULE]),
+
+ tsp("security -> "
+ "explicit blocking - "
+ "block user 'two' for dir '~p'", [OpenDir]),
true = block_user(Node, "two", Port, OpenDir, 10),
-%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
+
+ tsp("security -> "
+ "explicit blocking - "
+ "auth request for user 'two' with correct password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword",
- [{statuscode, 401}]).
+ [{statuscode, 401}]),
+ tsp("security -> "
+ "done").
+
%%-------------------------------------------------------------------------
auth(Type, Port, Host, Node) ->
+ tsp("auth -> "
+ "entry with"
+ "~n Type: ~p"
+ "~n Port: ~p"
+ "~n Host: ~p"
+ "~n Node: ~p", [Type, Port, Host, Node]),
+
%% Authentication required!
ok = httpd_test_lib:verify_request(Type,Host,Port,Node,
"GET /open/ HTTP/1.0\r\n\r\n",
@@ -913,13 +1024,11 @@ list_users(Node, Root, _Host, Port, Dir) ->
receive_security_event(Event, Node, Port) ->
- %% io:format(user, "~w:receive_security_event -> entry with"
- %% "~n Event: ~p"
- %% "~n Node: ~p"
- %% "~n Port: ~p"
- %% "~n", [?MODULE, Event, Node, Port]),
+ tsp("receive_security_event -> await ~w event", [element(2, Event)]),
receive
Event ->
+ tsp("receive_security_event -> "
+ "received expected ~w event", [element(2, Event)]),
ok;
{'EXIT', _, _} ->
receive_security_event(Event, Node, Port)
@@ -1027,8 +1136,14 @@ check_lists_members1(L1,L2) ->
{error,{lists_not_equal,L1,L2}}.
-%% tsp(F) ->
-%% inets_test_lib:tsp(F).
+%% p(F) ->
+%% p(F, []).
+
+%% p(F, A) ->
+%% io:format(user, "~w:" ++ F ++ "~n", [?MODULE|A]).
+
+tsp(F) ->
+ inets_test_lib:tsp(F).
tsp(F, A) ->
inets_test_lib:tsp(F, A).
diff --git a/lib/inets/test/inets.spec.vxworks b/lib/inets/test/inets.spec.vxworks
deleted file mode 100644
index 6886299226..0000000000
--- a/lib/inets/test/inets.spec.vxworks
+++ /dev/null
@@ -1,5 +0,0 @@
-{topcase, {dir, "../inets_test"}}.
-{skip, {inets_SUITE, ip_mod_cgi, "Requires processes"}}.
-{skip, {inets_SUITE, ip_mod_all_modules, "Requires processes"}}.
-{skip, {inets_SUITE, ssl, "Requires SSL"}}.
-
diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl
index c94be796cd..0f8671b682 100644
--- a/lib/inets/test/inets_test_lib.erl
+++ b/lib/inets/test/inets_test_lib.erl
@@ -31,6 +31,7 @@
send/3, close/2]).
-export([copy_file/3, copy_files/2, copy_dirs/2, del_dirs/1]).
-export([info/4, log/4, debug/4, print/4]).
+-export([timestamp/0, formated_timestamp/0]).
-export([tsp/1, tsp/2, tsf/1, tss/1]).
-export([check_body/1]).
-export([millis/0, millis_diff/2, hours/1, minutes/1, seconds/1, sleep/1]).
@@ -530,34 +531,27 @@ connect(ip_comm, Host, Port, Opts, Type) ->
"~n Opts: ~p"
"~n Type: ~p", [Host, Port, Opts, Type]),
- case gen_tcp:connect(Host, Port, Opts) of
+ case gen_tcp:connect(Host, Port, Opts, timer:seconds(10)) of
{ok, Socket} ->
tsp("connect success"),
{ok, Socket};
- {error, nxdomain} when Type =:= inet6 ->
- tsp("connect error nxdomain when"
- "~n Opts: ~p", [Opts]),
- connect(ip_comm, Host, Port, Opts -- [inet6], inet);
- {error, eafnosupport} when Type =:= inet6 ->
- tsp("connect error eafnosupport when"
- "~n Opts: ~p", [Opts]),
- connect(ip_comm, Host, Port, Opts -- [inet6], inet);
- {error, econnreset} when Type =:= inet6 ->
- tsp("connect error econnreset when"
- "~n Opts: ~p", [Opts]),
- connect(ip_comm, Host, Port, Opts -- [inet6], inet);
- {error, enetunreach} when Type =:= inet6 ->
- tsp("connect error eafnosupport when"
- "~n Opts: ~p", [Opts]),
- connect(ip_comm, Host, Port, Opts -- [inet6], inet);
- {error, econnrefused} when Type =:= inet6 ->
- tsp("connect error econnrefused when"
- "~n Opts: ~p", [Opts]),
+ {error, Reason} when ((Type =:= inet6) andalso
+ ((Reason =:= timeout) orelse
+ (Reason =:= nxdomain) orelse
+ (Reason =:= eafnosupport) orelse
+ (Reason =:= econnreset) orelse
+ (Reason =:= enetunreach) orelse
+ (Reason =:= econnrefused) orelse
+ (Reason =:= ehostunreach))) ->
+ tsp("connect(ip_comm) -> Connect error: "
+ "~n Reason: ~p"
+ "~n Type: ~p"
+ "~n Opts: ~p", [Reason, Type, Opts]),
connect(ip_comm, Host, Port, Opts -- [inet6], inet);
Error ->
- tsp("connect(ip_conn) -> Fatal connect error: "
+ tsp("connect(ip_comm) -> Fatal connect error: "
"~n Error: ~p"
"~nwhen"
"~n Host: ~p"
@@ -642,6 +636,9 @@ tsf(Reason) ->
tss(Time) ->
test_server:sleep(Time).
+timestamp() ->
+ http_util:timestamp().
+
formated_timestamp() ->
format_timestamp( os:timestamp() ).
diff --git a/lib/inets/test/rules.mk b/lib/inets/test/rules.mk
index 047c03b267..c4a62a87ed 100644
--- a/lib/inets/test/rules.mk
+++ b/lib/inets/test/rules.mk
@@ -17,17 +17,12 @@ DEFAULT_TARGETS = opt debug instr release release_docs clean docs
# Erlang language section
# ----------------------------------------------------
EMULATOR = beam
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-# VxWorks object files should be compressed.
-# Other object files should have debug_info.
-ERL_COMPILE_FLAGS += +compressed
-else
+
ifdef BOOTSTRAP
ERL_COMPILE_FLAGS += +slim
else
ERL_COMPILE_FLAGS += +debug_info
endif
-endif
ERLC_WFLAGS = -W
ERLC = erlc $(ERLC_WFLAGS) $(ERLC_FLAGS)
ERL.beam = erl.beam -boot start_clean
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 488947c3a1..949eceea7f 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -18,7 +18,7 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 5.9
+INETS_VSN = 5.9.1
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/inviso/AUTHORS b/lib/inviso/AUTHORS
deleted file mode 100644
index fc5040fe92..0000000000
--- a/lib/inviso/AUTHORS
+++ /dev/null
@@ -1,4 +0,0 @@
-Original Authors and Contributors:
-
-Lennart Ohman
-
diff --git a/lib/inviso/Makefile b/lib/inviso/Makefile
deleted file mode 100644
index be19199151..0000000000
--- a/lib/inviso/Makefile
+++ /dev/null
@@ -1,37 +0,0 @@
-# ``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
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved via the world wide web at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights 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 $(ERL_TOP)/make/target.mk
-include $(ERL_TOP)/make/$(TARGET)/otp.mk
-
-#
-# Macros
-#
-
-SUB_DIRECTORIES = src doc/src
-
-include vsn.mk
-VSN = $(RUNTIME_TOOLS_VSN)
-
-SPECIAL_TARGETS =
-
-#
-# Default Subdir Targets
-#
-include $(ERL_TOP)/make/otp_subdir.mk
-
-
diff --git a/lib/inviso/doc/html/.gitignore b/lib/inviso/doc/html/.gitignore
deleted file mode 100644
index e69de29bb2..0000000000
--- a/lib/inviso/doc/html/.gitignore
+++ /dev/null
diff --git a/lib/inviso/doc/man3/.gitignore b/lib/inviso/doc/man3/.gitignore
deleted file mode 100644
index e69de29bb2..0000000000
--- a/lib/inviso/doc/man3/.gitignore
+++ /dev/null
diff --git a/lib/inviso/doc/pdf/.gitignore b/lib/inviso/doc/pdf/.gitignore
deleted file mode 100644
index e69de29bb2..0000000000
--- a/lib/inviso/doc/pdf/.gitignore
+++ /dev/null
diff --git a/lib/inviso/doc/src/Makefile b/lib/inviso/doc/src/Makefile
deleted file mode 100644
index f00b10f29c..0000000000
--- a/lib/inviso/doc/src/Makefile
+++ /dev/null
@@ -1,122 +0,0 @@
-# ``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
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved via the world wide web at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights 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 $(ERL_TOP)/make/target.mk
-include $(ERL_TOP)/make/$(TARGET)/otp.mk
-
-# ----------------------------------------------------
-# Application version
-# ----------------------------------------------------
-include ../../vsn.mk
-VSN=$(INVISO_VSN)
-APPLICATION=inviso
-
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
-# Target Specs
-# ----------------------------------------------------
-XML_APPLICATION_FILES = ref_man.xml
-XML_REF3_FILES = \
- inviso.xml \
- inviso_as_lib.xml \
- inviso_lfm.xml \
- inviso_lfm_tpfreader.xml \
- inviso_rt.xml \
- inviso_rt_meta.xml
-
-XML_PART_FILES = part.xml part_notes.xml
-XML_CHAPTER_FILES = inviso_chapter.xml notes.xml
-
-BOOK_FILES = book.xml
-
-XML_FILES = \
- $(BOOK_FILES) $(XML_CHAPTER_FILES) \
- $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES)
-
-GIF_FILES = \
- inviso_users_guide_pic1.gif
-
-# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
-
-docs: pdf html man
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: gifs $(HTML_REF_MAN_FILE)
-
-man: $(MAN3_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-debug opt:
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
-
-release_spec:
-
-
-
-
diff --git a/lib/inviso/doc/src/book.xml b/lib/inviso/doc/src/book.xml
deleted file mode 100644
index c258f33ff7..0000000000
--- a/lib/inviso/doc/src/book.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE book SYSTEM "book.dtd">
-
-<book xmlns:xi="http://www.w3.org/2001/XInclude">
- <header titlestyle="normal">
- <copyright>
- <year>2006</year><year>2009</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- 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
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>Inviso</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <insidecover>
- </insidecover>
- <pagetext>Inviso</pagetext>
- <preamble>
- <contents level="2"></contents>
- </preamble>
- <parts lift="no">
- <xi:include href="part.xml"/>
- </parts>
- <applications>
- <xi:include href="ref_man.xml"/>
- </applications>
- <releasenotes>
- <xi:include href="notes.xml"/>
- </releasenotes>
- <listofterms></listofterms>
- <index></index>
-</book>
-
diff --git a/lib/inviso/doc/src/fascicules.xml b/lib/inviso/doc/src/fascicules.xml
deleted file mode 100644
index 0678195e07..0000000000
--- a/lib/inviso/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/inviso/doc/src/inviso.xml b/lib/inviso/doc/src/inviso.xml
deleted file mode 100644
index c0e2e8f0de..0000000000
--- a/lib/inviso/doc/src/inviso.xml
+++ /dev/null
@@ -1,693 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
-
-<erlref>
- <header>
- <copyright>
- <year>2006</year><year>2011</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- 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
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>inviso</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <module>inviso</module>
- <modulesummary>Main API Module to the Inviso Tracer</modulesummary>
- <description>
- <warning>
- <p>The <c>inviso</c> application is deprecated and will be
- removed in the R16 release.</p>
- </warning>
- <p>With the <c>inviso</c> API runtime components can be started and tracing managed across a network of distributed Erlang nodes, using a control component also started with <c>inviso</c> API functions.</p>
- <p>Inviso can be used both in a distributed environment and in a non-distributed. API functions not taking a list of nodes as argument works on all started runtime components. If it is the non-distributed case, that is the local runtime component. The API functions taking a list of nodes as argument, or as part of one of the arguments, can not be used in a non-distributed environment. Return values named <c>NodeResult</c> refers to return values from a single Erlang node, and will therefore be the return in the non-distributed environment.</p>
- </description>
- <funcs>
- <func>
- <name>start() -> {ok,pid()} | {error,Reason}</name>
- <name>start(Options) -> {ok,pid()} | {error,Reason}</name>
- <fsummary>Start a control component at the local node</fsummary>
- <type>
- <v>Options = [Option]</v>
- </type>
- <desc>
- <p><c>Options</c> may contain both options which will be default options to a runtime component when started, and options to the control component. See <seealso marker="#add_nodes/3">add_nodes/3</seealso> for details on runtime component options. The control component recognizes the following options:</p>
- <taglist>
- <tag><c>{subscribe,Pid}</c></tag>
- <item>
- <p>Making the process <c>Pid</c> receive Inviso events from the control component.</p>
- <p>Starts a control component process on the local node. A control component must be started before runtime components can be started manually or otherwise accessed through the <c>inviso</c> API.</p>
- </item>
- </taglist>
- </desc>
- </func>
- <func>
- <name>stop() -> shutdown</name>
- <fsummary>Stop the control component</fsummary>
- <desc>
- <p>Stops the control component. Runtime components are left as is. They will behave according to their dependency values.</p>
- </desc>
- </func>
- <func>
- <name>add_node(RTtag) -> NodeResult | {error,Reason}</name>
- <name>add_node(RTtag,Options) -> NodeResult | {error,Reason}</name>
- <fsummary>Starts or adopts a runtime component at the local node</fsummary>
- <type>
- <v>RTtag = PreviousRTtag = term()</v>
- <v>Options = [Option]</v>
- <v>&nbsp;Option -- see below</v>
- <v>&nbsp;Option = {dependency,Dep}</v>
- <v>&nbsp;&nbsp;Dep = int() | infinity</v>
- <d>The timeout, in milliseconds, before the runtime component will terminate if abandoned by <em>this</em>control component.</d>
- <v>&nbsp;Option = {overload,Overload} | overload</v>
- <d>Controls how and how often overload checks shall be performed. Just <c>overload</c>specifies that no loadcheck shall be performed.</d>
- <v>&nbsp;&nbsp;Overload = Interval | {LoadMF,Interval,InitMFA,RemoveMFA}</v>
- <v>&nbsp;&nbsp;&nbsp;LoadMF = {Mod,Func} | function()/1</v>
- <v>&nbsp;&nbsp;&nbsp;Interval = int() | infinity</v>
- <d>Interval is the time in milliseconds between overload checks.</d>
- <v>&nbsp;&nbsp;&nbsp;InitMFA = RemoveMFA = {Mod,Func,ArgList} | void</v>
- <d>When starting up the runtime component or when changing options (see <c>change_options/2</c>) the overload mechanism is initialized with a call to the <c>InitMFA</c>function. It shall return <c>LoadCheckData</c>. Every time a load check is performed, <c>LoadMF</c>is called with <c>LoadCheckData</c>as its only argument. <c>LoadMF</c>shall return <c>ok</c>or <c>{suspend,Reason}</c>. When the runtime component is stopped or made to change options involving changing overload-check, the <c>RemoveMFA</c>function is called. Its return value is discarded.</d>
- <v>NodeResult = {ok,NAns} | {error,Reason}</v>
- <v>&nbsp;NAns = new | {adopted,State,Status,PreviousRTtag} | already_added</v>
- <v>&nbsp;&nbsp;State = new | tracing | idle</v>
- <v>&nbsp;&nbsp;Status = running | {suspended,SReason}</v>
- </type>
- <desc>
- <p>Starts or tries to connect to an existing runtime component at the local node, regardless if the system is distributed or not. <c>Options</c> will override any default options specified at start-up of the control component.</p>
- <p>The <c>PreviousRTtag</c> can indicate if the incarnation of the runtime component at the node in question was started by "us" and then can be expected to do tracing according to "our" instructions or not.</p>
- </desc>
- </func>
- <func>
- <name>add_node_if_ref(RTtag) -> NodeResult | {error,{wrong_reference,OtherTag}} | {error,Reason}</name>
- <name>add_node_if_ref(RTtag,Options) -> NodeResult | {error,{wrong_reference,OtherRef}} | {error,Reason}</name>
- <fsummary>Start or adopt a runtime component at the local node, provided it has a certain rttag</fsummary>
- <type>
- <v>OtherRef = term()</v>
- <d>rttag of the running incarnation</d>
- </type>
- <desc>
- <p>As <seealso marker="#add_node/1">add_node/1,2</seealso> but will only adopt the runtime component if its rttag is <c>RTtag</c>.</p>
- </desc>
- </func>
- <func>
- <name>add_nodes(Nodes,RTtag) -> {ok,NodeResults} | {error,Reason}</name>
- <name>add_nodes(Nodes,RTtag,Options) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Start or adopt runtime components at some nodes</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- </type>
- <desc>
- <p>As <seealso marker="#add_node/1">add_node/1,2</seealso> but for a distributed environment.</p>
- </desc>
- </func>
- <func>
- <name>add_nodes_if_ref(Nodes,RTtag) -> NodeResult | {error,Reason}</name>
- <name>add_nodes_if_ref(Nodes,RTtag,Options) -> NodeResult | {error,Reason}</name>
- <fsummary>Start or adopt runtime components at some nodes, provided they have a certain rttag</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- </type>
- <desc>
- <p>As <seealso marker="#add_node_if_ref/1">add_node_if_ref/1,2</seealso> but for a distributed environment.</p>
- </desc>
- </func>
- <func>
- <name>stop_nodes() -> {ok,NodeResults} | NodeResult</name>
- <name>stop_nodes(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Stop runtime components</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Stops runtime component on <c>Nodes</c>. <c>stop_nodes/0</c> will if the control component is running on a distributed node stop all runtime components. And if running on a non distributed node, stop the local and only runtime component.</p>
- </desc>
- </func>
- <func>
- <name>stop_all() = {ok,NodeResults} | NodeResult</name>
- <fsummary>Stop both control and runtime components</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>A combination of <seealso marker="#stop/0">stop/0</seealso> and <seealso marker="#stop_nodes/0">stop_nodes/0</seealso>.</p>
- </desc>
- </func>
- <func>
- <name>change_options(Options) -> NodeResult | {ok,NodeResults} | {error,Reason}</name>
- <name>change_options(Nodes,Options) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Change options for runtime components</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Changes the options for one or several runtime components. If for instance overload is redefined, the previous overload will be stopped and the new started. See <seealso marker="#add_node/1">add_node/1</seealso> for details on <c>Options</c>.</p>
- </desc>
- </func>
- <func>
- <name>init_tracing(TracerData) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>init_tracing(TracerList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>init_tracing(Nodes,TracerData) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Initiate tracing</fsummary>
- <type>
- <v>TracerData = [{trace,LogTD} [,{ti,TiTD}] }] | LogTD</v>
- <v>LogTD = {HandlerFun,Data1} | collector | {relayer,CollectingNode} | {ip,IPPortParameters} | {file,FilePortParameters}</v>
- <v>TiTD = {file,FileName} | {file,FileName,TiSpec} | {relay,Node}</v>
- <v>&nbsp;TiSpec = {InitMFA,RemoveMF,CleanMF}</v>
- <v>&nbsp;&nbsp;InitMFA = {Mi,Fi,Argsi}</v>
- <v>&nbsp;&nbsp;RemoveMF = {Mr,Fr} | void</v>
- <v>&nbsp;&nbsp;CleanMF = {Mc,Fc}</v>
- <v>&nbsp;&nbsp;Mi = Fi = Mr = Fr = Mc = Fd = atom()</v>
- <v>&nbsp;&nbsp;Argsi = [term()]</v>
- <v>TracerList = [{Node,TracerData}]</v>
- <v>IPPortParameters = Portno | {Portno,Qsize}</v>
- <v>&nbsp;Portno = tcp_portno()</v>
- <v>&nbsp;Qsize = int()</v>
- <v>FilePortParameters = {Filename,wrap,Tail,{time,WrapTime},WrapCnt} | {FileName,wrap,Tail,WrapSize,WrapCnt} | {FileName,wrap,Tail,WrapSize} | {FileName,wrap,Tail} | FileName</v>
- <v>&nbsp;FileName = string()</v>
- <v>&nbsp;Tail = string() =/= ""</v>
- <v>&nbsp;WrapTime = WrapCnt = WrapSize = int() >0</v>
- <v>TracerList = [{Node,TracerData}]</v>
- <v>Nodes = [Node]</v>
- <v>HandlerFun = function()/2;</v>
- <v>&nbsp;HandlerFun(TraceMsg,Data1) -> NewData</v>
- <v>CollectingNode = pid() | node()</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {ok,LogResults} | {error,NReason}</v>
- <v>&nbsp;LogResults = [LogResult]</v>
- <v>&nbsp;&nbsp;LogResult = {trace_log,LogRes} | {ti_log,LogRes}</v>
- <v>&nbsp;&nbsp;&nbsp;LogRes = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Starts the tracing at the specified nodes, meaning that the runtime components transits from the state <c>new</c> or <c>idle</c> to <c>tracing</c>. For trace messages to be generated, there must of course also be trace pattern and/or trace flags set. Such can not be set before tracing has been initiated with <c>init_tracing/1,2</c>.</p>
- <p><c>TracerData</c> controls how the runtime component will handle generated trace messages. The <c>trace</c> tag controls how regular trace messages are handled. The <c>ti</c> tag controls if and how trace information will be stored and the meta tracer will be activated. That is if <c>ti</c> is omitted, no meta tracer will be started as part of the runtime component. It is possible to have <c>ti</c> without <c>trace</c>, but most likely not useful.</p>
- <p>The <c>ip</c> and <c>file</c> trace tracerdata instructions results in using the built in trace ip-port and file-port respectively. <c>relayer</c> will result in that all regular trace messages are forwarded to a runtime component at the specified node. Using a <c>HandlerFun</c> will result in that every incoming regular trace message is applied to the <c>HandlerFun</c>. <c>collector</c> can be used to use this runtime component to receive relayed trace messages and print them to the shell.
- </p>
- <p>The trace information can be configured to either write trace information to a plain trace information file or to relay it to another inviso meta tracer on another node. The inviso meta tracer is capable of matching function calls with their function returns (only if <c>return_trace</c> is activated in the meta trace match specification for the function in question). This is necessary since it may not be possible to decide what to do, if anything shall be done at all, until the return value of the function call is examined.
- </p>
- <p>To be able to match calls with returns a state can be saved when detecting a function call in a public loop data structure kept by the inviso meta tracer. The public loop data structure is given as argument to a handler-function called whenever a meta trace message arrives to the inviso meta tracer (both function calls and function returns). The public loop data structure is first initiated by the <c>Mi:Fi</c> function which takes the items in <c>Argsi</c> as arguments. <c>Fi</c> shall return the initial public loop data structure. When meta tracing is stopped, either because tracing is stopped or because tracing is suspended, the <c>Mr:Fr(PublicLoopData)</c> is called to offer a possibility to clean-up. Note that for every function meta-tracing is activated, a public loop data modification function can be specified. That function will prepare the current loop data structure for this particular function.
- </p>
- <p>Further there is a risk that function call states becomes abandoned inside the public loop data structure. This will happen if a function call is entered into the public loop data structure, but no function return occurs. To prevent the public loop data structure from growing infinitely the clean function <c>Fc</c> will periodically be called with the public loop data structure as argument. Elements entered into the public loop data structure as a result of a function call must contain a timestamp for the <c>Fc</c> to be able to conclude if it is abandoned or not. <c>Fc</c> shall return a new public loop data structure.
- </p>
- <p>When initiating tracing involving trace information without a <c>TiSpec</c>, a default public loop data structure will be initiated to handle locally registered process aliases. The default public loop data structure is a two-tuple where the first element is used by the meta tracing on the BIF <c>register/2</c>. The second element is left for user usage.</p>
- <p>The default public loop data structure may be extended with more element positions. The first position must be left to the implementation of registered-name translations. If the public loop data structure is changed no longer meeting this requirement, the <seealso marker="#tpm_localnames/0">tpm_localnames/0,1</seealso> and <seealso marker="#tpm_globalnames/0">tpm_globalnames/0,1</seealso> can no longer be used.</p>
- <p>A wrap files specification is used to limit the disk space consumed by the trace. The trace is written to a limited number of files each with a limited size. The actual filenames are <c>Filename ++ SeqCnt ++ Tail</c>, where <c>SeqCnt</c> counts as a decimal string from 0 to <c>WrapCnt</c> and then around again from 0. When a trace message written to the current file makes it longer than <c>WrapSize</c>, that file is closed, if the number of files in this wrap trace is as many as <c>WrapCnt</c> the oldest file is deleted then a new file is opened to become the current. Thus, when a wrap trace has been stopped, there are at most <c>WrapCnt</c> trace files saved with a size of at least <c>WrapSize</c> (but not much bigger), except for the last file that might even be empty. The default values are <c>WrapSize == 128*1024</c> and <c>WrapCnt == 8</c>.</p>
- <p>The <c>SeqCnt</c> values in the filenames are all in the range 0 through <c>WrapCnt</c> with a gap in the circular sequence. The gap is needed to find the end of the trace.</p>
- <p>If the <c>WrapSize</c> is specified as <c>{time,WrapTime}</c>, the current file is closed when it has been open more than <c>WrapTime</c> milliseconds, regardless of it being empty or not.</p>
- <p>The ip trace driver has a queue of <c>QSize</c> messages waiting to be delivered. If the driver cannot deliver messages as fast as they are produced by the runtime system, they are dropped. The number of dropped messages are indicated in the trace log as separate trace message.</p>
- </desc>
- </func>
- <func>
- <name>stop_tracing(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <name>stop_tracing() -> {ok,NodeResults} | NodeResult</name>
- <fsummary>Stop tracing</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {ok,State} | {error,Reason}</v>
- <v>&nbsp;State = new | idle</v>
- </type>
- <desc>
- <p>Stops tracing on all or specified <c>Nodes</c>. Flushes the trace buffer if a trace-port is used, closes the trace-port and removes all trace flags and meta-patterns. The nodes are called in parallel.</p>
- <p>Stopping tracing means going to state <c><![CDATA[idle<c>. If the runtime component was already in state <c>new]]></c>, it will of course remain in state <c>new</c> (then there was no tracing to stop).</p>
- </desc>
- </func>
- <func>
- <name>clear() -> {ok,NodeResults} | NodeResult</name>
- <name>clear(Nodes,Options) -> {ok,NodeResults} | {error,Reason}</name>
- <name>clear(Options) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Stop tracing and remove meta trace patterns</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>Options = [Option]</v>
- <v>&nbsp;Option = keep_trace_patterns | keep_log_files</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>&nbsp;NodeResult = {ok,{new,Status}} | {error,Reason}</v>
- <v>&nbsp;&nbsp;Status = running | {suspended,SReason}</v>
- </type>
- <desc>
- <p>Stops all tracing including removing meta-trace patterns. Removes all trace patterns. If the node is <c>tracing</c> or <c>idle</c>, trace-logs belonging to the current tracerdata are removed. Hence the node is returned to state <c>new</c>. Note that the node can still be suspended.</p>
- <p>Various options can make the node keep set trace patterns and log-files. The node still enters the <c>new</c> state.</p>
- </desc>
- </func>
- <func>
- <name>tp(Nodes,Mod,Func,Arity,MatchSpec,Opts) -> </name>
- <name>tp(Nodes,Mod,Func,Arity,MatchSpec) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tp(Mod,Func,Arity,MatchSpec,Opts) -> </name>
- <name>tp(Mod,Func,Arity,MatchSpec) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>tp(Nodes,PatternList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tp(PatternList) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Set global trace patterns</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>Mod = Func = atom() | '_'</v>
- <v>Arity = int() | '_'</v>
- <v>MatchSpec = true | false | [] | matchspec()</v>
- <v>PatternList = [Pattern],</v>
- <v>&nbsp;Pattern = {Mod,Func,Arity,MatchSpec,Opts}</v>
- <v>Opts = [Opt]</v>
- <v>&nbsp;Opt = only_loaded</v>
- <v>NodeResults = [NodeResult]</v>
- <v>&nbsp;NodeResult = {ok,[Ans]} | {error,Reason}</v>
- <v>&nbsp;&nbsp;Ans = int() | {error,Reason}</v>
- </type>
- <desc>
- <p>Set trace pattern (global) on specified or all nodes. The integer replied if the call was successfully describes the number of matched functions. The functions without a <c>Nodes</c> argument means all nodes, in a non-distributed environment it means the local node. Using wildcards follows the rules for wildcards of <c>erlang:trace_pattern/3</c>. It is for instance illegal to specify <c>M == '_'</c> while <c>F</c> is not <c>'_'</c>.</p>
- <p>When calling several nodes, the nodes are called in parallel.</p>
- <p>The option <c>only_loaded</c> will prevent modules not loaded (yet) into the runtime system to become loaded just as a result of that a trace pattern is requested to be set on it. Otherwise modules are automatically loaded if not already loaded (since the module must be present for a trace pattern to be set on it). The latter does not apply if the wildcard <c>'_'</c> is used as module specification.</p>
- </desc>
- </func>
- <func>
- <name>tpl(Nodes,Mod,Func,Arity,MatchSpec) -> </name>
- <name>tpl(Nodes,Mod,Func,Arity,MatchSpec,Opts) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tpl(Mod,Func,Arity,MatchSpec) -> </name>
- <name>tpl(Mod,Func,Arity,MatchSpec,Opts) -> {ok,NodeResults} | NodeResult| {error,Reason}</name>
- <name>tpl(Nodes,PatternList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tpl(PatternList) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Set local trace patterns</fsummary>
- <desc>
- <p>See <seealso marker="#tp/6">tp/N</seealso> function above for details on arguments and return values.</p>
- <p>Set local trace pattern on specified functions. When calling several nodes, the nodes are called in parallel.</p>
- </desc>
- </func>
- <func>
- <name>ctp(Nodes,Mod,Func,Arity) -> {ok,NodeResults} | {error,Reason}</name>
- <name>ctp(Mod,Func,Arity) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Clear global trace patterns</fsummary>
- <desc>
- <p>See <seealso marker="#tp/6">tp/N</seealso> for argument descriptions.</p>
- <p>Clear global trace patterns. When calling several nodes, the nodes are called in parallel.</p>
- </desc>
- </func>
- <func>
- <name>ctpl(Nodes,Mod,Func,Arity) -> {ok,NodeResults} | {error,Reason}</name>
- <name>ctpl(Mod,Funct,Arity) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Clear local trace patterns</fsummary>
- <desc>
- <p>See <seealso marker="#tp/6">tp/N</seealso> for argument description.</p>
- <p>Clear local trace patterns. When calling several nodes, the nodes are called in parallel.</p>
- </desc>
- </func>
- <func>
- <name>tf(Nodes,PidSpec,FlagList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tf(PidSpec,FlagList) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>tf(Nodes,TraceConfList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tf(NodeTraceConfList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tf(TraceConfList) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Set process trace flags</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>NodeTraceConfList = [{Node,TraceConfList}]</v>
- <v>TraceConfList = [{PidSpec,FlagList}]</v>
- <v>FlagList = [Flag]</v>
- <v>PidSpec = all | new| existing | pid() | locally_registered_name()</v>
- <v>Flag -- see erlang:trace/3</v>
- <v>NodeResult = {ok,[Ans]} | {error,Reason}</v>
- <v>Ans = int() | {error,Reason}</v>
- </type>
- <desc>
- <p>Set process trace flags on processes on all or specified nodes. The integer returned if the call was successful describes the matched number of processes. The functions without a <c>Nodes</c> argument means all nodes, in a non-distributed environment it means the local node.
- </p>
- <p>There are many combinations which does not make much sense. For instance specifying a certain process identifier at all nodes. Or an empty <c>TraceConfList</c> for all nodes.</p>
- <p>When calling several nodes, the nodes are called in parallel.</p>
- </desc>
- </func>
- <func>
- <name>ctf(Nodes,PidSpec,FlagList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>ctf(PidSpec,FlagList) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>ctf(Nodes,TraceConfList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>ctf(TraceConfList) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Clear process trace flags</fsummary>
- <desc>
- <p>See <seealso marker="#tf/3">tf/N</seealso> for arguments and return value description.</p>
- <p>Clear process trace flags on all or specified nodes. When calling several nodes, the nodes are called in parallel.</p>
- </desc>
- </func>
- <func>
- <name>ctf_all(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <name>ctf_all() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Clear all process trace flags</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Clears all trace flags on all or specified nodes. Just for convenience.</p>
- </desc>
- </func>
- <func>
- <name>init_tpm(Mod,Func,Arity,CallFunc) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>init_tpm(Nodes,Mod,Func,Arity,CallFunc) -> {ok,NodeResults} | {error,Reason}</name>
- <name>init_tpm(Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>init_tpm(Nodes,Mod,Func,Arity, InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Initialize meta tracing</fsummary>
- <type>
- <v>Mod = Func = atom()</v>
- <v>Arity = int()</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- <v>InitFunc,RemoveFunc = {Module,Function} | function()/4 | void</v>
- <v>CallFunc = ReturnFunc = {Module,Function} | function()/3 | void</v>
- </type>
- <desc>
- <p>Initializes <c>Mod:Func/Arity</c> for meta tracing without setting any meta trace patterns. This is necessary if the named match specs will be used (see <seealso marker="#tpm_ms/5">tpm_ms/5,6</seealso>). Otherwise initialization of public loop data can be done at the same time as setting meta trace patterns using <seealso marker="#tpm/8">tpm/8,9</seealso>.</p>
- <p>Note that we can not use wildcards here (even if it is perfectly legal in Erlang). It also sets the <c>CallFunc</c> and <c>ReturnFunc</c> for the meta traced function. That is the functions which will be called when a function call and a return_trace meta trace message respectively arrives to the inviso meta tracer for <c>Mod:Func/Arity</c>.</p>
- <p>This function is also available without <c>InitFunc</c> and <c>RemoveFunc</c>. That means that no initialization of the public loop data structure will be done and that <c>CallFunc</c> and <c>ReturnFunc</c> must either use already existing parts of public loop data structure or not use it at all.</p>
- <p>The <c>InitFunc</c> initializes the already existing public loop data structure for use with <c>Mod:Func/Arity. InitFunc(Mod,Func,Arity,PublLD) -> {ok,NewPublLD,Output}</c> where <c>OutPut</c> can be a binary which will then be written to the trace information file. If it is not a binary, no output will be done. <c>RemoveFunc</c> will be called when the meta tracing is cleared with <seealso marker="#ctpm/3">ctpm/3,4</seealso>. <c>RemoveFunc(Mod,Func,Arity,PublLD) -> {ok,NewPublLD}</c>.</p>
- <p>See <seealso marker="#tpm/4">tpm/N</seealso> for details on <c>CallFunc</c> and <c>ReturnFunc</c>.</p>
- </desc>
- </func>
- <func>
- <name>tpm(Mod,Func,Arity,MS) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>tpm(Nodes,Mod,Func,Arity,MS) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tpm(Mod,Func,Arity,MS,CallFunc) -> {ok,NodeResults} | NodeResults | {error,Reason}</name>
- <name>tpm(Nodes,Mod,Func,Arity,MS,CallFunc) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tpm(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> {ok,NodeResults} | NodeResults | {error,Reason}</name>
- <name>tpm(Nodes,Mod,Func,Arity,MS, InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Activate meta tracing</fsummary>
- <type>
- <v>Mod = Func = atom()</v>
- <v>Arity = int()</v>
- <v>MS = [match_spec()]</v>
- <v>Nodes = [Node]</v>
- <v>InitFunc = RemoveFunc = {Module,Function} | function()/4 | void</v>
- <v>CallFunc = ReturnFunc = {Module,Function} | function()/3 | void</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {ok,1} | {ok,0} | {error,Reason}1</v>
- </type>
- <desc>
- <p>Activates meta-tracing in the inviso_rt_meta tracer. Except when using <c>tpm/6</c>, <c>tpm/8</c> and <c>tpm/9</c> the <c>Mod:Func/Arity</c> must first have been initiated using <seealso marker="#init_tpm/4">init_tpm/N</seealso>. When calling several nodes, the nodes are called in parallel.</p>
- <p><c>CallFunc</c> will be called every time a meta trace message arrives to the inviso meta tracer because of a call to <c>Func</c>. <c>CallFunc(CallingPid,ActualArgList,PublLD) -> {ok,NewPrivLD,Output}</c> where <c>Output</c> can be a binary or <c>void</c>. If it is a binary it will be written to the trace information file.</p>
- <p><c>ReturnFunc</c> will be called every time a meta return_trace message arrives to the inviso meta tracer because of a return_trace of a call to <c>Func</c>. <c>ReturnFunc(CallingPid,ReturnValue,PublLD) -> {ok,NewPrivLD,Output}</c>. Further the <c>ReturnFunc</c> must handle the fact that a return_trace message arrives for a call which was never noticed. This because the message queue of the meta tracer may have been emptied.</p>
- </desc>
- </func>
- <func>
- <name>tpm_tracer(Mod,Func,Arity,MS) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>tpm_tracer(Nodes,Mod,Func,Arity,MS) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tpm_tracer(Mod,Func,Arity,MS,CallFunc) -> {ok,NodeResults} | NodeResults | {error,Reason}</name>
- <name>tpm_tracer(Nodes,Mod,Func,Arity,MS,CallFunc) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tpm_tracer(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> {ok,NodeResults} | NodeResults | {error,Reason}</name>
- <name>tpm_tracer(Nodes,Mod,Func,Arity,MS, InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Activate meta tracing and at the same time append a {tracer,Tracer} process trace flag to the enable list in a match specification <c>trace</c>action term</fsummary>
- <desc>
- <p>See tpm/X for details on arguments and return values.</p>
- <p>Same as tpm/X but all match specs in <c>MS</c> containing a <c>trace</c> action term will have a <c>{tracer,Tracer}</c> appended to its enable-list. <c>Tracer</c> will be the current output for regular trace messages as specified when tracing was initiated. This function is useful when setting a meta trace pattern on a function with the intent that its execution shall turn tracing on for the process executing the match-spec in the meta trace pattern. The reason the <c>tracer</c> process trace flag can not be explicitly written in the action term by the user is that it may be difficult to learn its exact value for a remote node. Further more inviso functions are made to work on several nodes at the same time, requiring different match specs to be set for different nodes.</p>
- <p>Simple example: We want any process executing the function <c>mymod:init(1234)</c> (with the argument, exactly the integer 1234) to begin function-call tracing. In the example, if the process is found to be one that shall start call tracing, we also first disable <c>all</c> process trace flags to ensure that we have full control over what the process traces. <c>void</c> in the example specifies that the meta-tracer (inviso_rt_meta) will not call any function when meta trace messages for <c>mymod:init/1</c> arrives. There is no need for a <c>CallFunc</c> since the side-effect (start call-tracing) is achieved immediately with the match-spec.</p>
- <code type="none">
- inviso:tpm_tracer(mymod,init,1,[{[1234],[],[{trace,[all],[call]}]}],void). </code>
- <p>This will internally, by the meta tracer on each Erlang node, be translated to:</p>
- <code type="none">
- erlang:trace_pattern({mymod,init,1},[{[1234],[],[{trace,[all],[call,{{tracer,T}}]}]}],[{meta,P}]).
- </code>
- <p>Where <c>T</c> is the tracer for regular trace messages (most often a trace-port, but can be the runtime component inviso_rt process), and <c>P</c> is the meta tracer (the inviso_rt_meta process).</p>
- </desc>
- </func>
- <func>
- <name>tpm_ms(Mod,Func,Arity,MSname,MS) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>tpm_ms(Nodes,Mod,Func,Arity,MSname,MS) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Add match specifications</fsummary>
- <type>
- <v>Nodes = [Node]&lt;v> &lt;v>Mod = Func = atom()&lt;v> &lt;v>Arity = int()&lt;v> &lt;v>MSname = term()&lt;v> &lt;v>MS = [match_spec()]&lt;v> &lt;v>NodeResults = [{Node,NodeResult}]&lt;v> &lt;v>NodeResult = {ok,1} | {ok,0} | {error,Reason}&lt;v></v>
- </type>
- <desc>
- <p>This function adds a list of match-specs to the already existing ones. It uses an internal database to keep track of existing match-specs. This set of match specs can hereafter be referred to with the name <c>MSname</c>. If the match-spec does not result in any meta traced functions (for whatever reason), the <c>MS</c> is not saved in the database. The previously known match-specs are not removed. If <c>MSname</c> is already in use as a name referring to a set of match-specs for this particular meta-traced function, the previous set of match-specs are replaced with <c>MS</c>.</p>
- <p><c>Mod:Func/Arity</c> must previously have been initiated in order for this function to add a match-spec.</p>
- <p>When calling several nodes, the nodes are called in parallel. <c>{ok,1}</c> indicates success.</p>
- </desc>
- </func>
- <func>
- <name>tpm_ms_tracer(Mod,Func,Arity,MSname,MS) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>tpm_ms_tracer(Nodes,Mod,Func,Arity,MSname,MS) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Add match specifications and at the same time append a {tracer,Tracer} process trace flag to the enable list in a match specification <c>trace</c>action term</fsummary>
- <desc>
- <p>See tpm_ms/X for details on arguments and return values, and tpm_tracer/X for explanations about the appending of <c>{tracer,Tracer}</c> process trace flag.</p>
- </desc>
- </func>
- <func>
- <name>ctpm_ms(Mod,Func,Arity,MSname) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>ctpm_ms(Nodes,Mod,Func,Arity,MSname) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Remove a match specification</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Removes a named match-spec from the meta traced function. Note that it never is a fault to remove a match spec. Not even from a function which is non existent.</p>
- <p>When calling several nodes, the nodes are called in parallel.</p>
- </desc>
- </func>
- <func>
- <name>ctpm(Mod,Func,Arity) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>ctpm(Nodes,Mod,Func,Arity) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Remove a meta trace pattern</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Removes the meta trace pattern for the function, means stops generating output for this function. The public loop data structure may be cleared by the previously entered <c>RemoveFunc</c>.</p>
- <p>When calling several nodes, the nodes are called in parallel.</p>
- </desc>
- </func>
- <func>
- <name>tpm_localnames() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>tpm_localnames(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Set meta trace pattern on <c>register/2</c></fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {R1,R2}</v>
- <v>R1 = R2 = {ok,0} | {ok,1} | {error,Reason}</v>
- </type>
- <desc>
- <p>Quick version for setting meta-trace patterns on <c>erlang:register/2</c>. It uses a default <c>CallFunc</c> and <c>ReturnFunc</c> in the meta-tracer server. The main purpose of this function is to create ti-log entries for associations between pids and registered name aliases. The implementation uses return_trace to see if the registration was successful or not, before actually making the ti-log alias entry. Further the implementation also meta traces the BIF <c>unregister/1</c>.</p>
- <p>If both <c>N1</c> and <c>N2</c> is 1, function call was successful. <c>N1</c> and <c>N2</c> represent setting meta trace pattern on <c>register/2</c> and <c>unregister/1</c>.</p>
- </desc>
- </func>
- <func>
- <name>ctpm_localnames() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>ctpm_localnames(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Clear meta trace pattern on <c>register/2</c></fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {R1,R2}</v>
- <v>R1 = R2 = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Function for removing previously set patters by <seealso marker="#tpm_localnames/0">tpm_localnames/0</seealso>. The two results <c>R1</c> and <c>R2</c> represents that meta pattern is removed from both <c>register/2</c> and <c>unregister/1</c>.</p>
- </desc>
- </func>
- <func>
- <name>tpm_globalnames() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>tpm_globalnames(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Set meta trace pattern on <c>global:register_name/2</c></fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {R1,R2}</v>
- <v>R1 = R2 = {ok,0} | {ok,1} | {error,Reason}</v>
- </type>
- <desc>
- <p>Quick version for setting meta-trace patterns capable of learning the association of a pid with a globally registered name (registered using <c>global:register_name</c>). The implementation meta-traces on <c>global:handle_call({register,'_','_','_'},'_','_')</c> and <c>global:delete_global_name/2</c>. The <c>N1</c> and <c>N2</c> represents the success of the two sub-tmp calls.</p>
- </desc>
- </func>
- <func>
- <name>ctpm_globalnames() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>ctpm_globalnames(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Clear meta trace pattern on <c>global:register_name/2</c></fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {R1,R2} | {error,Reason}</v>
- <v>R1 = R2 = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Function for removing previously set meta patters by <seealso marker="#tpm_globalnames/0">tpm_globalnames/0,1</seealso>. The two results <c>R1</c> and <c>R2</c> represents that meta pattern are removed from both <c>global:handle_call/3</c> and <c>global:delete_global_name/1</c>.</p>
- </desc>
- </func>
- <func>
- <name>ctp_all() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>ctp_all(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Clear all (global and local) trace patterns</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Clears all, both global and local trace patterns. Does not clear meta trace patterns. Equivalent to a call to <seealso marker="#ctp/4">ctp/3,4</seealso> and to <seealso marker="#ctpl/4">ctpl/3,4</seealso> with wildcards <c>'_'</c> for all modules, functions and arities.</p>
- </desc>
- </func>
- <func>
- <name>suspend(SReason) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>suspend(Nodes,SReason) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Suspend runtime components</fsummary>
- <type>
- <v>SReason = term()</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Suspends the runtime components. <c>SReason</c> will become the suspend-reason replied in for instance a <seealso marker="#get_status/0">get_status/0,1</seealso> call. A runtime component that becomes suspended removes all trace flags and all meta trace patterns. In that way trace output is no longer generated. The task of reactivating a suspended runtime component is outside the scoop of inviso. It can for instance be implemented by a higher layer trace-tool "remembering" all trace flags and meta patterns set.</p>
- </desc>
- </func>
- <func>
- <name>cancel_suspension() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>cancel_suspend(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Reactivate suspended runtime components</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Makes the runtime components <c>running</c> again (as opposite to <c>suspended).</c> Since reactivating previous trace flags and meta trace patterns is outside the scoop of inviso, cancelling suspension is simply making it possible to set trace flags and meta trace patterns again.</p>
- </desc>
- </func>
- <func>
- <name>get_status() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>get_status(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Get status of runtime components</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {ok,{State,Status}} | {error,Reason}</v>
- <v>State = new | idle | tracing</v>
- <v>Status = running | {suspended,SReason}</v>
- <v>SReason = term()</v>
- </type>
- <desc>
- <p>Finds out the state and status of a runtime component. A runtime component is in state <c>new</c> before it has been initiated to do any tracing the first time. There are clear-functions which can make a runtime component become <c>new</c> again without having to restart. A runtime component becomes <c>idle</c> after tracing is stopped.</p>
- </desc>
- </func>
- <func>
- <name>get_tracerdata() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>get_tracerdata(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Get tracerdata of runtime components</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {ok,NResult} | {error,Reason}</v>
- <v>NResult = TracerData | no_tracerdata</v>
- </type>
- <desc>
- <p>Returns the current tracerdata of a runtime component. A runtime component in state <c>new</c> can not have tracerdata. An <c>idle</c> runtime component does have tracerdata, the last active tracerdata. <c>TracerData</c> will be a term as specified to <c>init_tracing</c> when tracing was initiated for the runtime component.</p>
- </desc>
- </func>
- <func>
- <name>list_logs() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>list_logs(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <name>list_logs(NodeTracerData) -> {ok,NodeResults} | {error,Reason}</name>
- <name>list_logs(TracerData) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Get log file names associated with tracerdata</fsummary>
- <type>
- <v>TracerData -- see init_tracing/1,2</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {ok,FileList} | {ok,no_log} | {error,Reason}</v>
- <v>&nbsp;FileList = [FileType]</v>
- <v>&nbsp;&nbsp;FileType = {trace_log,Dir,Files} | {ti_log,Dir,Files}</v>
- <v>&nbsp;&nbsp;Files = [FileNameWithOutPath]</v>
- </type>
- <desc>
- <p>Returns the actually existing log files associated with <c>TracerData</c>. If a tracerdata is not specified, current tracerdata is used for that particular runtime component. <c>Files</c> will be a list of one or more files should it be a wrap-set. Otherwise the it is a list of only one filename.</p>
- <p>This function is useful to learn the name and path of all files belonging to a trace. This information can later be used to move those files for merging. Note that since it is possible to ask on other tracerdata than the current, it is possible to learn filenames of previously done traces, under the circumstances that they have not been removed.</p>
- </desc>
- </func>
- <func>
- <name>fetch_log(LogSpecList,DestDir,Prefix) -> {ok,NodeResults} | {error,not_distributed} | {error,Reason} </name>
- <name>fetch_log(DestDir,Prefix) -> {ok,NodeResults} | {error,not_distributed} | {error,Reason}</name>
- <fsummary>Fetch log files to control component node</fsummary>
- <type>
- <v>DestDir = string()</v>
- <v>Prefix = string()</v>
- <v>LogSpecList = [LogSpec]</v>
- <v>&nbsp;LogSpec = {Node,FileSpecList} | Node | {Node,TracerData}</v>
- <v>TracerData = see init_tracing/1,/2</v>
- <v>FileSpecList = [{trace_log,Dir,FileList},{ti_log,Dir,FileList}] | [{trace_log,Dir,FileList}]</v>
- <v>&nbsp;FileList = [RemoteFileName]</v>
- <v>NodeResult = {Conclusion,ResultFileSpec} | no_log | {error,NReason}</v>
- <v>&nbsp;NReason = own_node | Reason</v>
- <v>&nbsp;Conclusion = complete | incomplete</v>
- <v>&nbsp;ResultFileSpec = [{trace_log,FileResults},{ti_log,FileResults}]</v>
- <v>&nbsp;&nbsp;FileResults = [FileResult]</v>
- <v>&nbsp;&nbsp;&nbsp; FileResult = {ok,FileName} | {error,FReason}</v>
- <v>&nbsp;&nbsp;&nbsp;&nbsp;FReason = {file_open,{posix(),FileName}} | {file_open,{posix(),RemoteFileName}} | {file_open,{posix(),[DestDir,Prefix,RemoteFileName]}} | {file_write,{posix(),FileName}} | {truncated,FileName} | {truncated,{Reason,FileName}}</v>
- <v>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;posix() = atom()</v>
- </type>
- <desc>
- <p>Copies log files over distributed erlang to the control component node. This function can only be used in a distributed system.</p>
- <p>The resulting transferred files will have the prefix <c>Prefix</c> and will be located in <c>DestDir</c>. The source files can either be pointed out using a <c>FileListSpec</c> or tracerdata. If no files are explicitly specified, current tracerdata for that node will be used. Note that if source files have the same name (on several nodes) they will overwrite each other at <c>DestDir</c>.</p>
- </desc>
- </func>
- <func>
- <name>delete_log(Nodes,TracerData) -> {ok,NodeResults} | {error,Reason}</name>
- <name>delete_log(NodeSpecList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>delete_log(Spec) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>delete_log(TracerData) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>delete_log() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Delete log files associated with tracerdata</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>NodeSpecList = [{Node,Spec}]</v>
- <v>&nbsp;Spec = [AbsPathFileName] | LogSpecs</v>
- <v>&nbsp;&nbsp;LogSpecs = [LogSpec]</v>
- <v>&nbsp;&nbsp;&nbsp;LogSpec = {trace_log,Dir,[FileNameWithoutPath]} | {ti_log,Dir,[FileNameWithoutPath]}</v>
- <v>TracerData -- see init_tracing/1,/2</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {ok,no_log} | {ok,LogInfos} | {ok,FileInfos}</v>
- <v>&nbsp;LogInfos = [LogInfo]</v>
- <v>&nbsp;&nbsp;LogInfo = {trace_log,FileInfos} | {ti_log,FileInfos}</v>
- <v>&nbsp;FileInfos = [FileInfo]</v>
- <v>&nbsp;&nbsp;FileInfo = {ok,FileName} | {error,Reason} </v>
- </type>
- <desc>
- <p>Deletes listed files or files corresponding to tracerdata. If no tracerdata or list of files are specified in the call, current tracerdata at the runtime components will be used to identify files to delete. All filenames shall be strings.</p>
- <p><c>FileName</c> can either be an absolute path or just a filename depending on if <c>AbsPathFileName</c> or a <c>LogSpec</c> was used to identify the file.</p>
- </desc>
- </func>
- <func>
- <name>subscribe() -> ok | {error,Reason}</name>
- <name>subscribe(Pid) -> ok | {error,Reason}</name>
- <fsummary>Subscribe to Inviso events</fsummary>
- <type>
- <v>Pid = pid()</v>
- </type>
- <desc>
- <p>Adds <c>Pid</c> or <c>self()</c> if using <c>subscribe/0</c> to the inviso-event sending list. Note that it is possible to add a pid several times and that the <c>Pid</c> then will receive multiple copies of inviso-event messages.</p>
- <p>All events will be sent to all subscribers in the event sending list.</p>
- <code type="none">
-Event = {inviso_event,ControllerPid,erlang:localtime(),Msg}
- Msg = {connected, Node, {RTtag, {State,Status}}}
- | {disconnected, Node, NA}
- | {state_change,Node,{State,Status}}
- | {port_down,Node,Reason}
- Node = node() | local_runtime
- </code>
- <p>Subscribing to inviso-event may be necessary for a higher layer trace-tool using inviso to follow the runtime components. <c>local_runtime</c> will be used for a runtime component running in a non-distributed environment.</p>
- </desc>
- </func>
- <func>
- <name>unsubscribe() -> ok</name>
- <name>unsubscribe(Pid) -> ok</name>
- <fsummary>Unsubscribe to Inviso events</fsummary>
- <desc>
- <p>Removes <c>Pid</c> (once) from the subscription list.</p>
- </desc>
- </func>
- </funcs>
-</erlref>
-
diff --git a/lib/inviso/doc/src/inviso_as_lib.xml b/lib/inviso/doc/src/inviso_as_lib.xml
deleted file mode 100644
index 1f4961166c..0000000000
--- a/lib/inviso/doc/src/inviso_as_lib.xml
+++ /dev/null
@@ -1,94 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
-
-<erlref>
- <header>
- <copyright>
- <year>2006</year>
- <year>2011</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- 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
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>inviso_as_lib</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <module>inviso_as_lib</module>
- <modulesummary>The Inviso Autostart Utility Library</modulesummary>
- <description>
- <p>The purpose of the Inviso autostart utility library is to facilitate the creation and modification of autostart configuration files used by the standard autostart.</p>
- </description>
- <funcs>
- <func>
- <name>setup_autostart(Node, R, Opts, TracerData, CmdFiles, Bindings, Transl, RTtag) -> ok | {error, Reason}</name>
- <fsummary>Create an autostart configuration file</fsummary>
- <type>
- <v>Node = atom()</v>
- <v>R = int()</v>
- <v>Opts -- see inviso:add_nodes/2,3</v>
- <v>TracerData -- see inviso:init_tracing/1,2</v>
- <v>CmdFiles = [CmdFile]</v>
- <v>&nbsp;CmdFile = string()</v>
- <v>Bindings = [{Var,Val}]</v>
- <v>&nbsp;Var = atom()</v>
- <v>&nbsp;Val = term()</v>
- <v>Transl = [{{M1,F1,Arity}, {M2,F2,{Mt,Ft}}}]</v>
- <v>&nbsp;M1 = F1 = M2 = F2 = Mt = Ft = atom()</v>
- <v>&nbsp;Arity = int()</v>
- <v>RTtag = term()</v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>Creates an autostart configuration file on <c>Node</c>. The name of the file is automatically deducted from consulting the Runtime_Tools configuration parameters at <c>Node</c>.</p>
- <p><c>R</c> is the number of allowed autostarts remaining.</p>
- <p><c>Opts</c> is the options which shall be given to the runtime component. See <seealso marker="inviso#add_nodes/2">inviso:add_nodes/2,3</seealso>.</p>
- <p><c>TracerData</c> is used when initiating tracing on this node. See <seealso marker="inviso#init_tracing/1">inviso:init_tracing/1,2</seealso>.</p>
- <p><c>CmdFiles</c> points out files containing instructions understood by the <c>inviso_autostart_server</c> implementation of an autostart initiator.</p>
- <p><c>Bindings</c> is a list of <c>{Var, Val}</c> tuples, where <c>Var</c> is the name of a variable and <c>Val</c> the actual value of the variable.</p>
- <p><c>Transl</c> means that <c>M1:F1/Arity</c> shall be translated into <c>M2:F2</c>.</p>
- <p><c>RTtag</c> is the incarnation tag of the runtime component. See See <seealso marker="inviso#add_nodes/2">inviso:add_nodes/2,3</seealso>.</p>
- </desc>
- </func>
- <func>
- <name>set_repeat(Node, R) -> ok | {error, Reason}</name>
- <fsummary>Set the repeat parameter in the autostart file</fsummary>
- <type>
- <v>Node = atom()</v>
- <v>R = int()</v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>Sets the repeat parameter in the autostart file at <c>Node</c> without changing any of its other contents. The autostart configuration file must exist.</p>
- <p><c>R</c> is the number of allowed autostarts remaining.</p>
- </desc>
- </func>
- <func>
- <name>inhibit_autostart(Node) -> ok | {error, Reason}</name>
- <fsummary>Set the repeat parameter in the autostart file to 0</fsummary>
- <type>
- <v>Node = atom()</v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>Sets the repeat parameter in the autostart file at <c>Node</c> to 0. Equivalent to <c>set_repeat(Node, 0)</c>.</p>
- </desc>
- </func>
- </funcs>
-</erlref>
-
diff --git a/lib/inviso/doc/src/inviso_chapter.xml b/lib/inviso/doc/src/inviso_chapter.xml
deleted file mode 100644
index b69fb97586..0000000000
--- a/lib/inviso/doc/src/inviso_chapter.xml
+++ /dev/null
@@ -1,362 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
-
-<chapter>
- <header>
- <copyright>
- <year>2006</year><year>2011</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- 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
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>Inviso</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- <file>inviso_chapter.xml</file>
- </header>
-
- <section>
- <p><em>inviso</em>: (Latin) to go to see, visit, inspect, look at.</p>
- <warning>
- <p>The <c>inviso</c> application is deprecated and will be
- removed in the R16 release.</p>
- </warning>
- <title>Introduction</title>
- <p>The Inviso trace system consists of one or several runtime components supposed to run on each Erlang node doing tracing and one control component which can run on any node with available processor power. Inviso may also be part of a higher layer trace tool. See the inviso-tool as an example. The implementation is spread out over the Runtime_tools and the Inviso Erlang/OTP applications. Erlang modules necessary to run the runtime component are located in Runtime_tools and therefore assumed to be available on any node. Even though Inviso is introduced with Erlang/OTP R11B the runtime component implementation is done with backward compatibility in mind. Meaning that it is possible to compile and run it on older Erlang/OTP releases.</p>
- <image file="inviso_users_guide_pic1.gif">
- <icaption>Inviso Trace System Architecture Overview.</icaption>
- </image>
- <p>This document describes the control and runtime components of the Inviso trace system.</p>
-
- <section>
- <title>Underlying Mechanisms</title>
- <p>Inviso is built on Erlang trace BIFs and standard linked in trace-port drivers for efficient trace message logging. This means that Inviso can not co-exist in runtime with any other trace tool using the trace BIFs.</p>
- </section>
-
- <section>
- <title>Trace Recepie</title>
- <p>This is a short step-by-step description of how tracing using Inviso can be done.</p>
- <list type="ordered">
- <item>Start the Inviso control component at any node. Preferably a node that is not participating in the "work" done by your "system". The control component runs independently and normally not linked to any other process. (Prompt 2 in the example below.)</item>
- <item>Add all Erlang nodes to the inviso control component where you want to trace. This is starting runtime components on all involved Erlang nodes. Can include the node where the control component runs as well. Note that the Runtime_Tools application must be running on the nodes where runtime components shall be started. (Prompt 1 and 3 in the example below.)</item>
- <item>Initiate tracing on the added nodes. Initiating tracing means "opening" the output to where trace-messages will be written. Most commonly this is a file. Note that it is not actually necessary to initiate the same tracing on all nodes. It might for instance be wise to use different filenames on different nodes. In the example below tracing is initiated on two nodes. The same node as where the shell is running (<c>node()</c>) and at <c>node2@hurin</c>. Further both "regular" tracing (<c>trace</c>) as well as trace information (<c>ti</c>) are specified for both nodes. (Prompt 4 in the example below).</item>
- <item>If needing pid-to-alias translations, activate meta tracing on the necessary functions. This requires that trace information was specified when initiating tracing. (Prompt 5 in the example below illustrates using pid to locally registered name translations).</item>
- <item>Set trace-patterns on the functions that shall be traced. (Prompt 6 in the example below).</item>
- <item>Set process trace flags on necessary processes. Do not forget to use the <c>timestamp</c> flag in order to be able to merge log files together in chronological order. (Prompt 7 in the example below).</item>
- <item>Run your code. (Prompt 8 in the example below).</item>
- <item>Stop tracing (opposite of initiate tracing) and clear trace-patterns on the nodes. It is actually not necessary to stop tracing on all nodes at once. Nodes no longer of interest can be made to stop tracing before others. (Prompt 9 in the example below stops tracing. Prompt 13 removes all trace flags and trace patterns. Removing trace flags are really not necessary since those will be removed when the runtime components are stopped. Removing trace patterns may many times be necessary to "return" the node to a "clean" state from a trace perspective. Trace patterns are never automatically cleared by the runtime system unless the Erlang module in question is reloaded.)</item>
- <item>If necessary fetch the log files from the various nodes. (Prompt 10 in the example blow).</item>
- <item>Merge and format the log files. (Prompt 12 in the example below).</item>
- <item>Stop the runtime components. This is important if the Erlang nodes are real "live" systems, and will not necessarily be stopped just because the tracing is completed. (Prompt 14 in the example below).</item>
- </list>
- <p>This "recipe" is valid also when tracing in a non-distributed environment. The only difference is that function calls not taking a node-name as argument are used. The runtime component will then of course run on the same node as the control component.</p>
- <p>Simple example illustrating the above listed recipe. It traces on two nodes, node1 where the control component also runs. And node2 which is a remote node from the control components perspective. The example uses a mixture of API-calls specifying what nodes to trace on and API functions working on all added nodes. This is in this example interchangeable since all to the control component known nodes are participating in the same way.</p>
- <pre>
-Eshell V5.5 (abort with ^G)
-(node1@hurin)1><input>application:start(runtime_tools).</input>
-ok
-(node1@hurin)2> <input>inviso:start().</input>
-{ok,&lt;0.56.0>}
-(node1@hurin)3> <input>inviso:add_nodes([node(),node2@hurin],mytag).</input>
-{ok,[{'node1@hurin',{ok,new}},
- {'node2@hurin',{ok,new}}]}
-(node1@hurin)4> <input>inviso:init_tracing( [{node(),[{trace,{file,"tracefile_node1.log"}},{ti,{file,"trace_node1.ti"}}]}, {node2@hurin,[{trace,{file,"tracefile_node2.log"}},{ti,{file,"trace_node2.ti"}}]}]).</input>
-{ok,[{'node1@hurin',{ok,[{trace_log,ok},{ti_log,ok}]}},
- {'node2@hurin',{ok,[{trace_log,ok},{ti_log,ok}]}}]}
-(node1@hurin)5> <input>inviso:tpm_localnames([node(),node2@hurin]).</input>
-{ok,[{'node1@hurin',{{ok,1},{ok,1}}},
- {'node2@hurin',{{ok,1},{ok,1}}}]}
-(node1@hurin)6> <input>inviso:tpl([node(),node2@hurin],code,which,'_',[]).</input>
-{ok,[{'node1@hurin',{ok,[2]}},
- {'node2@hurin',{ok,[2]}}]}
-(node1@hurin)7> <input>inviso:tf(all,[call,timestamp]).</input>
-{ok,[{'node1@hurin',{ok,"/"}},
- {'node2@hurin',{ok,"-"}}]}
-(node1@hurin)8> <input>code:which(ordset).</input>
-non_existing
-(node1@hurin)9> <input>inviso:stop_tracing().</input>
-{ok,[{'node1@hurin',{ok,idle}},
- {'node2@hurin',{ok,idle}}]}
-(node1@hurin)10> <input>inviso:fetch_log([node2@hurin],".","aprefix_").</input>
-{ok,[{'node2@hurin',
- {complete,[{trace_log,[{ok,"aprefix_tracefile_node2.log"}]},
- {ti_log,[{ok,"aprefix_trace_node2.ti"}]}]}}]}
-(node1@hurin)11> <input>inviso:list_logs([node()]).</input>
-{ok,[{'node1@hurin',
- {ok,[{trace_log,".",["tracefile_node1.log"]},
- {ti_log,".",["trace_node1.ti"]}]}}]}
-(node1@hurin)12> <input>inviso_lfm:merge( [{node(),[{trace_log,["tracefile_node1.log"]}, {ti_log,["trace_node1.ti"]}]}, {node2@hurin,[{trace_log,["aprefix_tracefile_node2.log"]}, {ti_log,["aprefix_trace_node2.ti"]}]}],"theoutfile.txt").</input>
-{ok,15}
-(node1@hurin)13> <input>inviso:clear().</input>
-{ok,[{'node1@hurin',{ok,{new,running}}},
- {'node2@hurin',{ok,{new,running}}}]}
-(node1@hurin)14> <input>inviso:stop_nodes().</input>
-{ok,[{'node2@hurin',ok},
- {'node1@hurin',ok}]}
-(node1@hurin)15> </pre>
- </section>
- </section>
-
- <section>
- <title>Incarnation runtime tags</title>
- <p>Incarnation runtime tags are used to identify an incarnation of a runtime component. An incarnation is one "start-up" of a runtime component on a specific Erlang node. The reason why it can sometimes be necessary to examine the incarnation runtime tag is that a user wants to connect, adopt, an already running runtime component. This may be the case if the runtime component has autostarted or because the control component terminated without killing the runtime component. While the user has been out of control of the runtime component it may very well have terminated and been restarted. If it was restarted without the user's knowledge, its incarnation runtime tag has most likely changed. The user can therefore, if the current incarnation runtime tag is not what it is supposed to be, conclude that the runtime component is not "doing" what is expected.</p>
- <p>The runtime tag is set at runtime component start-up. This is either done when it is started manually by a call to <c>inviso:add_nodes/X</c>, or according to a specification in one of the autostart configuration files.</p>
- </section>
-
- <section>
- <title>Runtime component state and status</title>
- <p>A runtime component has a state and a status. The possible states are: <c>new</c>, <c>tracing</c> and <c>idle</c>. A runtime component that is <c>tracing</c> has (possibly) open log files. A <c>new</c> runtime component has no current tracer-data. That is it lacks any history of what it has done just recently. An <c>idle</c> runtime component is no longer tracing. It does therefore have current tracer-data that describes what it did do when it was tracing.</p>
- <p>The status describes if the runtime component is <c>running</c> or suspended. A suspended runtime component may very well be in state <c>tracing</c>. However the point is that it shall not generate any processor load. It will therefore refrain from generating any trace messages.</p>
- </section>
-
- <section>
- <title>The Runtime Meta Tracer</title>
- <p>Meta tracing is a trace mechanism separate from the regular tracing. It is normally used by a trace-tool to learn about function calls made anywhere in an Erlang node. A typical example is that there is a possibility in Inviso to get pids translated to registered name in the final formatted trace-log (for processes having registered names). This is done by meta-tracing on the BIF <c>register/2</c> to learn about all name/pid associations made.</p>
- <p>Meta tracing in Inviso is done by the <c>inviso_rt_meta</c> process, which is part of the runtime component if trace-information, ti, is initiated. See <seealso marker="inviso#init_tracing/1">inviso:init_tracing/1</seealso> for details. The runtime meta tracer opens and controls the so called trace information file. Translations can then be done off-line using the associations logged in the trace information file. Currently the only type of trace information file available is a straight binary file. A wrap-file makes no sense since pid-to-name associations made in the beginning will most likely be lost.</p>
- <p>The runtime meta tracer can also be used to translate pids to own identifiers. The only thing needed is one or several association points in the form of function calls which will only be made if an association is done in the system. The pid and own-identifier must be arguments and/or return values from the same function call.</p>
- <p>The runtime meta tracer can further more be used to achieve side-effects during tracing, like turning tracing on or off.</p>
-
- <section>
- <title>Matching function calls with return values</title>
- <p>It may sometimes be necessary to wait for a meta traced function to return before it can be decided what to do. This may be due to that one piece of information to make the decision is in the arguments to the function, the other in the return value. This kind of logic can be programmed to be executed by the inviso meta tracer. In order for the inviso meta tracer to "remember" function-call arguments until the function return trace message arrives, a <c>public loop data structure</c> is implemented. The public loop data structure is first created when tracing is initiated (of course only when trace information is specified in the <em>init_tracing</em> call). The public loop data can then later be further initiated each time meta tracing (<c>tpm</c> and <c>tpm_ms</c>) is activated for a certain function.</p>
- <p>The default public loop data structure is a tuple of size two. The first element in that tuple is used by the predefined meta tracing for capturing locally registered names. The second element is free to use for any other purpose. The elements of the tuple must in the default implementation be lists of tuples. Where each sub-tuple shall represent one waiting call. The last element of that tuple must be a now-stamp (as returned by the BIF <c>now/0</c>). See below for an explanation of the now-stamp. The size of the outer most tuple may be increased as long as the term residing in the first element is left unchanged, and all other elements follow the above described rules.</p>
- <p>The inviso meta tracer "cleans" the public loop data structure approximately once every minute. The reason for this is that entries in the public loop data structure may become abandoned. If for instance a process crashes while executing the body of a meta traced function, no return value will be generated. Or in other words, receiving the call meta trace-message can have caused information to have been written into the public loop data structure. That entry will be used and removed when the return_trace meta trace-message arrives. But if the meta traced function causes an exception, no return_trace message will come. The function which normally removes the entry is then therefore never called.</p>
- <p>The default clean-function assumes that every item in the public loop data tuple is a list. Where each list contains tuples where the last element of those tuples are "now-stamps". The default clean-function considers an entry older than 30 seconds to be abandoned.</p>
- </section>
-
- <section>
- <title>Making pid/alias entries in the ti-file</title>
- <p>When activating meta tracing for a function for the purpose of writing pid-alias associations in the trace information file, a call-func and possibly also a return-func is specified. These functions will be called when a meta trace message arrives to the inviso meta tracer as a result of function calls or returns for this meta traced function. What exactly to write in the trace information file is dictated by the merge mechanism. This since pid-alias translations are done off line when merging log-files. See the chapter on merging and formatting log files for more details.</p>
- <p>Simple example where the call to the function <c>connection:assoc_id(Pid,Ref)</c> will associated <c>Pid</c> with the id <c>Ref</c>. We will then in a merged log-file see a translation between <c>Pid</c> and <c>Ref</c>. Actually for all future since there is no unalias function meta traced in this example. The inviso meta tracer will receive a meta trace message every time <c>connection:assoc_id/2</c> is called. When that message arrives the meta tracer will call <c>mytrace:call_assoc_id/3</c> which must return <c>{ok,NewPublicLoopData,OutPutBinary}</c>.</p>
- <code type="none">
- -module(mytrace).
-
- call_assoc_id(_CallingPid,[Pid,Ref],PublLoopData) ->
- {ok,PublLoopData,term_to_binary({Pid,Ref,alias,now()})}.
- </code>
- <pre>
-
-(node1@hurin)21> <input>inviso:tpm(connection,assoc_id,2,[], {mytrace,call_assoc_id}).</input>
-{ok,[{'node1@hurin',{ok,1}},
- {'node2@hurin',{ok,1}}]}
-(node1@hurin)22> </pre>
- </section>
-
- <section>
- <title>Extending the public loop data structure.</title>
- <p>It is of course very likely that the public loop data structure must be extended to host all functions where the meta tracer must delay its action until the function in question returns. What is necessary is to create your own public loop data structure at trace initialization. This is done by using the <c>TiSpec</c>. <c>TiSpec={InitMFA,RemoveMF,CleanMF}</c>, where <c>InitMFA</c> creates the structure, <c>RemoveMF</c> removes it (must often not necessary unless a database, file or similar is used as storage instead of a tuple). <c>CleanMF</c> is the function which will be called each every 60 seconds to go over the public loop data structure. Following the below rules, not much programming will be needed, apart from the <c>InitMFA</c>:</p>
- <list type="bulleted">
- <item>Make the public loop data structure a tuple of lists, where each list is a list of tuples where the tuples represents one entry.</item>
- <item>Make the <c>CallFunc</c> (the function called each time a call meta trace message arrives for the function in question) add a tuple to the correct list where the last element of that tuple is a now-stamp.</item>
- <item>Make sure that the first element in the loop data structure tuple is left alone for the default implementation of the handling of registered names.</item>
- <item>Use <c>inviso_rt_meta:clean_std_publld/1</c> (which is exported for this purpose) as <c>CleanMF</c>. This function is normally the default clean function, if not using the possibility to in detail initiate the inner workings of the inviso meta tracer.</item>
- </list>
- <p>Simple example where tracing is initiated with a public loop data structure having 10 places for nine (the locally registered names is mandatory) different functions to be meta traced. Note that the BIF <c>list_to_tuple/1</c> is used as initialization function. And that the Stdlib function <c>lists:duplicate/2</c> is used to create something for the initialization function to work on.</p>
- <pre>
-(node1@hurin)4> <input>inviso:init_tracing( [{node2@hurin,[{trace,{file,"tracefile_node2.log"}}, {ti,{file,"trace_node2.ti",{{erlang,list_to_tuple,[lists:duplicate(10,[])]}, void,{inviso_rt_meta,clean_std_publld}}}}]}]).</input>
-{ok,[{'node2@hurin',{ok,[{trace_log,ok},{ti_log,ok}]}}]}
-(node1@hurin)5> </pre>
- </section>
-
- <section>
- <title>Using the inviso meta tracer to achieve side effects</title>
- <p>Since meta tracing is independent of regular tracing and catches any function call to a particular function made in any process, it is well suited to be used to turn things on or off during execution. That trick is done by letting the <c>CallFunc</c> and (if used) <c>ReturnFunc</c> do these sideeffects. One must of course remember that the inviso meta tracer is a process amongst all other processes in the system. Meaning that the side effect is not necessarily done exactly when the meta traced function is called. Unless the side effect can be achieved using a match specification action.</p>
- </section>
- </section>
-
- <section>
- <title>Runtime Component Autostart</title>
- <p>In order to trace before any user interaction is possible, an autostart mechanism is implemented. The runtime component is started by the top supervisor of the Runtime_Tools application top supervisor. Hence the Runtime_Tools application must be part of the boot script for autostart tracing to work. The Runtime_Tools applications must of course be started before any application that is to be traced. Do note that application startup is not entirely synchronous. Meaning that just because the application controller has begun starting the next application, Runtime_Tools is not necessarily fully up and running.</p>
- <p>The autostart mechanism is configurable. The runtime component comes with a standard autostart configuration, only missing two text-files to be completely operational.</p>
-
- <section>
- <title>Autostart Configuration</title>
- <p>The autostart is controlled by the Runtime_Tools application configuration parameter <c>inviso_autostart_mod</c>. It must be the name of a module exporting an <c>autostart/1</c> function. The default value is <c>inviso_autostart</c>, a module which is provided with Runtime_Tools. See <seealso marker="#inviso_autostart">below</seealso> for details.</p>
- <p>An <c>autostart/1</c> function must offer the following:</p>
- <code type="none">
-autostart(RuntimeToolsArg) = {MFA,Options,Tag} | any()
- </code>
- <p><c>RuntimeToolsArgs</c> is the argument <c>Arg</c> provided to the Runtime_Tools application through the application resource file <c>{mod,{Module,Arg}}</c> parameter.</p>
- <p><c>MFA = {AutoMod,AutoFunc,AutoArgs} | any()</c> controls how tracing will be initiated. Note that initiating tracing is not necessarily the same as starting a runtime component. It is possible to have a runtime component without doing any tracing. The runtime component is started as long as <c>autostart/1</c> returns the proper tuple, and <c>Options</c> does not for instance require a certain non-existing control component. If it is not a proper tuple or there are other faults in the tuple items, the autostart will terminate. Typically will this happen if there is no <c>autostart/1</c> function.</p>
- <p>If MFA does not properly point out a function possible to call with <c>spawn(AutoMod,AutoFunc,AutoArg)</c>, there will simply be no initialization. (Initialization is done by a separate process spawned by the runtime component during autostart.) It may be worth reminding that <c>AutoMod</c> must be present at the node where the runtime component is supposed to run. Not necessarily the node where the control component usually runs.</p>
- <p><c>Options</c> is the list of options given to the runtime component. See <c>Options</c> in <seealso marker="inviso#add_nodes/2">inviso:add_nodes/2</seealso>.</p>
- <p><c>Tag</c> is the runtime component incarnation tag. See <c>Tag</c> in <seealso marker="inviso#add_nodes/2">inviso:add_nodes/2</seealso>.</p>
- </section>
-
- <section>
- <title>The Standard Autostart Implementation</title>
- <p>As mentioned above, Inviso comes with a complete implementation of autostart sufficient for most situations.</p>
-
- <section>
- <marker id="inviso_autostart"></marker>
- <title>inviso_autostart</title>
- <p>The default autostart module is <c>inviso_autostart</c>, provided as part of the Runtime_Tools application. Since that name is the default module name, it is not really necessary to set the <c>inviso_autostart_mod</c> configuration parameter for the Runtime_Tools application.</p>
- <p>Its <c>autostart/1</c> function reads a configuration file pointed out by the Runtime_Tools application configuration parameter <c>inviso_autostart_conf</c>. If the parameter is not present, a default file, <c>inviso_autostart.config</c> in the current working directory, will be consulted.</p>
- <p>The config file must be an ascii text file with one or more tuples ended with a dot. The following parameters are recognized:</p>
- <taglist>
- <tag><c>{repeat,N}</c></tag>
- <item>
- <p>Optional parameter where <c>N</c> specifies the maximum remaining autostarts. The autostart functionality will rewrite the configuration decreasing <c>N</c> if present. If <c>N==0</c> the autostart will be terminated.</p>
- </item>
- <tag><c>{mfa,{M,F,Args}}</c></tag>
- <item>
- <p>Optional parameter controlling how initialization shall be done. The control component will spawn a separate process to do the initializations by doing <c>spawn(M,F,Args)</c>.</p>
- </item>
- <tag><c>{options,Options}</c></tag>
- <item>
- <p>Optional parameter specifying the options for the runtime component itself. See <c>Options</c> in <seealso marker="inviso#add_nodes/2">inviso:add_nodes/2</seealso>.</p>
- </item>
- <tag><c>{tag,Tag}</c></tag>
- <item>
- <p>Optional parameter specifying the runtime component tag. If missing the default tag will be <c>default_tag</c>.</p>
- </item>
- </taglist>
- <p>Example:</p>
- <code type="none">
-{repeat,1}.
-{mfa,{inviso_autostart_server,
- init,
- [[{tracerdata,{file,"mylogfile"}},
- {cmdfiles,["a_trace_case.txt"]},
- {bindings,[{'M',mymod},{'F','_'},{'Arity','_'}]},
- {translations,[]}]]}}.
- </code>
- <p>The example file results in the start of a runtime component given no specific options. There will only be one autostart since the repeat parameter is set to 1. Tracing will be initiated by the standard initiator (<seealso marker="#autostart_server">inviso_autostart_server</seealso>). The initiator will initiate tracing opening a plain trace-port logfile (<c>"mylogfile"</c>). It will further read the <c>"a_trace_case.txt"</c> file to get instructions on what patterns and flags to set. If there are variables mentioned in the trace-case file <c>"a_trace_case.txt"</c>, it is parameterized, the variables <c>M</c>, <c>F</c> and <c>Arity</c> will get the values according to <c>bindings</c>. There will be no translations done, hence the trace-case file must be written using <c>inviso_rt</c> function calls directly.</p>
- </section>
-
- <section>
- <marker id="autostart_server"></marker>
- <title>inviso_autostart_server</title>
- <p>To further facilitate the standard autostart implementation a default initiator is implemented. To use it, simply specify it as mfa in the config file read by the standard autostart module.</p>
- <p>Its <c>init/1</c> function takes one argument on the form of a list of tuples. The following tuple-parameters are recognized:</p>
- <taglist>
- <tag><c>{tracerdata,TracerData}</c></tag>
- <item>
- <p>Specifies how tracing is initiated. See <seealso marker="inviso#init_tracing/1">inviso:init_tracing/1</seealso> for details on <c>TracerData</c>.</p>
- </item>
- <tag><c>{cmdfiles,ListOfFileNames}</c></tag>
- <item>
- <p>Specifies trace-case files which shall be executed to set the patterns and flags of the trace. See the <seealso marker="#trace_cases">Trace Cases</seealso> chapter for more details. The files will be executed in the order specified.</p>
- </item>
- <tag><c>{translations,Translations}</c></tag>
- <item>
- <p>Optional parameter specifying how functions in trace-case files shall be translated. This is useful since trace-cases can be written for higher-layer Inviso tools, but must during an autostart execute using <c>inviso_rt</c> function calls only.</p>
- <code type="none">
-Translations=
- [{{Mod1,Func1,Arity},
- {Mod2,Func2,{TranslMod,TranslFunc}}},...]
-TranslMod:TranslFunc(ListOfOrigArgs)->
- ListOfTransformedArgs
- </code>
- <p><c>Mod1:Func1/Arity</c> specifies the function that shall be translated into <c>Mod2:Func2/Arity</c>. The actual arguments will be translated with <c>TranslMod:TranslFunc/1</c>. The translation function shall take a list of the actual arguments, and return a list of new arguments. The return-value list may for instance have certain arguments removed, if such are not relevant to the <c>Func2</c> function. (Such arguments must actually be removed since the return-value list from the translation function must have the correct amount of elements corresponding to the arity of <c>Func2</c>.)</p>
- </item>
- <tag><c>{bindings,Bindings}</c></tag>
- <item>
- <p><c>Bindings=[{Var,Val}]</c> <br></br>
-<c>Var=atom()</c>, the name of the variable</p>
- <p>Optional parameter specifying the actual values of variables used in the trace-cases. <c>Bindings</c> is a bindings structure as used by functions in the <c>erl_eval</c> module.</p>
- </item>
- </taglist>
- </section>
-
- <section>
- <title>The Standard Autostart Utility Library</title>
- <p>To facilitate creating the configuration file described above, there are functions in a module named <c>inviso_as_lib</c> which can both create new files according to supplied arguments and update existing configuration files.</p>
- <p>The node(s) in question must be running since the functionality in the utility library uses distributed Erlang to access the file system.</p>
- </section>
- </section>
- </section>
-
- <section>
- <title>The Dependency Property</title>
- <p>In order to protect real "live" systems from getting a runtime component lingering around without a control component, a dependency property can be specified at runtime component start-up. The property specifies a dependency in milliseconds. Meaning that if the property is set to 0 (zero), the runtime component will terminate immediately if its current control component terminates.</p>
- <p>If a control component tries to start a runtime component at an Erlang node where there already is a runtime component, the control component will adopt the already existing runtime component if it has no current control component. Otherwise the control component will experience an error, not being able to start a runtime component at that node.</p>
- <p>It must also be noted that an autostart runtime component is running without control component, at least before any control component adopts it.</p>
- </section>
-
- <section>
- <title>Overload Protection</title>
- <p>Since Inviso is intended to be used on real "live" systems, it is possible to protect the system against overload, having Inviso suspend tracing should an overload situation occur.</p>
- <p>What indicates an overload situation must be programmed and configured outside of Inviso. Inviso can initiate an overload protection, call an overload function periodically and clean-up an overload mechanism should it decide to terminate.</p>
- <p>Internally inside the runtime component, suspending tracing means removing all process trace flags and meta patterns. Reactivating tracing is outside the scoop of Inviso, but can be implemented in a tool using Inviso.</p>
- <p>Simple example adding a runtime component and making it protect its Erlang node from overload.</p>
- <pre>
-inviso:add_node(my_rt_tag,
- [{overload,{{my_ovl,check},
- 15000,
- {my_ovl,start,[my_port_pgm]},
- {my_ovl,stop,[my_port_pgm]}}}]).
- </pre>
- <p>Immediately when the runtime component is started, it will initiate overload protection by calling <c>my_ovl:start(my_port_pgm)</c>. When tracing (not when in state idle or new), the runtime component will every 15000 milliseconds call <c>my_ovl:check/1</c>. Depending on its return value, the runtime component will either do nothing or suspend tracing. When the runtime component is stopped, <c>my_ovl:stop(my_port_pgm)</c> will be called.</p>
- </section>
-
- <section>
- <title>Merging and Formatting Logfiles</title>
- <p>If logging trace messages to a logfile has been used (decided when tracing is initiated) the various log files will be located on the different Erlang nodes participating in the trace. The log files must be merged and formatted for the following reasons:</p>
- <list type="bulleted">
- <item>The various log files from the different nodes must be merged into one logfile in chronological order (where trace messages from different nodes will be mixed). If only one Erlang node participated in the trace, this step is obviously not necessary.</item>
- <item>Trace-port log files are on binary format and must in most cases be transformed in some way. This can for instance be to a text-file format or inserted into a database for analysis.</item>
- <item>Use trace information data to translate process identifiers to aliases, both standard Erlang ones (as registered names) as well as own invented.</item>
- </list>
- <p>The first step before any merging can take place is of course to get all log files, including any trace information files to a location where the logfile merger can access them. This can either be done by simply copying the files. However if the file systems on the Erlang nodes are not that easily accessed, there is a <c>fetch_log</c> function implemented in the runtime component. It will transfer log files using distributed Erlang.</p>
- <p>Inviso comes with two Erlang modules, <c>inviso_lfm</c> and <c>inviso_lfm_tpfreader</c>, implementing a standard log file merger and formatter. The log file merger (<c>inviso_lfm</c>) uses a file reader process (implemented in <c>inviso_lfm_tpfreader</c>) to access log entries in parallel. It is possible to write your own logfile reader. This is necessary since you may have your own trace-log format and/or own trace information log format. The logfile merger can further more be configured to use your own formatter, customizing what to do with a trace message.</p>
- <p>Trace messages in the log files must of course be time-stamped for the logfile merger to be capable of correctly merging them. This means using the <c>timestamp</c> process trace flag.</p>
- <p>The standard inviso log-file reader understands the following trace information file entries:</p>
- <list type="bulleted">
- <item>
- <code type="none">
-{Pid,Alias,alias,NowStamp} </code>
- </item>
- <item>
- <code type="none">
-{Pid,Alias,unalias,NowStamp} </code>
- </item>
- </list>
- <p>The <c>Pid</c> in an <c>alias</c> entry must always be a proper pid. In an <c>unalias</c> entry it may also be the atom <c>undefined</c>. The latter means that all associations involving <c>Alias</c> shall stop to be valid. The standard inviso log file reader uses the now-stamp to make sure that associations are only used during time periods in the log-file when such are valid.</p>
- </section>
-
- <section>
- <marker id="trace_cases"></marker>
- <title>Trace Cases</title>
- <p>The idea behind trace cases is that someone knowledgeable of a certain system component can write a file specifying the trace-patterns and process trace flags necessary to trace on certain items once and for all. Hence a trace case will most likely be a series of calls to functions setting trace patterns and process trace flags.</p>
- <p>However, the actual Erlang nodes and values of arguments given in the trace function calls can not be static in order for the trace cases to become useful and reusable. A trace case file must therefore be possible to parameterize. Introducing variables that will get their values at the time of trace case execution. It may also be the case that Inviso is used as a component in a higher layer trace tool. Trace cases may therefore be written calling more complex functions than the low level <c>inviso_rt</c> functions which are available to autostart mechanisms. In a matter of fact, the <c>inviso</c> API itself can be considered a higher layer. It addresses multiple nodes at once where the <c>inviso_rt</c> API can only address the local node.</p>
- <p>This results in that for trace cases to be useful there must be a function call translation mechanism and an execution environment capable of handling variable bindings.</p>
- <p>A trace-case is a text ascii file consisting of function calls written as they could have been done in the Erlang shell:</p>
- <code type="none">
-modulename:functionname(arg1,arg3,...).
- </code>
- <p>A trace-case may contain any valid function call, including binding new variables which are used later in the trace-case, but:</p>
- <list type="bulleted">
- <item>No spawn, send or receive.</item>
- <item>No apply or similar (including <c>mod:F(Arg1,Arg2)</c>). This because the variable environment is not available during the translation. Only during execution.</item>
- </list>
- <p>Example: Trace cases are expected to be written to be executed directly in an Erlang shell (by some utility reading a text file on trace case format) calling <c>inviso</c> functions. The translations must then translate <c>inviso</c> function calls to <c>inviso_rt</c> function calls, since <c>inviso</c> is not available in the Runtime_Tools applications. It can not be assumed that any trace tools outside the Runtime_Tools application is available on the nodes. Luckily (!) the <c>inviso_rt</c> API resembles the <c>inviso</c> API very much, apart from that the <c>inviso_rt</c> API does not take a list of nodes as an argument. Therefore in most situations the only transformation necessary is to change from <c>inviso</c> to <c>inviso_rt</c> and remove the first argument to the function call.</p>
- <p>Assume that we have the following trace-case file:</p>
- <code type="none">
-inviso:tpl(Nodes,mymod,'_','_',MS).
-inviso:tf(Nodes,all,[call,timestamp]).
- </code>
- <p>For this to work in an autostart the following translation is needed:</p>
- <code type="none">
-[{{inviso,tpl,5},{inviso_rt,tpl,{erlang,tl}}},
- {{inviso,tf,3},{inviso_rt,tf,{erlang,tl}}}]
- </code>
- <p>Since transforming the arguments from <c>inviso</c> calls to <c>inviso_rt</c> calls is simply removing the first argument, there is no need to program any function to do this. The BIF <c>tl/1</c> can be used directly.</p>
- <p>Further there must be a variable binding for <c>MS</c> when executing the trace-case. It is not necessary to have one for <c>Nodes</c> since that argument is removed from all function calls by the translation.</p>
- </section>
-</chapter>
-
diff --git a/lib/inviso/doc/src/inviso_lfm.xml b/lib/inviso/doc/src/inviso_lfm.xml
deleted file mode 100644
index 70207d0b58..0000000000
--- a/lib/inviso/doc/src/inviso_lfm.xml
+++ /dev/null
@@ -1,93 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
-
-<erlref>
- <header>
- <copyright>
- <year>2006</year>
- <year>2011</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- 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
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>inviso_lfm</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <module>inviso_lfm</module>
- <modulesummary>An Inviso Off-Line Logfile Merger</modulesummary>
- <description>
- <p>Implements an off-line logfile merger, merging binary trace-log files from several nodes together in chronological order. The logfile merger can also do pid-to-alias translations.</p>
- <p>The logfile merger is supposed to be called from the Erlang shell or a higher layer trace tool. For it to work, all logfiles and trace information files (containing the pid-alias associations) must be located in the file system accessible from this node and organized according to the API description.</p>
- <p>The logfile merger starts a process, the output process, which in its turn starts one reader process for every node it shall merge logfiles from. Note that the reason for a process for each node is not remote communication, the logfile merger is an off-line utility, it is to sort the logfile entries in chronological order.</p>
- <p>The logfile merger can be customized both when it comes to the implementation of the reader processes and the output the output process shall generate for every logfile entry.</p>
- </description>
- <funcs>
- <func>
- <name>merge(Files, OutFile) -></name>
- <name>merge(Files, WorkHFun, InitHandlerData) -></name>
- <name>merge(Files, BeginHFun, WorkHFun, EndHFun, InitHandlerData) -> {ok, Count} | {error, Reason}</name>
- <fsummary>Merge logfiles into one file in chronological order</fsummary>
- <type>
- <v>Files = [FileDescription]</v>
- <v>&nbsp;FileDescription = FileSet | {reader,RMod,RFunc,FileSet}</v>
- <v>&nbsp;&nbsp;FileSet = {Node,LogFiles} | {Node,[LogFiles]}</v>
- <v>&nbsp;&nbsp;&nbsp;Node = atom()</v>
- <v>&nbsp;&nbsp;&nbsp;LogFiles = [{trace_log,[FileName]}] | [{trace_log,[FileName]},{ti_log,TiFileSpec}]</v>
- <v>&nbsp;&nbsp;&nbsp;&nbsp;TiFileSpec = [string()] - a list of one string.</v>
- <v>&nbsp;&nbsp;&nbsp;&nbsp;FileName = string()</v>
- <v>&nbsp;&nbsp;RMod = RFunc = atom()</v>
- <v>OutFile = string()</v>
- <v>BeginHFun = fun(InitHandlerData) -> {ok, NewHandlerData} | {error, Reason}</v>
- <v>WorkHFun = fun(Node, LogEntry, PidMappings, HandlerData) -> {ok, NewHandlerData}</v>
- <v>&nbsp;LogEntry = tuple()</v>
- <v>&nbsp;PidMappings = term()</v>
- <v>EndHFun = fun(HandlerData) -> ok | {error, Reason}</v>
- <v>Count = int()</v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>Merges the logfiles in <c>Files</c> together into one file in chronological order. The logfile merger consists of an output process and one or several reader processes.</p>
- <p>Returns <c>{ok, Count}</c> where <c>Count</c> is the total number of log entries processed, if successful.</p>
- <p>When specifying <c>LogFiles</c>, currently the standard reader-process only supports:</p>
- <list type="bulleted">
- <item>one single file</item>
- <item>a list of wraplog files, following the naming convention <c><![CDATA[<Prefix><Nr><Suffix>]]></c>.</item>
- </list>
- <p>Note that (when using the standard reader process) it is possible to give a list of <c>LogFiles</c>. The list must be sorted starting with the oldest. This will cause several trace-logs (from the same node) to be merged together in the same <c>OutFile</c>. The reader process will simply start reading the next file (or wrapset) when the previous is done.</p>
- <p><c>FileDescription == {reader,RMod,RFunc,FileSet}</c> indicates that <c>spawn(RMod, RFunc, [OutputPid,LogFiles])</c> shall create a reader process.</p>
- <p>The output process is customized with <c>BeginHFun</c>, <c>WorkHFun</c> and <c>EndHFun</c>. If using <c>merge/2</c> a default output process configuration is used, basically creating a text file and writing the output line by line. <c>BeginHFun</c> is called once before requesting log entries from the reader processes. <c>WorkHFun</c> is called for every log entry (trace message) <c>LogEntry</c>. Here the log entry typically gets written to the output. <c>PidMappings</c> is the translations produced by the reader process. <c>EndHFun</c> is called when all reader processes have terminated.</p>
- <p>Currently the standard reader can only handle one ti-file (per <c>LogFiles</c>). The current inviso meta tracer is further not capable of wrapping ti-files. (This also because a wrapped ti-log will most likely be worthless since alias associations done in the beginning are erased but still used in the trace-log).</p>
- <p>The standard reader process is implemented in the module <c>inviso_lfm_tpreader</c> (trace port reader). It understands Erlang linked in trace-port driver generated trace-logs and <c>inviso_rt_meta</c> generated trace information files.</p>
- </desc>
- </func>
- </funcs>
-
- <section>
- <title>Writing Your Own Reader Process</title>
- <p>Writing a reader process is not that difficult. It must:</p>
- <list type="bulleted">
- <item>Export an init-like function accepting two arguments, pid of the output process and the <c>LogFiles</c> component. <c>LogFiles</c> is actually only used by the reader processes, making it possible to redefine <c>LogFiles</c> if implementing an own reader process.</item>
- <item>Respond to <c>{get_next_entry, OutputPid}</c> messages with <c>{next_entry, self(), PidMappings, NowTimeStamp, Term}</c> or <c>{next_entry, self(), {error,Reason}}</c>.</item>
- <item>Terminate normally when no more log entries are available.</item>
- <item>Terminate on an incoming EXIT-signal from <c>OutputPid</c>.</item>
- </list>
- <p>The reader process must of course understand the format of a logfile written by the runtime component.</p>
- </section>
-</erlref>
-
diff --git a/lib/inviso/doc/src/inviso_lfm_tpfreader.xml b/lib/inviso/doc/src/inviso_lfm_tpfreader.xml
deleted file mode 100644
index bae40522a3..0000000000
--- a/lib/inviso/doc/src/inviso_lfm_tpfreader.xml
+++ /dev/null
@@ -1,83 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
-
-<erlref>
- <header>
- <copyright>
- <year>2006</year>
- <year>2011</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- 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
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>inviso_lfm_tpfreader</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <module>inviso_lfm_tpfreader</module>
- <modulesummary>Inviso Standard Reader Process to Standard Logfile Merger</modulesummary>
- <description>
- <p>Implements the standard reader process to the standard logfile merger <c>inviso_lfm</c>.</p>
- <p>The reader process reads logfiles belonging to the same set (normally one node) in chronological order and delivers logged trace messages one by one to the output process. Before any trace messages are delivered, the <c>inviso_lfm_tpreader</c> implementation reads the entire trace information file (if in use) and builds a database over pid-to-alias associations.</p>
- <p>The <c>inviso_lfm_tpreader</c> implementation is capable of considering that an alias may have been used for several processes during different times. An alias may also be in use for several pids at the same time, on purpose. If a process has generated a trace message, all associations between that pid and aliases will be presented as the list <c>PidMappings</c> in the message sent to the output process.</p>
- </description>
- <funcs>
- <func>
- <name>handle_logfile_sort_wrapset(LogFiles) -> FileList2</name>
- <fsummary>Sort logfiles in chronological order</fsummary>
- <type>
- <v>LogFiles = [{trace_log, FileList}]</v>
- <v>FileList = FileList2 = [FileName]</v>
- <v>&nbsp;FileName = string()</v>
- </type>
- <desc>
- <p>Only one <c>{trace_log, FileList}</c> tuple is expected in <c>LogFiles</c>, all other tuples are ignored. <c>FileList</c> must:</p>
- <list type="bulleted">
- <item>contain one single file name, or</item>
- <item>a list of wraplog files, following the naming convention <c><![CDATA[<Prefix><Nr><Suffix>]]></c>.</item>
- </list>
- <p>Sorts the files in <c>FileList</c> in chronological order beginning with the oldest. Sorting is only relevant if <c>FileList</c> is a list of wraplogs. The sorting is done on finding the modulo-counter in the filename and not on filesystem timestamps.</p>
- <p>This function is exported for convenience should an own reader process be implemented.</p>
- </desc>
- </func>
- </funcs>
-
- <section>
- <title>The Trace Information File Protocol</title>
- <p>The format of a trace information file is dictated by the meta tracer process. The <c>inviso_lfm_tpfreader</c> implementation of a reader process understands the following trace information entries. Note that the <c>inviso_rt_meta</c> trace information file is on binary format prefixing every entry with a 4 byte length indicator.</p>
- <taglist>
- <tag><c>{Pid, Alias, alias, NowStamp}</c></tag>
- <item>
- <p><c>Pid = pid()</c> <br></br>
-<c>Alias = term()</c> <br></br>
-<c>NowStamp = term()</c>, but in current implementation as
- returned from <c>erlang:now/0</c></p>
- <p>This message indicates that from now on shall <c>Pid</c> be associated with <c>Alias</c>.</p>
- </item>
- <tag><c>{MaybePid, Alias, unalias, NowStamp}</c></tag>
- <item>
- <p><c>MaybePid = pid() | undefined</c> <br></br>
-<c>Alias = term()</c> <br></br>
-<c>NowStamp = term()</c>, see above</p>
- <p>This message indicates that, if <c>MaybePid</c> is a pid, this pid shall no longer be associated with <c>Alias</c>. If it is <c>undefined</c>, all associations with <c>Alias</c> from now shall be considered invalid.</p>
- <p>Also note that there are many situations where <c>unalias</c> entries will be missing. For instance if a process terminates without making explicit function calls removing its associations first. This is seldom a problem unless the pid is reused.</p>
- </item>
- </taglist>
- </section>
-</erlref>
-
diff --git a/lib/inviso/doc/src/inviso_rt.xml b/lib/inviso/doc/src/inviso_rt.xml
deleted file mode 100644
index 3a8e77f65c..0000000000
--- a/lib/inviso/doc/src/inviso_rt.xml
+++ /dev/null
@@ -1,246 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
-
-<erlref>
- <header>
- <copyright>
- <year>2006</year>
- <year>2011</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- 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
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>inviso_rt</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <module>inviso_rt</module>
- <modulesummary>Direct API to the Inviso Runtime Component</modulesummary>
- <description>
- <p>The <c>inviso_rt</c> API is normally only used when programming autostart scripts or similar mechanisms. The reason is that the runtime component is part of the Runtime_tools application and will therefore always be available. But the regular inviso API is part of the Inviso application not necessarily available on the node doing an autostart. It is of course possible to runt a "lean" tracer only using the runtime component manually (i.e not through autostart). The runtime component shall otherwise be controlled through the control component, which is accessed with the <c>inviso</c> API.</p>
- </description>
- <funcs>
- <func>
- <name>init_tracing(TracerData) -> NodeResult | {error,Reason}</name>
- <fsummary>Initiate tracing</fsummary>
- <desc>
- <p>See <seealso marker="inviso#init_tracing/2">inviso:init_tracing/2</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>tp(Mod,Func,Arity,MatchSpec,Opts) -></name>
- <name>tp(Mod,Func,Arity,MatchSpec) -> NodeResult | {error,Reason}</name>
- <name>tp(PatternList) -> NodeResult | {error,Reason}</name>
- <fsummary>Set global trace patterns</fsummary>
- <type>
- <v>Mod,Func = atom() | '_' | ModRegExp | {DirRegExp,ModRegExp}</v>
- <v>&nbsp;ModRegExp = regexp_string()</v>
- <v>&nbsp;DirRegExp = regexp_string()</v>
- <v>Arity = int() | '_'</v>
- <v>MatchSpec = true | false | [] | matchspec()</v>
- <v>PatternList = [Pattern],</v>
- <v>&nbsp;Pattern = {Mod,Func,Arity,MatchSpec,Opts}</v>
- <v>Opts = [Opt]</v>
- <v>&nbsp;Opt = only_loaded</v>
- <v>NodeResult = {ok,[Ans]} | {error,Reason}</v>
- <v>&nbsp;&nbsp;Ans = int() | {error,Reason}</v>
- </type>
- <desc>
- <p>Set global trace patterns. The integer replied if the call was successfull describes the number of matched functions. Using wildcards follows the rules for wildcards of <c>erlang:trace_pattern</c>. It is for instance illegal to specify <c>M=='_'</c> while <c>F</c> is not <c>'_'</c>.</p>
- <p>Modules can also be specified using Erlang regular expressions as described in the <c>regexp</c> module. If <c>{DirRegExp,ModRegExp}</c> is used, module selection will further be restricted by that the module must be loaded from a location containing <c>DirRegExp</c> somewhere in the path. This can be used to for instance trace on all modules belonging to a certain application.</p>
- </desc>
- </func>
- <func>
- <name>tpl(Mod,Func,Arity,MatchSpec) -></name>
- <name>tpl(Mod,Func,Arity,MatchSpec,Opts) -> NodeResult | {error,Reason}</name>
- <name>tpl(PatternList) -> NodeResult | {error,Reason}</name>
- <fsummary>Set local trace patterns</fsummary>
- <desc>
- <p>See <seealso marker="#tp/5">tp/N</seealso> function above for details on arguments and return values.</p>
- <p>Set local trace pattern on specified functions.</p>
- </desc>
- </func>
- <func>
- <name>ctp(Mod,Func,Arity) -> NodeResult | {error,Reason}</name>
- <fsummary>Clear global trace patterns</fsummary>
- <desc>
- <p>See <seealso marker="#tp/5">tp/N</seealso> for argument descriptions.</p>
- <p>Clear global trace patterns.</p>
- </desc>
- </func>
- <func>
- <name>ctpl(Mod,Func,Arity) -> NodeResult | {error,Reason}</name>
- <fsummary>Clear local trace patterns</fsummary>
- <desc>
- <p>See <seealso marker="#tp/5">tp/N</seealso> for argument description.</p>
- <p>Clear local trace patterns.</p>
- </desc>
- </func>
- <func>
- <name>tf(PidSpec,FlagList) -> NodeResult | {error,Reason}</name>
- <name>tf(TraceConfList) -> NodeResult | {error,Reason}</name>
- <fsummary>Set process trace flags</fsummary>
- <type>
- <v>TraceConfList = [{PidSpec,FlagList}]</v>
- <v>FlagList = [Flag]</v>
- <v>PidSpec = all | new| existing | pid() | locally_registered_name()</v>
- <v>Flag = all process trace flags allowed.</v>
- <v>NodeResult = {ok,[Ans]} | {error,Reason}</v>
- <v>Ans = int() | {error,Reason}</v>
- </type>
- <desc>
- <p>Set process trace flags. The integer returned if the call was successful describes the matched number of processes.</p>
- </desc>
- </func>
- <func>
- <name>ctf(PidSpec,FlagList) -> NodeResult | {error,Reason}</name>
- <name>ctf(TraceConfList) -> NodeResult | {error,Reason}</name>
- <fsummary>Clear process trace flags</fsummary>
- <desc>
- <p>See <seealso marker="#tf/2">tf/1,2</seealso> for arguments and return value description.</p>
- <p>Clear process trace flags.</p>
- </desc>
- </func>
- <func>
- <name>init_tpm(Mod,Func,Arity,CallFunc) -> NodeResult | {error,Reason}</name>
- <name>init_tpm(Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> NodeResult | {error,Reason}</name>
- <fsummary>Initialize meta tracing</fsummary>
- <type>
- <v>Mod = Func = atom()</v>
- <v>Arity = int()</v>
- <v>NodeResult = ok | {error,Reason}</v>
- <v>InitFunc = RemoveFunc = {Module,Function} | function()/4 | void</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#init_tpm/4">inviso:init_tpm/5,7</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>tpm(Mod,Func,Arity,MS) -> NodeResult | {error,Reason}</name>
- <name>tpm(Mod,Func,Arity,MS,CallFunc) -> NodeResults | {error,Reason}</name>
- <name>tpm(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> NodeResults | {error,Reason}</name>
- <fsummary>Activate meta tracing</fsummary>
- <type>
- <v>Mod = Func = atom() =/= '_'</v>
- <v>Arity = int()</v>
- <v>MS = match_spec()</v>
- <v>InitFunc = CallFunc = ReturnFunc = RemoveFunc = {Module,Function} | function()</v>
- <v>NodeResult = {ok,1} | {ok,0} | {error,Reason}</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#tpm/4">inviso:tpm/4,5,8</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>tpm_tracer(Mod,Func,Arity,MS) -> NodeResult | {error,Reason}</name>
- <name>tpm_tracer(Mod,Func,Arity,MS,CallFunc) -> NodeResults | {error,Reason}</name>
- <name>tpm_tracer(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> NodeResults | {error,Reason}</name>
- <fsummary>Activate meta tracing</fsummary>
- <desc>
- <p>See inviso:tpm_tracer/4,5,8 for details.</p>
- </desc>
- </func>
- <func>
- <name>tpm_ms(Mod,Func,Arity,MSname,MS) ->d NodeResult | {error,Reason}</name>
- <fsummary>Add match specifications</fsummary>
- <type>
- <v>Mod = Func = atom()</v>
- <v>Arity = int()</v>
- <v>MSname = term()</v>
- <v>MatchSpec = [match_spec()]</v>
- <v>NodeResult = {ok,1} | {ok,0} | {error,Reason}</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#tpm_ms/5">inviso:tpm_ms/5</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>tpm_ms_tracer(Mod,Func,Arity,MSname,MS) ->d NodeResult | {error,Reason}</name>
- <fsummary>Add match specifications</fsummary>
- <desc>
- <p>See inviso:tpm_ms_tracer/5 for details.</p>
- </desc>
- </func>
- <func>
- <name>ctpm_ms(Mod,Func,Arity,MSname) -> NodeResult | {error,Reason}</name>
- <fsummary>Remove a match specification</fsummary>
- <type>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#ctpm_ms/4">inviso:ctpm_ms/4</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>ctpm(Mod,Func,Arity) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Remove a meta trace pattern</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#ctpm/3">inviso:ctpm/3</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>local_register() ->NodeResult | {error,Reason}</name>
- <fsummary>Set meta trace pattern on <c>register/2</c></fsummary>
- <type>
- <v>NodeResult = {R1,R2}</v>
- <v>R1 = R2 = {ok,0} | {ok,1} | {error,Reason}</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#tpm_localnames/0">inviso:tpm_localnames/0</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>remove_local_register() ->NodeResult | {error,Reason}</name>
- <fsummary>Clear meta trace pattern on <c>register/2</c></fsummary>
- <type>
- <v>NodeResult = {R1,R2} | {error,Reason}</v>
- <v>R1 = R2 = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#ctpm_localnames/0">inviso:ctpm_localnames/0</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>global_register() ->NodeResult | {error,Reason}</name>
- <fsummary>Set meta trace pattern on <c>global:register_name/2</c></fsummary>
- <type>
- <v>NodeResult = {R1,R2} | {error,Reason}</v>
- <v>R1 = R2 = {ok,0} | {ok,1} | {error,Reason}</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#tpm_globalnames/0">inviso:tpm_globalnames/0</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>remove_global_register() ->NodeResult | {error,Reason}</name>
- <fsummary>Clear meta trace pattern on <c>global:register_name/2</c></fsummary>
- <type>
- <v>NodeResult = {R1,R2} | {error,Reason}</v>
- <v>R1 = R2 = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#ctpm_globalnames/0">inviso:ctpm_globalnames/0</seealso> for details.</p>
- </desc>
- </func>
- </funcs>
-</erlref>
-
diff --git a/lib/inviso/doc/src/inviso_rt_meta.xml b/lib/inviso/doc/src/inviso_rt_meta.xml
deleted file mode 100644
index a1e5400ce0..0000000000
--- a/lib/inviso/doc/src/inviso_rt_meta.xml
+++ /dev/null
@@ -1,78 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
-
-<erlref>
- <header>
- <copyright>
- <year>2006</year><year>2009</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- 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
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>inviso_rt_meta</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <module>inviso_rt_meta</module>
- <modulesummary>Direct API to the Inviso Runtime Component's meta tracer</modulesummary>
- <description>
- <p>This module provides a direct API to the inviso meta tracer. These functions are only meant to be used in meta tracing <c>CallFunc</c> and <c>RemoveFunc</c>.</p>
- <p>It can sometimes be necessary to manipulate meta match-patterns from <c>CallFunc</c>s and <c>RemoveFunc</c>s. The problem then is that call-funcs and remove-funcs are meta trace call-backs executed inside the inviso meta tracer's context. Hence making calls to the regular API's manipulating meta trace-patterns will hang the inviso meta tracer!.</p>
- <p>To remedy this problem, a number of useful tpm-functions are available in this API. It must be understood that their actions are local to the Erlang node where they are called.</p>
- </description>
- <funcs>
- <func>
- <name>tpm_ms(Mod,Func,Arity,MSname,MS) -> {ok,0} | {ok,1} | {error,not_initiated}</name>
- <fsummary>Adds a list of match-specs, associated with the name <c>MSname</c>, to <c>Mod:Func/Arity</c>.</fsummary>
- <desc>
- <p>See inviso:tpm_ms/6 for details. Note that this function only effects meta trace-patterns on the Erlang node where the function is called. This also implies that only the local inviso meta tracer's name-database is updated with <c>MSname</c>.</p>
- </desc>
- </func>
- <func>
- <name>tpm_ms_tracer(Mod,Func,Arity,MSname,MS) -> {ok,0} | {ok,1} | {error,not_initiated}</name>
- <fsummary>As tpm_ms_tracer/5 but also adds a <c>{tracer,Tracer}</c>trace flag to the enable-list of every <c>trace</c>in <c>MS</c>.</fsummary>
- <desc>
- <p>See inviso:tpm_ms_ms/6 for details. Note that this function only effects meta trace-patterns on the Erlang node where the function is called. This also implies that only the local inviso meta tracer's name-database is updated with <c>MSname</c>.</p>
- </desc>
- </func>
- <func>
- <name>list_tpm_ms(Mod,Func,Arity) -> [MSname]</name>
- <fsummary>Returns a list of <c>MSname</c>.</fsummary>
- <desc>
- <p>Returns a list of all <c>MSname</c> in use for <c>Mod:Func/Arity</c>. This can be useful instead of having to have an own-implemented database over currently in use meta match-functions for a particular function.</p>
- </desc>
- </func>
- <func>
- <name>ctpm_ms(Mod,Func,Arity,MSname) -> ok</name>
- <fsummary>Removes the list of match-specs associated with the <c>MSname</c>from the meta trace-pattern of <c>Mod:Func/Arity</c>.</fsummary>
- <desc>
- <p>See inviso:ctpm_ms/5 for details. Note that this function only effects meta trace-patterns on the Erlang node where the function is called. This also implies that only the local inviso meta tracer's name-database is updated with <c>MSname</c>.</p>
- </desc>
- </func>
- <func>
- <name>get_tracer() -> Tracer</name>
- <fsummary>Returns the pid or port acting as regular tracer.</fsummary>
- <type>
- <v>Tracer = pid() | port()</v>
- </type>
- <desc>
- <p>Returns the pid or port acting as the receiver of regular trace messages. This is useful if it is necessary to manipulate meta trace-patterns by hand (using <c>erlang:trace_pattern/3</c>) and the <c>{tracer,Tracer}</c> must be used in one of the match-function bodies.</p>
- </desc>
- </func>
- </funcs>
-</erlref>
-
diff --git a/lib/inviso/doc/src/inviso_users_guide_pic1.gif b/lib/inviso/doc/src/inviso_users_guide_pic1.gif
deleted file mode 100644
index a6da9d37de..0000000000
--- a/lib/inviso/doc/src/inviso_users_guide_pic1.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inviso/doc/src/inviso_users_guide_pic1.ps b/lib/inviso/doc/src/inviso_users_guide_pic1.ps
deleted file mode 100644
index fc859cd129..0000000000
--- a/lib/inviso/doc/src/inviso_users_guide_pic1.ps
+++ /dev/null
@@ -1,24489 +0,0 @@
-%!PS-Adobe-2.0 EPSF-2.0
-%%Title: /clearcase/otp/tools/inviso/doc/src/inviso_users_guide_pic1b.ps
-%%Creator: XV Version 3.10a Rev: 12/29/94 - by John Bradley
-%%BoundingBox: -1 161 615 630
-%%Pages: 1
-%%DocumentFonts:
-%%EndComments
-%%EndProlog
-
-%%Page: 1 1
-
-% remember original state
-/origstate save def
-
-% build a temporary dictionary
-20 dict begin
-
-% define string to hold a scanline's worth of data
-/pix 1848 string def
-
-% define space for color conversions
-/grays 616 string def % space for gray scale line
-/npixls 0 def
-/rgbindx 0 def
-
-% lower left corner
--1 161 translate
-
-% size of image (on paper, in 1/72inch coords)
-616.03200 469.00800 scale
-
-% define 'colorimage' if it isn't defined
-% ('colortogray' and 'mergeprocs' come from xwd2ps
-% via xgrab)
-/colorimage where % do we know about 'colorimage'?
- { pop } % yes: pop off the 'dict' returned
- { % no: define one
- /colortogray { % define an RGB->I function
- /rgbdata exch store % call input 'rgbdata'
- rgbdata length 3 idiv
- /npixls exch store
- /rgbindx 0 store
- 0 1 npixls 1 sub {
- grays exch
- rgbdata rgbindx get 20 mul % Red
- rgbdata rgbindx 1 add get 32 mul % Green
- rgbdata rgbindx 2 add get 12 mul % Blue
- add add 64 idiv % I = .5G + .31R + .18B
- put
- /rgbindx rgbindx 3 add store
- } for
- grays 0 npixls getinterval
- } bind def
-
- % Utility procedure for colorimage operator.
- % This procedure takes two procedures off the
- % stack and merges them into a single procedure.
-
- /mergeprocs { % def
- dup length
- 3 -1 roll
- dup
- length
- dup
- 5 1 roll
- 3 -1 roll
- add
- array cvx
- dup
- 3 -1 roll
- 0 exch
- putinterval
- dup
- 4 2 roll
- putinterval
- } bind def
-
- /colorimage { % def
- pop pop % remove 'false 3' operands
- {colortogray} mergeprocs
- image
- } bind def
- } ifelse % end of 'false' case
-
-
-
-616 469 8 % dimensions of data
-[616 0 0 -469 0 469] % mapping matrix
-{currentfile pix readhexstring pop}
-false 3 colorimage
-
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff000000000000
-000000ffffffccccccffffffffffffffffff000000000000ffffffffffffccccccffffff
-000000ffffff000000000000ffffffffffffcccccc000000000000000000000000ffffff
-000000ffffff000000000000ffffffffffffffffff000000000000ffffffccccccffffff
-ffffffffffff000000ffffffffffffffffffccccccffffffffffffffffff000000000000
-000000ffffffccccccffffffffffffffffff000000000000ffffffffffffccccccffffff
-ffffff000000ffffff000000000000ffffffcccccc000000000000ffffffffffffffffff
-ffffff000000cccccc000000000000ffffffffffffffffffffffffffffffcccccc000000
-000000ffffffffffffffffffffffffffffff000000ffffff000000000000ffffffffffff
-ffffffffffff000000000000000000ffffffffffffffffffffffff000000cccccc000000
-000000ffffffffffff000000000000000000000000ffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff000000ffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffff000000
-000000000000ffffffffffff000000ffffffffffffffffff000000ffffffffffff000000
-000000000000ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-000000000000000000ffffffffffff000000000000ffffffffffff000000ffffffffffff
-000000000000000000ffffffffffff000000ffffffffffffffffffffffff000000ffffff
-ffffff000000ffffffffffffffffff000000000000000000ffffffffffff000000ffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000000000000000ffffff
-ffffff000000ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffff000000000000000000000000000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffffffffffffffff000000ffffffccccccffffffffffff000000ffffffffffff
-000000ffffffccccccffffff000000ffffffffffffffffff000000ffffffccccccffffff
-000000ffffffffffffffffffffffff000000ccccccffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffffffffffffffff000000ffffffccccccffffffffffff000000ffffffffffff
-ffffff000000ccccccffffffffffff000000ffffffffffffffffff000000ccccccffffff
-ffffff000000ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffff000000ffffffffffffffffffffffffccccccffffffffffff000000ffffffffffff
-ffffff000000ccccccffffff000000ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000ffffff000000000000000000000000ffffffffffff000000000000ccccccffffff
-ffffff000000ffffff000000000000ffffffccccccffffff000000000000000000000000
-ffffffffffff000000ffffffffffffffffff000000ffffff000000000000ccccccffffff
-000000000000ffffffffffffffffffffffffcccccc000000000000000000ffffffffffff
-ffffffffffffccccccffffffffffffffffff000000000000000000ffffffccccccffffff
-ffffffffffff000000000000ffffffffffffccccccffffffffffff000000ffffff000000
-000000ffffffcccccc000000000000ffffffffffffffffff000000ffffff000000000000
-ffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffff
-ffffff000000cccccc000000000000ffffffffffffffffffffffff000000000000000000
-ffffffffffffffffffffffff000000ffffff000000000000ffffffffffffffffff000000
-000000000000000000ffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff000000ffffff
-000000ffffffffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000000000ffffffffffff000000ffffffffffffffffffffffff000000ffffff
-ffffff000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffff000000ffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-000000000000000000ffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000000000ffffffffffff000000000000000000ffffffffffff000000000000
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffff000000000000000000ffffff
-ffffff000000000000ffffffffffff000000ffffff000000000000000000ffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-000000000000000000ffffffffffff000000ffffffffffff000000ffffffffffffffffff
-000000ffffffffffff000000000000000000ffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000
-000000ffffffffffffffffffffffffffffff000000000000ffffffffffffffffff000000
-000000000000ffffff000000000000000000ffffffffffff000000000000ffffff000000
-000000000000ffffffffffffffffffffffffffffff000000000000ffffffffffffffffff
-ffffff000000000000000000ffffffffffffffffffffffffffffffffffff000000000000
-000000ffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffff
-000000000000000000ffffff000000000000000000ffffff000000000000000000ffffff
-ffffff000000ffffff000000000000ffffffffffffffffffffffffffffffffffff000000
-000000ffffffffffffffffffffffff000000000000000000ffffff000000000000000000
-ffffffffffff000000000000000000ffffffffffffffffff000000000000000000ffffff
-000000000000000000ffffff000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffff000000000000000000000000000000ffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000000000000000000000
-000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffff000000ccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-000000ffffffffffffffffffffffff000000ccccccffffffffffff000000ffffffffffff
-ffffff000000ccccccffffffffffff000000ffffffffffffffffff000000ccccccffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff000000ccccccffffffffffffffffffffffffffffff
-000000ffffffccccccffffffffffff000000ffffffffffffffffff000000ccccccffffff
-ffffff000000ffffffffffffffffff000000ccccccffffff000000ffffffffffffffffff
-ffffff000000ccccccffffff000000ffffffffffffffffffffffff000000ccccccffffff
-ffffff000000ffffffffffffffffff000000ccccccffffff000000ffffffffffffffffff
-ffffffffffffccccccffffff000000ffffffffffffffffff000000ffffffccccccffffff
-000000ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffff000000000000000000ffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff000000
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000000000ffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff
-000000ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000ffffffffffffffffffffffff000000000000ffffff000000ffffffffffff
-000000000000000000ffffff000000000000000000ffffffffffff000000000000ffffff
-ffffff000000000000000000ffffff000000000000000000ffffff000000000000000000
-ffffff000000000000000000ffffffffffffffffff000000000000000000ffffffffffff
-ffffffffffffffffffffffffffffffffffff000000000000000000ffffffffffffffffff
-ffffffffffff000000000000ffffffffffffffffffffffff000000000000000000ffffff
-000000000000000000ffffff000000000000000000ffffff000000ffffff000000000000
-ffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffff
-000000000000000000ffffff000000000000000000ffffffffffff000000000000000000
-ffffffffffffffffff000000000000000000ffffff000000000000000000ffffffffffff
-000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff000000ffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000000000000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc999999ccccccffffffffffff
-ffffff666666999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-999999ccccccffffffffffffffffff666666999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffcccccc999999ccccccffffffffffffffffff666666999999ffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc999999ccccccffffffffffff
-ffffff666666999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-999999999999ffffffffffffffffff999999999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffcccccc999999999999ffffffffffffffffff999999999999ffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc999999999999ffffffffffff
-ffffff999999999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-666666999999ffffffffffffffffff999999999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffcccccc666666999999ffffffffffffffffff999999999999ffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc666666999999ffffffffffff
-ffffff999999999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-666666999999ffffffffffffffffff999999999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999ccccccffffffffffffffffff666666999999ffffff
-ffffffffffff999999666666ffffffffffffffffffcccccc666666ccccccffffffffffff
-ffffff666666999999ffffffffffffffffff999999666666ffffffffffffffffffcccccc
-666666ccccccffffffffffffffffff666666999999ffffffffffffffffff999999666666
-ffffffffffffffffffcccccc666666ccccccffffffffffffffffff666666999999ffffff
-ffffffffffff999999666666ffffffffffffffffffcccccc666666ccccccffffffffffff
-ffffff666666999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-666666ccccccffffffffffffffffff666666999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffcccccc666666ccccccffffffffffffffffff666666999999ffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc666666ccccccffffffffffff
-ffffff666666999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-666666ccccccffffffffffffffffff666666999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffcccccc999999ccccccffffffffffffffffff666666999999ffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc999999ccccccffffffffffff
-ffffff666666999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-999999ccccccffffffffffffffffff666666999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffcccccc999999ccccccffffffffffffffffff666666999999ffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc999999ccccccffffffffffff
-ffffff666666999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-999999999999ffffffffffffffffff999999999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffcccccc999999999999ffffffffffffffffff999999999999ffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc999999999999ffffffffffff
-ffffff999999999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-666666999999ffffffffffffffffff999999999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffcccccc666666999999ffffffffffffffffff999999999999ffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc666666999999ffffffffffff
-ffffff999999999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-666666999999ffffffffffffffffff999999999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffffffff
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffff666666000000333333ffffffffffff999999000000000000cccccc
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffff666666000000333333ffffffffffff999999000000000000cccccc
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffff666666000000333333ffffffffffff999999000000000000cccccc
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333000000333333ffffffffffff999999000000000000ffffff
-ffffffffffff000000000000999999ffffffffffff333333000000333333ffffffffffff
-999999000000000000ffffffffffffffffff000000000000666666ffffffffffff333333
-000000333333ffffffffffff999999000000000000ffffffffffffffffff000000000000
-666666ffffffffffff333333000000333333ffffffffffff999999000000000000ffffff
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffff666666000000333333ffffffffffff999999000000000000cccccc
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffff666666000000333333ffffffffffff999999000000000000cccccc
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffff666666000000333333ffffffffffff999999000000000000cccccc
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffff666666000000333333ffffffffffff999999000000000000cccccc
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffff666666000000333333ffffffffffff999999000000000000cccccc
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffffffffff
-ffffffffffff999999333333ccccccffffffffffffcccccc333333999999ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333999999ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffcccccc333333999999ffffffffffffffffff333333666666ffffff
-ffffffffffff999999333333ccccccffffffffffffcccccc333333999999ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333666666ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffcccccc333333666666ffffffffffffffffff333333666666ffffff
-ffffffffffff999999333333ccccccffffffffffffcccccc333333666666ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333666666ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffcccccc333333666666ffffffffffffffffff333333666666ffffff
-ffffffffffff999999333333ccccccffffffffffffcccccc333333666666ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333666666ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333999999ffffffffffffffffff333333666666ffffff
-ffffffffffff666666333333ffffffffffffffffff999999333333999999ffffffffffff
-ffffff333333666666ffffffffffffffffff666666333333ffffffffffffffffff999999
-333333999999ffffffffffffffffff333333666666ffffffffffffffffff666666333333
-ffffffffffffffffff999999333333999999ffffffffffffffffff333333666666ffffff
-ffffffffffff666666333333ffffffffffffffffff999999333333999999ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ffffffffffffffffff999999
-333333999999ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ffffffffffffffffff999999333333999999ffffffffffffffffff333333666666ffffff
-ffffffffffff999999333333ffffffffffffffffff999999333333999999ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ffffffffffffffffff999999
-333333999999ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffcccccc333333999999ffffffffffffffffff333333666666ffffff
-ffffffffffff999999333333ccccccffffffffffffcccccc333333999999ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333999999ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffcccccc333333999999ffffffffffffffffff333333666666ffffff
-ffffffffffff999999333333ccccccffffffffffffcccccc333333999999ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333666666ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffcccccc333333666666ffffffffffffffffff333333666666ffffff
-ffffffffffff999999333333ccccccffffffffffffcccccc333333666666ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333666666ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffcccccc333333666666ffffffffffffffffff333333666666ffffff
-ffffffffffff999999333333ccccccffffffffffffcccccc333333666666ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333666666ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc333333333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc333333333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999cccccc666666999999666666999999666666333333
-333333333333333333333333333333333333333333333333333333999999666666999999
-666666999999999999ccccccccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffff000000ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffcccccc000000ffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff000000ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffff000000ffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999666666
-666666000000000000333333666666333333666666666666999999666666999999cccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccc666666999999666666
-999999666666666666333333333333000000333333333333999999999999cccccccccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff000000
-000000000000000000ffffffffffff000000000000ffffffffffffffffff000000ffffff
-000000000000ffffffffffffffffff000000000000000000000000ffffffffffff000000
-ffffffffffffffffff000000ffffff000000000000ffffffffffff000000000000ffffff
-ffffffffffffffffffffffff000000000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000000000000000000000ffffffffffffffffff
-000000000000ffffffffffffffffffffffffffffffffffff000000000000ffffffffffff
-ffffffffffffffffff000000ffffffffffffffffffffffff000000000000000000ffffff
-ffffffffffffffffffffffffffffffffffff000000000000000000ffffffffffffffffff
-000000ffffff000000000000ffffffffffffffffffffffff000000ffffff000000000000
-ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffffffffff000000000000000000ffffffffffffffffffffffff000000000000000000
-ffffffffffffffffff000000000000000000000000ffffffffffff000000ffffffffffff
-ffffffffffff000000000000ffffffffffffffffffffffffffffff000000ffffff000000
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666666666333333000000000000333333666666999999
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccccccccc666666666666333333333333
-000000333333333333999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000ffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffff000000000000000000
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000000000
-ffffffffffff000000000000000000ffffffffffff000000000000ffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff000000
-ffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff000000ffffff
-ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff000000ffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffff000000
-000000000000ffffffffffff000000ffffffffffff000000000000000000ffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffff000000000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffff000000000000ffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffff000000000000000000ffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999666666333333333333999999999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccccccccc666666333333000000666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000
-ffffffffffffffffff000000000000000000000000000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000ffffffffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666000000333333
-666666999999ccccccffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff999999666666000000333333666666999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffcccccc000000
-ffffffffffffffffff000000ffffffffffffcccccc000000ffffffffffffffffff000000
-ffffffffffffcccccc000000ffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffffcccccc000000ffffffffffff000000ffffffffffffffffffcccccc000000
-ffffffffffffffffff000000ffffffffffffccccccffffff000000000000ffffffffffff
-ffffffffffffccccccffffffffffffffffff000000000000ffffff000000ccccccffffff
-000000ffffffffffffffffffffffff000000ccccccffffff000000ffffffffffffffffff
-ffffff000000ccccccffffffffffff000000ffffffffffffffffff000000ccccccffffff
-ffffff000000ffffffffffffffffffffffffccccccffffffffffff000000000000ffffff
-000000ffffffccccccffffff000000ffffffffffffffffffffffff000000ccccccffffff
-000000ffffffffffffffffffffffff000000ccccccffffffffffff000000ffffffffffff
-ffffff000000ccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999666666000000666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999666666
-000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff000000ffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666000000666666999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666333333333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffff000000000000000000ffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffff000000ffffff000000ffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000ffffff000000ffffffffffff000000
-ffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff000000ffffff
-ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff000000ffffff
-ffffffffffffffffffffffffffffff000000ffffffffffff000000000000ffffffffffff
-000000000000ffffffffffff000000ffffffffffffffffff000000000000ffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffff000000
-000000ffffffffffffffffff000000ffffff000000ffffffffffff000000ffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333333333999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000ffffff
-ffffffffffffffffff000000000000ffffff000000ffffffffffff000000000000000000
-ffffff000000000000000000ffffffffffff000000000000ffffffffffff000000000000
-000000ffffff000000000000000000ffffff000000000000000000ffffff000000000000
-000000ffffffffffffffffff000000000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffff
-000000000000ffffffffffffffffffffffffffffffffffff000000000000ffffffffffff
-ffffffffffff000000000000000000ffffffffffff000000000000000000ffffffffffff
-ffffffffffffffffffffffffffffff000000000000000000ffffff000000000000ffffff
-000000ffffff000000000000ffffffffffffffffffffffff000000ffffff000000000000
-ffffffffffffffffffffffff000000000000000000ffffff000000000000000000ffffff
-ffffffffffff000000000000000000ffffffffffffffffff000000000000000000ffffff
-000000000000ffffffffffff000000000000ffffffffffff000000000000000000ffffff
-ffffffffffff000000000000ffffffffffffffffffffffff000000000000000000ffffff
-000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333
-666666ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffcccccc666666000000999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000ffffffccccccffffffffffffffffffffffffffffff000000ffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc333333333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999333333666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000
-000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000ffffffffffffffffffffffffffffff000000000000000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333ccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff999999333333666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333ccccccffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffcccccc
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccccccccccccccc
-ffffffffffffffffffcccccc999999ccccccffffffffffffffffffcccccccccccccccccc
-ccccccccccccccccccccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff
-ffffff999999333333333333333333000000000000000000000000000000000000333333
-ffffffffffffffffffffffff000000ccccccffffffffffffffffff333333000000000000
-000000000000000000000000000000000000000000333333ffffffffffffffffffffffff
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999ffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-cccccc999999999999333333ffffffffffffffffffcccccc000000ccccccffffffffffff
-ffffff666666666666666666999999666666cccccc999999ccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccccccccccccccccccccc999999ffffffffffffffffffcccccc
-000000ccccccffffffffffffffffff666666666666666666ccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666666666333333333333000000000000
-000000000000333333666666ffffffffffffffffffffffff999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333333333000000000000000000333333
-666666999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999999999ffffffffffffffffff666666333333333333666666666666cccccccccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999666666
-666666000000000000666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc999999666666333333ccccccffffffffffffffffff
-333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffff999999333333ffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000000000000000ffffffffffffffffff000000000000ffffff
-000000000000ffffff000000000000000000ffffff000000000000000000ffffff000000
-000000000000ffffffffffffffffffffffff000000000000000000000000ffffffffffff
-000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000000000000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999666666333333000000000000000000333333333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffccccccccccccffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333ffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffffffffffffffffffffff000000000000
-ffffffffffff000000ffffff000000ffffffccccccffffff000000ffffffffffffffffff
-ffffff000000ccccccffffffffffff000000ffffffffffffffffffffffffcccccc000000
-ffffffffffffffffff000000ffffffffffffccccccffffffffffffffffffffffffffffff
-000000ffffffccccccffffff000000ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000000000
-000000333333000000999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000333333666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffff
-ffffffffffff000000ffffff000000ffffffffffff000000ffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffff000000000000000000ffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666333333ffffffffffffffffffcccccccccccccccccc
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666333333000000333333666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffff000000ffffff000000ffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffff000000ffffff000000
-ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333ffffffffffffffffffcccccc999999666666333333000000
-666666ffffffffffffffffff999999ccccccffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666333333000000333333999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000000000000000000000000000ffffff000000000000000000
-ffffff000000000000000000ffffff000000000000ffffffffffffffffffffffff000000
-000000000000000000000000ffffff000000000000000000000000ffffffffffffffffff
-000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000000000000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999333333999999333333333333000000000000333333666666999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffcccccc333333666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666ffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff000000000000000000666666666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000000000000000000000000000000000000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffccccccccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333000000333333ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666000000333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999333333000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999ffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333999999
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff333333999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333666666ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999333333333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666333333999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333666666666666000000000000000000666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333000000666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666333333ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666333333
-ccccccffffffcccccc000000000000000000000000000000666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc000000333333999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333333333
-999999ffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffff999999333333333333999999ffffff
-ffffffffffffffffff000000000000000000000000000000000000666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff333333000000666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-666666000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333666666ccccccffffffffffffffffff
-ffffffffffffffffff666666000000000000000000000000666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666333333333333999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666000000666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffff999999000000000000000000000000ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666333333333333999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-666666000000666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc000000000000999999333333333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccccccccc666666333333000000666666
-999999ffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffcccccc999999666666333333000000666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ccccccffffffffffff333333333333ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666000000333333666666ccccccccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999333333333333666666999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999333333333333000000333333333333999999999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999cccccc666666666666
-333333000000000000666666666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999cccccc666666666666333333
-000000333333666666666666999999666666ccccccccccccccccccccccccccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccccccccc
-cccccc999999999999666666999999333333333333000000666666333333999999999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999999999666666999999333333333333333333333333333333000000
-000000000000000000000000000000000000000000000000000000333333333333333333
-333333666666666666666666666666ccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000000000999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666000000666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333666666
-cccccc000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666333333ffffffffffff
-999999000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333000000
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666333333999999ffffffffffffffffff
-333333000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000999999ffffffffffffffffffffffffcccccc
-333333000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff333333999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999333333666666ffffffffffffffffffffffffffffffffffff999999
-666666000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666ccccccffffffffffffffffffffffffffffffffffffffffff333333
-cccccc000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999000000666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333cccccc
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333ffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666333333999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333000000
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333ccccccffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999333333333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333ffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999000000999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333ccccccffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333000000999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666000000666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333ccccccffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffccccccccccccffffffffffffffffff666666999999666666333333
-333333333333333333333333999999ffffffcccccc333333666666999999666666999999
-666666999999666666ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666ffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999999999666666
-666666333333000000000000666666ffffffffffffffffff999999666666999999cccccc
-ccccccccccccccccccccccccffffffffffffffffffcccccc999999666666999999666666
-999999666666999999666666ffffffffffffffffff333333666666666666cccccccccccc
-ffffffffffffffffffffffffffffffffffff999999666666ffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666ffffffffffffffffff333333666666999999
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666999999333333333333
-000000333333666666ffffffffffffffffff333333ccccccffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-cccccc666666333333000000666666999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffff999999000000999999ffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333333333
-333333999999ccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff999999333333666666000000333333333333cccccc
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc999999999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333ccccccffffffffffffcccccc999999333333
-333333000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000
-666666ccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999333333333333666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffff
-cccccc000000ffffffffffff999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-000000000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffff666666000000666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc000000666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000999999ccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffff333333ccccccccccccffffffffffffffffffffffffffffff
-ffffff000000ccccccffffffffffffffffff666666333333333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc000000666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc000000333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffff999999000000666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc000000666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc000000333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc000000ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999333333999999ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff000000000000000000000000
-000000ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-000000000000ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffccccccffffff
-ffffff000000ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffcccccc000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff000000ffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffcccccc666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-000000000000ffffff000000ffffffffffff000000000000000000ffffffffffffffffff
-000000000000ffffffffffffffffff000000000000ffffffffffffffffffffffffffffff
-000000ffffffffffff000000000000000000ffffffffffff000000000000000000000000
-ffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ccccccffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffff000000ffffffffffff
-ffffff000000000000ffffffffffff000000ffffffffffff000000ffffffcccccc000000
-ffffff000000ffffffffffff000000ffffff000000ccccccccccccffffffffffffffffff
-000000999999000000333333333333000000333333000000ffffffffffff333333333333
-666666666666333333999999666666999999666666ccccccffffffffffffffffffffffff
-ffffff000000ccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff000000999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffcccccc
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffff000000ffffff000000ffffff
-ffffff999999999999000000333333000000000000000000666666ffffffffffffffffff
-999999666666999999ccccccccccccccccccccccccccccccffffffffffffffffffcccccc
-999999666666999999666666999999666666999999666666ffffffffffffffffff333333
-666666000000ccccccccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffff999999
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffff000000999999666666666666ffffff
-ffffff333333666666999999999999ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-666666000000333333333333000000333333666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000000000000000ffffffffffff
-000000ffffffffffffcccccc000000000000333333000000666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffccccccccccccffffffffffffcccccc666666999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff333333999999
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff333333333333333333999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffff000000ffffffffffffffffffffffffccccccffffffffffffffffff999999666666
-000000333333333333ccccccffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666000000666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffff333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999333333333333999999ffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999000000666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffcccccc333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999333333333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffccccccccccccffffffffffff999999666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333333333ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffff000000999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffff000000ccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff666666333333
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000666666ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffff333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333333333
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffff666666333333ffffffffffffffffffffffffffffffffffffffffff333333999999
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffff999999333333999999ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000000000000000000000000000ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffff000000000000ffffffffffffffffffffffffccccccffffff
-ffffff333333ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffcccccc000000999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffff000000ffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc000000333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffff000000000000ffffff000000ffffffffffff000000000000
-000000ffffffffffffffffff000000000000ffffffffffffffffff000000000000ffffff
-ffffffffffffffffffffffff000000ffffffffffff000000000000000000ffffffffffff
-000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-333333333333ccccccffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffccccccffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffff000000ffffffffffffffffff000000000000ffffffffffff000000ffffffffffff
-000000ffffffcccccc000000ffffff000000ffffffffffff000000ffffff000000cccccc
-ccccccffffffffffffffffff000000999999000000333333333333000000333333000000
-ffffffffffff333333333333666666999999666666999999666666999999666666cccccc
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff000000999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333000000ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-000000ffffff000000ffffffffffff999999999999000000333333000000000000000000
-666666ffffffffffffffffff999999666666999999cccccccccccccccccccccccccccccc
-ffffffffffffffffffcccccc999999666666999999666666999999666666999999666666
-ffffffffffffffffff333333666666666666ccccccccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999000000999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666000000ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff000000
-999999666666666666ffffffffffff333333666666999999999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666999999333333333333000000333333666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999000000666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666000000ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000ffffffffffff000000ffffffffffffcccccc000000000000333333000000
-666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccccccccffffff
-ffffffcccccc666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666333333ccccccffffffffffffffffffccccccffffff
-ffffffffffff333333999999ffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffff333333333333333333999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff999999666666000000333333333333ccccccffffffffffffffffffffffff
-999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333999999ffffffffffffffffffffffff
-ffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc999999333333333333999999ffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333333333ccccccffffffffffff
-ffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-333333333333666666ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffccccccccccccffffffffffff
-999999666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000666666cccccc
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666000000666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666cccccc
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff000000999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff666666333333333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999000000999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666000000666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc333333333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffff666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333999999ffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-999999333333999999ffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffff000000000000000000000000000000ffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffff000000000000ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffcccccc000000999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333000000
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffff000000ffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffff000000000000ffffff000000
-ffffffffffff000000000000000000ffffffffffffffffff000000000000ffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffff000000ffffffffffff000000
-000000000000ffffffffffff000000000000000000000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff333333333333ccccccffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffccccccffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffffffffffffffff000000000000ffffff
-ffffff000000ffffffffffff000000ffffffcccccc000000ffffff000000ffffffffffff
-000000ffffff000000ffffffffffffffffffffffffffffff000000ffffff000000ffffff
-ffffff000000ffffff000000ffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-000000ffffffffffffffffff000000ffffff000000ffffffffffffffffffffffff000000
-000000000000ffffffffffffffffffffffffffffffffffff000000000000ffffffffffff
-ffffff000000ffffff000000000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff
-000000ffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff000000
-ffffffffffff000000ffffffffffffffffffffffff000000ffffff000000ffffffffffff
-000000ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000000000000000ffffffffffff000000ffffffffffffffffff
-000000000000000000000000000000ffffff000000000000000000ffffffffffff000000
-000000000000ffffffffffffffffffffffffffffff000000000000000000000000000000
-ffffffffffffffffff000000000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666333333ccccccffffff
-ffffffffffffccccccffffffffffffffffff333333999999ffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666000000000000999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333999999
-ffffffffffffffffffffffffffffffffffff333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000000000000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-333333ccccccffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666333333000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000666666ccccccffffff999999666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999333333000000333333ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666ccccccffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff999999666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666ffffffffffffffffffcccccc999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333ccccccffffffcccccc333333333333
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000000000ffffff000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000000000000000
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333000000000000000000
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffff666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff333333999999ffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffff000000ffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff000000ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999000000000000666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc000000000000000000000000000000
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000000000ffffffffffffffffff000000000000000000
-ffffffffffff000000ffffffffffff000000ffffffffffffffffff000000000000ffffff
-ffffff000000000000ffffff000000000000000000ffffffffffff000000000000000000
-ffffff000000000000ffffff000000ffffff000000000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333000000000000
-666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333000000000000000000000000000000
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000ffffff000000ffffffffffff000000ffffffffffff000000
-ffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff000000ffffff
-000000ffffff000000ffffffffffff000000ffffffffffff000000ffffffffffff000000
-ffffffffffff000000000000ffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffccccccccccccffffffffffffffffff999999666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-000000000000000000999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999333333000000000000000000
-000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffff000000ffffffffffffffffff000000
-ffffffffffff000000ffffffffffff000000ffffff000000000000000000ffffff000000
-ffffffffffffffffffffffffffffff000000ffffff000000ffffffffffffffffff000000
-ffffffffffff000000ffffffffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333999999999999666666000000000000000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000333333ffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999333333
-000000666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333333333ccccccffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffff000000ffffff000000ffffffffffff000000ffffff
-ffffff000000ffffffffffff000000ffffffcccccc000000ffffffffffff000000000000
-ffffffffffff000000ffffff000000ffffffffffff000000ffffffffffff000000ffffff
-ffffff000000ffffffffffffffffff000000cccccc000000ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff000000000000000000000000000000000000000000000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffccccccffffffffffffffffffcccccc999999ffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999cccccc999999999999666666999999666666999999333333333333
-333333000000333333333333333333333333333333333333666666999999666666999999
-666666cccccc999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000000000000000ffffffffffff000000000000000000ffffffffffff
-ffffff000000000000ffffff000000000000ffffff000000000000000000ffffff000000
-000000000000ffffffffffff000000000000ffffff000000000000000000ffffffffffff
-ffffff000000ffffffffffffffffff000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333333333000000000000000000000000000000000000666666666666
-ccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccccccccc999999999999333333
-333333000000666666333333666666666666999999666666999999666666cccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccc999999666666999999666666
-666666333333666666333333000000000000666666666666999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666333333ccccccffffff666666000000000000000000000000333333333333333333
-000000000000000000333333333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc333333999999999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999333333333333000000333333333333666666666666cccccc
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc999999999999666666333333000000000000
-333333666666666666ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333000000999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-333333ffffffffffffffffffffffffcccccc333333000000000000666666ffffffffffff
-ffffffcccccc999999666666999999ffffffffffffffffffcccccc333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666000000000000000000666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666000000333333666666ccccccccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999999999333333333333666666999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000ffffff
-666666333333ccccccffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffcccccc333333
-ccccccffffffffffffffffffffffffffffffcccccc666666000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999ffffffffffff
-ffffffffffff000000333333333333999999999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccccccccc999999333333000000000000333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999666666333333000000666666
-999999ffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffcccccc999999333333333333000000666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000333333ffffff
-ffffff999999333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666666666333333000000000000000000000000666666666666
-ccccccccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffff999999666666333333000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999333333333333666666999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666000000
-666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999999999666666999999666666666666999999ffffffffffffcccccc333333
-666666333333999999666666999999666666ccccccccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000666666ffffff
-ffffffffffffcccccc333333333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc666666666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999999999333333333333
-666666000000000000cccccc999999999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-ffffffffffffffffffffffff333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666333333333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999666666000000666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccccccccc999999ffffffffffff999999
-000000666666666666999999666666999999999999ffffffffffffffffffcccccccccccc
-999999cccccc666666999999666666999999333333333333666666ffffffffffffcccccc
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000ccccccffffff
-ffffffffffffffffffffffff999999000000666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666333333666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc999999666666ffffffffffffffffffcccccc666666666666
-ccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666ffffffffffffffffffffffff999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666333333999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666000000333333333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666333333333333333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000000000ccccccffffff
-ffffffffffffffffffffffffffffffffffff666666ccccccffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffcccccc666666000000666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffcccccc333333333333
-000000000000000000333333666666999999999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333000000000000
-333333999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999000000666666
-ccccccffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffff333333333333666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666000000666666999999ffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999333333000000333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccccccccc666666666666000000000000000000ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666000000000000000000333333666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffff
-ffffff999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999000000666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333
-000000666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccccccccffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccccccccccccccffffffffffffffffff
-000000999999ffffffffffffffffffccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999666666333333000000ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333333333ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666000000000000999999ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333333333ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffff666666000000000000666666666666cccccccccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-666666333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000666666666666999999ffffffffffffccccccffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffcccccccccccc
-333333333333ccccccffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffcccccc999999666666333333000000666666ccccccffffffffffffffffff
-ffffffffffff333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999999999666666333333000000000000
-000000333333666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-ccccccffffffffffffffffff666666333333333333999999999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666333333ccccccffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffcccccc000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc000000333333999999ffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffcccccc333333000000999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000333333ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-000000000000999999ffffffffffffffffffffffffffffff999999000000333333666666
-ccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666000000
-000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-ffffffffffffffffff333333999999ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffff666666000000333333999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999666666666666333333ffffffffffffffffffffffff999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666666666000000000000000000333333666666
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999000000666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000
-000000999999ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-333333333333000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333000000000000333333
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccccccccc999999999999333333333333000000666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333000000333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000ffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999666666333333
-000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666000000ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000000000
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999ffffffffffffffffff333333000000333333666666666666
-999999ccccccccccccffffffffffffcccccc666666000000000000000000999999cccccc
-ffffffffffffffffffffffffffffffffffffcccccccccccccccccccccccccccccc666666
-999999ffffffffffffffffff333333333333666666666666ccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666000000666666ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333000000000000333333333333999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffcccccc000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000000000ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000000000999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999999999
-666666333333333333999999333333000000000000000000000000000000000000000000
-000000000000ccccccffffffffffff333333000000333333333333333333333333999999
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333000000333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccccccccc666666666666000000000000000000333333333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccccccccffffffffffffffffffffffff
-000000000000333333666666999999ccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333ffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffcccccc666666999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333666666
-ccccccffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffff666666666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000000000000000999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccccccccc666666666666999999
-ffffffffffffffffffccccccccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999999999333333333333000000000000333333666666666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999000000000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333000000000000333333666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333ffffffffffffffffffccccccccccccccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc999999999999333333333333666666ffffff
-ffffffffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333000000999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000000000999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666000000
-000000000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000000000000000
-666666999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666ffffffffffffffffffcccccc666666666666999999999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffff000000ffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000000000666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333000000000000000000
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-333333000000000000000000333333999999999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333333333000000000000
-000000000000333333666666666666ccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000ccccccffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffcccccc333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999333333ccccccffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffff000000ffffff000000000000ffffff
-ffffff000000ffffff000000000000000000cccccc000000000000000000ffffffffffff
-000000000000cccccc000000000000000000ffffffffffff000000000000000000ffffff
-000000000000ffffff000000000000000000000000ffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc000000333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc000000000000999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666000000000000000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999666666000000333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-cccccc999999666666333333333333000000ccccccffffffffffffffffffcccccccccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000000000000000000000ffffffffffff000000ffffff
-ffffff000000ffffffffffff000000ffffff000000ffffffffffff000000ffffffffffff
-000000ffffffffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff
-ffffff000000000000ffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666000000666666ffffffffffffffffffffffffffffff
-ffffffffffff999999000000000000999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333000000000000333333666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000333333999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffcccccc000000
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff333333999999
-ffffffffffffffffffcccccc666666999999999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff
-ffffff000000ffffffffffff000000000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffff000000ffffff000000ffffffffffffffffff000000ffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666000000ccccccffffffffffffffffffffffffffffff
-ffffffcccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000000000000000999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-ffffffffffffffffffffffff666666666666ccccccccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666000000000000000000333333333333666666999999
-ccccccccccccffffffffffff666666999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000ffffffffffffffffff000000ffffff000000ffffffffffff
-000000ffffffffffff000000ffffff000000ffffffffffff000000ffffffffffff000000
-ffffff000000ffffff000000ffffffffffff000000ffffffffffff000000ffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000000000ccccccffffffffffffffffffffffffffffff
-999999000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333000000000000000000666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-000000333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666333333000000000000000000666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccccccccc666666666666333333000000
-000000000000cccccccccccc333333ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffff666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff000000000000000000ffffff000000000000000000000000000000000000
-000000000000cccccc000000000000000000000000000000ffffffffffffcccccc000000
-000000ffffffffffff000000000000ffffff000000000000000000000000000000ffffff
-000000ffffffccccccffffff000000000000ffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc000000333333ffffffffffffffffffffffffffffffcccccc
-000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666000000
-000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999333333000000000000
-333333000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffff666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999000000666666ffffffffffffffffffffffff999999000000
-000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333000000000000333333
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999333333000000999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-333333000000000000ccccccffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333000000999999ffffffffffffffffff999999000000000000
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666000000000000000000999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffff666666666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333333333666666000000000000999999ffffffffffff999999000000000000666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333000000000000000000666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccccccccffffffffffffffffff999999
-666666999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999333333ffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000000000000000000000666666999999000000000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffff
-ffffffcccccc666666000000000000000000999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333000000000000333333999999999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000000000000000000000000000000000000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333000000000000333333666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666000000000000000000666666999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000000000000000000000000000000000666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000000000999999cccccc666666000000
-000000000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc999999333333000000333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000
-000000ffffffffffffffffff000000000000ffffff000000000000ffffff000000000000
-000000ffffff000000000000000000ffffff000000000000000000ffffffffffffffffff
-ffffff000000000000000000000000ffffffffffff000000000000000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000000000ffffff000000000000
-ffffff000000000000000000000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffccccccccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000000000000000000000000000000000000000ccccccffffffffffff
-ffffffffffffffffffffffffcccccc333333000000000000000000000000000000000000
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666000000000000999999ffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666ffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-000000ffffffffffffffffffffffff000000000000ffffffffffff000000ffffff000000
-ffffffffffffcccccc000000ffffffffffffffffffffffff000000ffffffccccccffffff
-000000ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff000000000000ccccccffffff
-ffffffffffff000000ffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffcccccc000000ffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333000000000000000000000000000000000000000000000000ccccccffffff
-ffffffffffffffffffffffff666666000000000000000000000000000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666000000333333999999ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffff000000
-ffffffffffff000000ffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffff000000000000000000ffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000000000000000000000000000333333666666ccccccffffffffffff
-ffffffffffffffffff999999000000000000000000000000000000333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666000000000000999999ffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999000000333333999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-000000ffffff000000ffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffff000000ffffff000000ffffffffffffffffff000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000000000ffffff000000000000ffffff000000000000
-ffffff000000000000ffffff000000000000000000000000000000ffffffffffffffffff
-000000000000000000000000000000000000ffffffffffff000000000000000000ffffff
-000000000000ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc000000000000333333999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc000000000000000000000000000000000000000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666000000333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666000000000000333333
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000
-000000000000000000ffffff000000000000000000ffffff000000000000000000ffffff
-000000000000ffffffffffffffffffffffff000000000000000000000000000000ffffff
-000000000000000000000000ffffffffffffffffff000000000000000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000000000000000000000ffffff
-ffffffffffffffffff000000000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffff000000000000ffffff000000
-ffffffffffff000000ffffffffffff000000ffffffffffff000000ffffffffffff000000
-ffffffffffff000000ffffff000000ffffffffffff000000ffffffffffff000000ffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccccccccc999999999999666666999999666666333333333333333333333333333333
-333333333333000000333333333333333333333333333333333333333333333333666666
-666666999999333333666666666666666666666666666666666666999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-000000000000000000666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333ffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffff666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333999999ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffffffffff000000ffffffcccccc000000
-ffffffffffff000000ffffffffffff000000ccccccffffff000000ffffff000000ffffff
-ffffffffffff000000ffffff000000ffffff000000ffffffffffffffffff000000ffffff
-ffffff000000ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffcccccc000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999999999333333333333000000
-333333333333666666666666999999666666999999cccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc666666
-999999666666666666333333666666000000000000000000666666666666999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999333333333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff
-ffffff000000ffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff
-ffffff000000ffffff000000ffffffffffff000000ffffffffffff000000ffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666333333000000333333666666666666ccccccccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999999999666666333333
-000000333333666666ccccccccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffccccccffffffffffffffffff999999999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000000000000000000000000000000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000000000ffffff000000ffffffffffff000000000000
-ffffff000000000000ffffff000000000000ffffff000000000000ffffff000000000000
-000000000000000000000000000000ffffff000000000000000000ffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-666666000000666666999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999666666333333666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333333333
-999999ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff999999333333666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffff333333333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffcccccc333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333ccccccffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff999999333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666000000666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000000000333333999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333000000
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000000000333333999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffff999999000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffcccccc
-666666000000333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666000000000000333333
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666333333ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-cccccc666666333333ffffffffffffffffffffffffffffff666666666666000000666666
-ccccccffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffcccccc666666333333666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc333333000000333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffff999999666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666333333999999ffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffcccccc000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff999999
-333333333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666000000999999ffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999333333666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffcccccc666666333333000000666666666666ccccccccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999999999
-333333000000333333666666999999ffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999ffffffffffffffffffccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666000000999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999333333333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffff999999999999333333333333000000333333333333
-666666666666999999999999ccccccccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-cccccccccccccccccc666666999999333333666666333333000000333333666666666666
-ccccccffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc
-999999999999666666666666333333333333333333000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000333333
-333333333333333333999999666666cccccc999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999000000000000
-000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333666666ccccccffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-666666000000000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666000000666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000000000999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666000000333333999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-333333000000333333999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000666666999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999000000333333999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333666666ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666ffffffffffffffffff999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-ccccccffffffffffffcccccc666666999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666000000000000999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333000000999999
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff999999333333333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333000000000000333333
-666666999999999999ffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffcccccc999999999999333333333333000000333333
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999000000666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999666666666666ffffffffffffffffff000000000000000000000000000000000000
-000000000000ffffffffffffffffff333333666666666666ccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666000000666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999333333333333666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffff999999ccccccffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333000000333333999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666000000666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999333333
-333333666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333000000ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333000000000000666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999666666000000333333333333999999
-ccccccffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff999999666666000000333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666000000000000000000000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999000000000000333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc666666
-333333333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccccccccc666666333333000000666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000000000000000000000000000333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666000000333333999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666666666000000000000333333666666666666ccccccccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999999999333333333333
-000000333333333333999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666000000000000000000000000000000999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333000000
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999999999333333333333000000
-333333333333999999666666999999999999cccccc999999ccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccccccccc999999
-cccccc666666999999666666666666333333000000333333666666666666cccccccccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666000000000000000000000000ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-cccccc666666333333ffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-000000000000666666ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccccccccc666666999999666666666666333333333333333333333333000000000000
-000000000000000000000000000000000000000000000000333333333333333333333333
-333333999999666666999999999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000000000000000000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999333333000000666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-333333000000000000ffffffcccccc666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999999999000000999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666333333666666cccccc
-ffffff000000000000ffffffffffff999999000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-000000000000666666ffffffffffffcccccc999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-000000333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666000000999999ccccccffffffffffff
-ffffff000000000000ffffffffffffffffffffffff999999333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffff666666999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666000000000000666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999333333333333999999ffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffff999999000000666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333000000666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333000000333333
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000333333666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333000000000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666333333000000666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333000000333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffccccccffffff666666999999ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff666666999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333000000333333999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666333333666666ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666000000000000ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666000000333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666000000999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666000000000000ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000333333999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999333333333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000000000ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffff999999ccccccffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666000000000000999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-333333666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999000000000000999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffccccccffffffffffffffffffffffffffffff666666999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999000000000000333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666000000999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999000000000000999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333000000
-666666ccccccffffffffffffffffffffffffcccccc333333333333999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc333333000000999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666333333000000666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333666666ffffffffffff666666333333666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-000000000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000999999999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333000000666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-cccccc666666333333ffffffffffffffffffffffffffffff666666333333999999ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffff999999000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999000000000000333333999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-333333333333999999ffffffffffff333333333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffff666666333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666ffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc666666000000999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffff999999666666333333666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc999999333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666ffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333333333999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffcccccc999999
-000000666666999999ccccccffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffcccccc999999333333333333666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666000000333333666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666333333666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666000000333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999000000000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666333333333333333333999999999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc666666
-666666000000333333666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666333333000000666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-666666000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666000000000000999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc000000000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc999999666666666666333333000000333333333333
-333333999999666666cccccc999999cccccc999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999cccccc999999999999666666666666333333333333000000333333333333999999
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333000000000000666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999333333333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666000000333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000000000999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-cccccc666666999999333333666666333333666666000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000333333
-666666333333666666666666999999999999ccccccccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffcccccccccccc333333000000000000
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666333333666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc000000000000666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffcccccc999999
-666666ffffffffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666000000999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc000000000000666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333333333999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333000000333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffccccccccccccffffffffffffffffffcccccc999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666333333666666ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000000000333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000000000
-333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666000000
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000333333999999ffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333000000333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-333333000000000000333333666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999333333333333999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666000000000000999999ccccccffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666333333000000333333666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666333333666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000333333666666
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666666666ffffffffffffffffffffffff
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666000000999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333
-000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000
-000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000000000
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ccccccffffffffffffffffffcccccc666666666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333
-666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000000000999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666333333000000333333666666999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666000000999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffff666666333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666666666000000000000
-000000666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999cccccc999999999999666666999999666666999999666666333333333333333333
-333333333333333333333333333333333333000000333333333333999999666666999999
-999999cccccc999999ccccccccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffccccccffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000666666
-ccccccffffffffffffffffffffffffffffffcccccc000000000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999333333000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc999999666666666666333333000000000000
-333333333333333333333333666666666666666666666666999999999999999999999999
-999999999999999999333333333333333333999999666666666666666666666666666666
-666666333333333333333333333333000000333333333333999999999999cccccccccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffff666666333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333666666ffffffffffffffffffcccccc333333000000666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffcccccc000000999999ffffffcccccc999999
-666666666666333333000000000000333333333333666666666666999999999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999999999000000666666999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc666666999999666666666666333333000000
-000000333333333333999999999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffcccccc666666
-000000333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999cccccc
-999999666666000000666666999999333333000000333333333333333333333333333333
-333333ccccccffffffffffff999999999999666666999999666666999999999999cccccc
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666666666333333333333333333
-666666666666999999cccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccc999999999999666666666666000000333333666666ccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666000000333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccccccccffffffffffff999999000000000000333333666666333333
-666666666666cccccc999999333333666666cccccccccccccccccccccccccccccccccccc
-ccccccccccccffffffffffffcccccc666666999999666666999999666666666666333333
-666666000000333333333333999999ffffffffffffccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999666666333333000000333333666666999999999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999666666333333000000000000666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666000000333333999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999999999666666333333
-000000000000333333999999ffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccccccccccccccffffffffffff999999000000000000333333666666
-666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999333333333333333333999999999999cccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc999999666666
-666666000000333333666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666000000000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffccccccffffffffffffffffff999999666666666666cccccc999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-999999333333000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999000000333333
-666666999999999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999666666000000333333666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999666666000000000000666666ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff333333333333666666999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333333333666666cccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccc999999666666333333333333999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333000000
-666666666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999333333333333333333
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666000000333333999999999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999666666000000666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffccccccffffffffffffcccccc666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000999999999999cccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc666666
-333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffccccccccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333333333666666cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999999999000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff333333000000333333ccccccffffffffffffffffffffffff
-cccccc666666000000333333999999ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffcccccc333333000000666666ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333
-999999cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccc666666333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999666666000000666666ccccccffffff666666
-333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999000000666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333666666cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999666666000000999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333000000333333666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffff999999666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffff999999999999ffffffffffffffffff999999666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffff999999999999ffffffffffff
-ffffff999999666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-999999999999ffffffffffffffffff999999666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffff999999999999ffffffffffffffffff999999666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffff999999999999ffffffffffff
-ffffff999999666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-999999999999ffffffffffffffffff999999666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffff999999999999ffffffffffffffffff999999666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffff999999999999ffffffffffff
-ffffff999999666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-999999999999ffffffffffffffffff999999666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffff999999999999ffffffffffffffffff999999666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffff999999666666ffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333666666999999cccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccc333333333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666ffffffcccccc666666000000
-333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-cccccc666666333333ffffffcccccc000000000000666666ffffffffffff333333000000
-000000ffffffffffffcccccc000000000000ccccccffffffcccccc000000000000666666
-ffffffffffff333333000000000000ffffffffffffcccccc000000000000ccccccffffff
-cccccc000000000000666666ffffffffffff333333000000000000ffffffffffffcccccc
-000000000000999999ffffffcccccc000000000000666666ffffffffffff333333000000
-000000ffffffffffffcccccc000000000000999999ffffffcccccc000000000000666666
-ffffffffffff333333000000000000ffffffffffffcccccc000000000000999999ffffff
-cccccc000000000000666666ffffffffffff333333000000000000ffffffffffffcccccc
-000000000000999999ffffffffffff333333000000666666ffffffffffff333333000000
-000000ffffffffffffcccccc000000000000999999ffffffffffff333333000000666666
-ffffffffffff333333000000000000ffffffffffffcccccc000000000000999999ffffff
-ffffff333333000000666666ffffffffffff333333000000000000ffffffffffffcccccc
-000000000000999999ffffffffffff333333000000666666ffffffffffff333333000000
-000000ffffffffffffcccccc000000000000999999ffffffffffff333333000000666666
-ffffffffffff333333000000000000ffffffffffffcccccc000000000000999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000999999999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999333333999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000666666ccccccffffffcccccccccccc
-666666000000ccccccffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff999999333333666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffff666666333333ccccccffffffffffffcccccc333333
-999999ffffffffffffffffff333333333333ffffffffffffffffff666666333333cccccc
-ffffffffffffcccccc333333999999ffffffffffffffffff333333333333ffffffffffff
-ffffff666666333333ccccccffffffffffffcccccc333333999999ffffffffffffffffff
-333333333333ffffffffffffffffff666666333333ccccccffffffffffffcccccc333333
-999999ffffffffffffffffff333333333333ffffffffffffffffff666666333333cccccc
-ffffffffffffcccccc333333999999ffffffffffffffffff333333333333ffffffffffff
-ffffff666666333333ccccccffffffffffffcccccc333333999999ffffffffffffffffff
-666666333333ffffffffffffffffff666666333333ccccccffffffffffffcccccc333333
-999999ffffffffffffffffff666666333333ffffffffffffffffff666666333333cccccc
-ffffffffffffcccccc333333999999ffffffffffffffffff666666333333ffffffffffff
-ffffff666666333333ccccccffffffffffffcccccc333333999999ffffffffffffffffff
-666666333333ffffffffffffffffff666666333333ccccccffffffffffffcccccc333333
-999999ffffffffffffffffff666666333333ffffffffffffffffff666666333333cccccc
-ffffffffffffcccccc333333999999ffffffffffffffffff666666333333ffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666333333cccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccc999999666666666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666666666cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999666666
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333999999cccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-999999333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999999999333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666999999cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccc999999333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-666666cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999666666333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccc666666999999ffffffffffffffffff666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffccccccccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999333333ccccccffffffcccccc333333999999ffffff
-ffffffffffff666666666666666666ccccccccccccffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffff666666000000999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-666666333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999cccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccc666666999999ffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333333333000000000000000000333333333333999999999999
-cccccc000000000000ffffffffffffffffff999999000000000000666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc000000ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccccccccc666666666666333333000000
-000000000000000000ffffffcccccc999999333333000000000000000000333333cccccc
-ffffffffffffffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666cccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccc666666666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffcccccc000000000000000000000000000000000000000000
-999999ffffffffffffffffffffffffffffff666666666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000999999999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffff000000000000000000000000000000000000
-000000999999ffffffffffffffffffffffff666666ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffcccccc333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333cccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccc333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffcccccc000000000000000000000000666666999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000ffffffffffff000000ffffffffffffffffffffffffffffffffffff000000000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999333333999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffff999999000000666666999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666cccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccc000000cccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccc000000cccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccc333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000ffffffccccccffffff000000ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-000000ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffff000000
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666666666cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc000000cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999000000
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999666666666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000000000000000000000000000ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffff000000000000ffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff000000000000
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffcccccc000000
-000000ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffff666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000000000000000ffffff000000ffffff000000000000
-000000ffffffffffffffffff000000000000000000ffffff000000000000ffffffffffff
-ffffff000000000000ffffffffffff000000000000ffffffffffffffffffffffffffffff
-000000ffffff000000000000ffffff000000000000ffffff000000000000ffffff000000
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666cccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccc000000000000000000cccccccccccccccccc
-000000000000cccccc000000000000cccccc000000000000000000cccccc000000000000
-000000cccccc000000000000000000cccccccccccccccccccccccc000000000000000000
-000000cccccccccccc000000000000000000cccccccccccccccccccccccccccccccccccc
-cccccccccccccccccc000000000000cccccc000000000000cccccc000000000000000000
-000000000000cccccccccccccccccccccccccccccccccccccccccc000000000000000000
-cccccc000000cccccccccccccccccccccccc000000000000000000cccccc000000000000
-000000000000000000cccccccccccccccccccccccc000000000000000000cccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccc333333999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffff000000ffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000000000000000000000ffffffffffff000000000000ffffff000000ffffffffffff
-ffffff000000ffffff000000ffffffffffff000000000000ffffffffffff000000ffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffff000000ffffffffffff000000000000ffffff000000ffffff000000
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc000000cccccc999999cccccc
-999999000000000000cccccc999999000000999999000000999999cccccc999999000000
-999999cccccc999999cccccc000000cccccc999999cccccc000000cccccc999999cccccc
-999999cccccc000000cccccc999999cccccc000000cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc000000000000999999cccccc999999cccccc000000cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc000000cccccc
-000000cccccc000000cccccc999999000000999999cccccc999999000000999999000000
-999999cccccc999999cccccc999999cccccc000000cccccc999999cccccc000000cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999333333999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffff000000ffffff000000ffffffffffffffffff000000000000000000000000ffffff
-ffffff000000000000ffffffffffff000000000000ffffffffffffffffffffffffffffff
-000000ffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff000000
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333cccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccc000000cccccccccccccccccc
-cccccc000000cccccccccccccccccc000000cccccc000000cccccccccccc000000cccccc
-cccccccccccccccccccccccc000000cccccccccccccccccccccccc000000000000000000
-cccccccccccc000000cccccccccccccccccc000000cccccccccccccccccccccccccccccc
-cccccccccccccccccccccccc000000cccccccccccccccccccccccccccccc000000cccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc000000cccccc
-000000cccccc000000cccccccccccc000000000000000000000000000000cccccc000000
-cccccccccccccccccccccccccccccccccccccccccc000000000000000000000000cccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccc333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffff000000000000ffffff000000ffffffffffff000000000000
-000000ffffffffffffffffff000000000000ffffffffffffffffff000000000000ffffff
-ffffffffffffffffffffffff000000ffffffffffff000000000000000000ffffffffffff
-000000000000000000000000ffffffffffffffffff000000000000000000ffffffffffff
-000000000000ffffff000000000000000000ffffff000000000000ffffffffffff000000
-ffffff000000000000ffffffffffff000000000000ffffff000000000000ffffff000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-000000ffffffffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffff000000cccccc000000ffffffffffffffffff000000ffffffffffffccccccffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffff
-000000ffffffcccccc000000ffffffffffff000000ffffffffffff000000cccccc000000
-ffffff000000ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc000000cccccc999999cccccc
-999999000000999999cccccc999999000000999999cccccc000000cccccc000000cccccc
-999999cccccc999999cccccc000000cccccc999999cccccc999999cccccc999999cccccc
-000000cccccc000000cccccc999999cccccc000000cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc000000cccccc999999cccccc999999cccccc000000cccccc
-999999cccccc000000cccccc999999cccccc999999cccccc999999cccccc000000cccccc
-000000cccccc000000cccccc999999000000999999cccccc999999cccccc999999000000
-999999cccccc999999000000999999cccccc000000cccccc999999cccccc000000cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666ffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffff000000ffffffffffffffffff000000000000ffffffffffff000000ffffffffffff
-000000ffffffcccccc000000ffffff000000ffffffffffff000000ffffff000000ffffff
-ffffffffffffffffffffffff000000ffffff000000ffffffffffff000000ffffff000000
-ffffffffffff000000ffffffffffffffffffffffffffffff000000ffffffcccccc000000
-ffffff000000ffffffffffff000000ffffff000000ffffff000000ffffffffffff000000
-000000ffffff000000ffffff000000ffffff000000ffffffffffff000000000000ffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffff666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000ffffffffffffffffff000000000000000000ffffffffffff000000000000
-000000ffffffffffffffffff000000000000000000ffffff000000000000000000ffffff
-000000000000ffffffffffff000000000000ffffffffffffffffffffffffffffff000000
-000000000000000000000000000000000000000000000000ffffff000000000000000000
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666666666cccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccc000000000000000000000000000000cccccc
-000000000000000000cccccc000000000000000000cccccc000000000000cccccccccccc
-cccccccccccc000000000000000000000000000000cccccc000000000000000000000000
-cccccccccccccccccc000000000000000000cccccccccccccccccccccccccccccccccccc
-cccccccccccccccccc000000000000000000000000cccccccccccccccccccccccc000000
-000000000000cccccccccccccccccccccccccccccccccccccccccc000000000000000000
-000000000000000000000000cccccccccccc000000000000000000000000cccccccccccc
-000000000000000000cccccccccccccccccc000000000000000000000000000000000000
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccc999999666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-000000ffffff000000ffffffffffffffffffffffff000000000000000000ffffffffffff
-ffffffffffffffffffffffff000000000000ffffffffffffffffff000000ffffff000000
-000000000000ffffffffffffffffffffffffffffffffffff000000ffffff000000000000
-000000ffffffffffffffffff000000000000ffffffffffffffffffffffffffffff000000
-ffffffffffff000000000000000000000000ffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff000000
-ffffffffffff000000ffffffffffff000000ffffff000000ffffffffffff000000ffffff
-ffffffffffffffffff000000ffffff000000ffffffffffff000000ffffffffffff000000
-ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff000000ffffff
-ffffff000000ffffff000000ffffff000000ffffffffffff000000ffffff000000ffffff
-ffffff000000ffffff000000ffffffffffff000000ffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333999999cccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccc999999666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000ffffffffffff000000ffffffffffffffffff000000000000000000000000
-000000ffffff000000000000000000ffffffffffff000000000000000000ffffffffffff
-ffffffffffffffffff000000000000000000000000000000ffffffffffffffffff000000
-000000000000ffffffffffffffffffffffffffffff000000ffffffffffff000000000000
-000000ffffffffffff000000000000000000000000000000ffffffffffff000000ffffff
-ffffff000000000000000000000000000000ffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff333333000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc000000000000000000000000
-000000000000000000cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999000000000000000000000000000000000000000000999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffff000000cccccc000000ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccc666666666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000
-000000000000ffffffffffffffffff000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999999999333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333999999cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccc333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-999999333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc333333999999cccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccc333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999999999
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333666666cccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc999999333333
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666333333cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999333333333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333999999cccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccc666666333333999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333333333999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999999999333333666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333666666
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccc999999666666333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-000000666666999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999999999333333333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666000000ccccccffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff999999333333333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999333333333333999999cccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc999999
-666666000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc000000666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999000000333333666666cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999666666333333333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999000000666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666000000666666999999cccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccc999999333333333333666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc000000000000999999ffffffcccccc
-666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000ffffffccccccffffff000000ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333000000666666
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999999999333333333333333333999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000000000666666ffffffffffffffffff
-666666333333000000999999ccccccffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffff999999333333000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000000000000000ffffff000000ffffff000000000000
-000000ffffffffffffffffff000000000000000000ffffff000000000000ffffffffffff
-ffffff000000000000ffffffffffff000000000000ffffffffffffffffffffffff000000
-000000ffffff000000000000ffffff000000000000ffffff000000000000000000ffffff
-000000000000ffffff000000000000ffffff000000000000000000000000000000ffffff
-ffffff000000000000000000ffffff000000000000ffffff000000000000000000000000
-000000ffffff000000000000ffffffffffff000000000000000000000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333000000666666666666cccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc999999
-999999333333333333333333999999ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000000000666666ffffffffffffffffffffffff
-ffffffffffffcccccc333333333333666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999000000666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000000000000000000000ffffffffffff000000000000ffffff000000ffffffffffff
-ffffff000000ffffff000000ffffffffffff000000000000ffffffffffff000000ffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-000000000000ffffff000000000000ffffff000000000000ffffffffffffffffff000000
-ffffff000000000000ffffff000000ffffffffffff000000ffffff000000ffffffffffff
-000000ffffffffffffffffff000000ffffff000000000000ffffffffffff000000ffffff
-000000000000ffffff000000ffffff000000ffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666000000000000333333666666999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999999999666666333333000000333333
-666666999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000000000333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc999999333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000333333666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffff000000ffffff000000ffffffffffffffffff000000000000000000000000ffffff
-ffffff000000000000ffffffffffff000000000000ffffffffffffffffffffffffffffff
-000000ffffffffffff000000ffffffffffff000000000000ffffffffffffffffff000000
-ffffff000000ffffffffffff000000ffffffffffff000000ffffff000000ffffffffffff
-000000ffffffffffffffffff000000ffffff000000ffffffffffffffffff000000ffffff
-000000ffffffffffff000000ffffffffffff000000000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc666666333333000000666666666666
-999999999999cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccc999999999999333333333333333333999999999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333000000333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333333333
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-000000ffffffffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffff000000cccccc000000ffffffffffffffffff000000ffffffffffffccccccffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffff
-000000ffffffcccccc000000ffffffffffff000000000000ffffffffffffcccccc000000
-ffffff000000ffffffffffff000000ffffffcccccc000000ffffff000000ffffffffffff
-000000ffffffccccccffffff000000ffffff000000ffffffffffffffffff000000ffffff
-000000ffffffffffff000000ffffff000000ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-333333000000000000333333333333666666666666999999999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc666666666666333333333333
-333333000000000000333333666666ccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333000000333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-666666333333000000666666999999ccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffcccccc999999333333000000000000666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000ffffffffffffffffff000000000000000000ffffffffffff000000000000
-000000ffffffffffffffffff000000000000000000ffffff000000000000000000ffffff
-000000000000ffffffffffff000000000000ffffffffffffffffffffffffffffff000000
-000000000000ffffff000000000000ffffff000000000000000000000000000000ffffff
-000000000000000000ffffff000000000000000000000000000000000000000000ffffff
-ffffff000000000000000000ffffff000000000000000000ffffff000000000000000000
-000000000000ffffff000000000000000000000000000000000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccccccccc666666666666333333333333000000333333333333
-666666666666666666666666999999999999999999999999cccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccc999999999999999999999999999999
-999999666666666666666666666666333333000000000000666666666666999999999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333000000333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666666666ffffffffffffffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666333333666666ccccccccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999cccccc
-666666999999666666666666333333333333333333333333000000000000000000000000
-000000000000000000000000000000000000000000333333333333333333333333333333
-333333999999666666999999999999ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333000000333333
-333333666666666666999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccccccccc999999999999333333333333000000000000
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000000000000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999999999333333999999ffffffffffff999999666666666666999999999999
-ccccccccccccccccccccccccffffff999999999999ccccccccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccccccccc999999cccccccccccc999999666666
-999999ccccccffffffffffff999999333333666666666666ccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffff999999000000000000000000000000000000000000000000000000000000000000
-999999ffffffffffffffffff333333999999ffffffffffffffffff999999000000000000
-000000000000000000000000000000000000000000000000999999ffffffffffffffffff
-333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000000000333333999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000
-000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999999999666666999999
-333333333333333333999999ffffff999999000000000000000000000000000000000000
-000000000000333333ffffffffffffcccccc333333333333333333333333666666999999
-666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-ffffffffffffffffffffffffccccccccccccffffffffffffffffffcccccccccccccccccc
-ccccccccccccccccccccccccccccccccccccccccccccccccffffffffffffffffffffffff
-ccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000000000000000000000333333999999999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000000000
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000000000000000000000000000000000
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000000000000000000000000000666666
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000000000999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000000000000000000000000000333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999333333000000000000
-000000000000333333666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666000000000000000000000000000000000000000000333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-666666333333000000000000000000000000333333999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc000000000000666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-000000000000000000000000000000999999999999000000000000000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999333333000000000000000000000000333333999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333000000666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666000000000000000000
-000000333333999999ccccccffffffffffffffffff333333333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999666666000000000000000000000000
-000000666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333000000333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000000000000000000000333333999999
-ccccccffffffffffffffffffffffffffffffffffff999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999333333
-000000000000000000000000333333666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666000000333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666000000000000000000000000333333999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999333333333333000000000000000000000000333333999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666000000333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-000000000000000000000000000000999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666333333000000000000000000000000333333
-999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666000000333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666000000000000000000
-000000333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666000000000000
-000000000000000000666666999999ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000000000999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000000000000000000000333333999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999333333000000000000000000000000333333666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666000000000000000000000000333333999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999999999666666999999333333333333333333333333333333333333333333666666
-666666999999666666ccccccccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999333333000000000000000000000000000000333333999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffff999999
-000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-000000000000000000000000000000999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999333333000000000000
-333333666666666666666666999999999999999999999999999999999999999999666666
-666666666666666666333333333333000000666666999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc666666333333000000000000000000
-000000333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffcccccc000000
-000000666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666000000000000000000
-000000333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc999999000000333333333333999999999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999666666000000333333999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-000000000000000000000000000000666666999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffcccccc000000000000
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000000000000000000000333333999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffff
-ffffffffffffffffffcccccc333333333333999999cccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccc999999666666333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999333333000000000000000000000000333333666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffff000000000000333333
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000000000000000000000333333999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffff999999333333999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999999999
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000ffffffccccccffffff000000ffffffffffffffffffffffffffffffccccccffffff
-ffffff000000ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffcccccc000000
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff000000ffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc999999333333000000000000000000000000000000
-333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999
-666666000000000000666666666666999999ffffffffffff000000000000333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-000000000000000000000000000000999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffcccccc333333cccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccc000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666333333000000
-000000000000000000333333666666999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000
-000000000000000000000000000000666666ffffff333333000000333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666000000000000000000
-000000333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffff999999333333cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-ffffffffffffffffffffffffffffff000000ffffff000000000000000000000000ffffff
-000000000000000000ffffff000000000000ffffff000000000000000000ffffffffffff
-000000000000ffffff000000000000ffffff000000000000ffffffffffffffffff000000
-ffffffffffffffffff000000000000000000000000ffffffffffffffffff000000000000
-ffffffffffff000000000000ffffff000000000000ffffff000000000000ffffff000000
-000000ffffffffffff000000000000ffffff000000000000ffffff000000000000ffffff
-ffffff000000000000ffffff000000000000ffffff000000000000000000000000000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999666666000000000000000000000000000000666666666666ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000
-000000000000000000000000000000ffffff333333000000000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000000000000000000000333333999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffcccccc333333cccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccc333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-ffffffffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff000000
-ffffff000000ffffffffffffffffff000000000000ffffffffffffffffff000000ffffff
-ffffff000000000000ffffff000000ffffffffffffffffff000000ffffffffffff000000
-ffffffffffffffffffffffff000000ffffffffffff000000ffffff000000ffffffffffff
-000000ffffffffffff000000000000ffffff000000000000ffffff000000000000ffffff
-000000ffffffffffffffffffffffff000000ffffff000000000000ffffff000000ffffff
-000000ffffffffffff000000ffffff000000000000ffffff000000ffffff000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999333333000000000000000000000000333333666666
-ccccccffffffffffffffffffffffffffffffffffff333333999999ffffffffffff666666
-000000000000000000000000999999666666000000000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000000000000000000000333333999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffff666666666666999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffcccccc000000
-ffffffffffffffffffffffffffffff000000ccccccffffff000000ffffffffffff000000
-ffffff000000ccccccffffffffffff000000000000ffffffffffffffffff000000ffffff
-ffffff000000ffffffffffff000000ffffff000000000000000000ffffffffffff000000
-ffffffffffffccccccffffff000000ffffffffffff000000ffffff000000000000000000
-000000ffffffffffff000000ffffffffffff000000ffffffffffff000000ffffffffffff
-000000ffffffcccccc000000000000000000ffffff000000ffffffffffff000000ffffff
-000000000000000000000000ffffff000000ccccccffffff000000ffffff000000ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999333333000000000000000000
-000000000000333333999999ccccccffffffcccccc000000000000999999ffffffffffff
-000000000000000000000000666666000000000000ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-000000000000000000000000000000999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffff
-ffffffffffffffffff999999333333666666cccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc999999333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff000000
-ffffff000000ffffffffffffffffff000000000000ffffffffffffffffff000000ffffff
-ffffff000000ffffffffffff000000000000ffffffffffff000000ffffffffffff000000
-ffffffffffffffffffffffff000000ffffffffffff000000ffffff000000ffffffffffff
-ffffffffffffffffff000000ffffffffffff000000ffffffffffff000000ffffffffffff
-000000ffffff000000ffffffffffff000000ffffff000000ffffffffffff000000ffffff
-000000ffffffffffffffffffffffff000000ffffffffffff000000ffffff000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-333333000000000000000000000000000000666666000000000000000000666666ffffff
-999999000000000000333333000000000000ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666000000000000000000
-000000333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffcccccc666666000000666666666666cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999999999333333000000333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000000000ffffffffffffffffffffffff000000000000000000ffffff
-ffffff000000000000ffffff000000000000000000000000000000000000ffffffffffff
-000000000000000000ffffff000000000000000000000000000000000000000000000000
-000000ffffffffffffffffff000000000000000000ffffffffffffffffff000000000000
-000000ffffff000000000000000000000000000000000000ffffff000000000000ffffff
-000000000000ffffff000000000000000000000000000000000000ffffff000000000000
-ffffff000000000000000000000000000000000000ffffff000000000000000000000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666000000000000000000000000000000000000000000666666
-cccccc000000000000000000000000999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000000000000000000000333333999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666000000333333333333
-666666999999999999999999cccccccccccccccccccccccccccccccccccccccccc999999
-999999999999999999666666666666333333000000333333999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000000000000000000000000000000000
-666666333333000000000000999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000000000000000000000333333999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999999999
-666666333333333333333333000000000000000000000000000000000000000000333333
-333333333333333333999999999999ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffff000000000000000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffff000000000000000000ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999cccccc666666666666000000000000000000000000000000000000
-000000333333000000333333333333333333333333333333333333333333333333333333
-666666666666000000000000000000666666999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999666666666666000000
-000000000000666666333333999999333333000000000000666666666666999999999999
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-999999666666666666666666666666333333333333000000333333333333999999999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccccccccc666666333333000000333333666666999999999999ffffff
-ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc999999666666
-333333000000333333666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333666666999999ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999666666000000666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-333333999999ccccccffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff999999333333666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000000000ffffffffffff000000000000ffffff000000000000000000000000
-ffffffffffffffffff000000000000000000ffffffffffff000000000000ffffffffffff
-ffffff000000000000ffffffffffff000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffff000000ffffffffffff000000000000000000ffffffffffffffffff
-000000ffffff000000ffffffffffff000000ffffff000000ffffffffffff000000ffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000ffffffcccccc000000ffffffffffff000000ffffff000000ffffffccccccffffff
-000000ffffff000000ffffffffffffffffffcccccc000000000000000000000000ffffff
-ffffff000000000000ffffffffffff000000000000ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333333333
-666666ffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffcccccc666666333333666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffff000000ffffffffffff000000ffffff000000ffffffffffffffffff
-000000ffffff000000ffffffffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-999999000000666666999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999333333333333999999ffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000000000000000ffffffffffff000000000000000000ffffff000000000000000000
-ffffffffffffffffff000000000000000000ffffffffffff000000000000000000ffffff
-000000000000ffffffffffff000000000000ffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffff999999666666333333000000333333999999999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc
-666666666666000000333333666666ccccccffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffcccccc666666666666333333000000333333
-666666333333999999666666ccccccccccccccccccccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccccccccccccccc999999999999666666666666333333333333000000333333333333
-999999999999ffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc
-ffffffffffffffffff666666333333333333333333ffffffffffffffffff666666999999
-666666999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffcccccc000000
-000000000000ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999cccccc666666999999333333333333333333333333000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-333333333333333333666666666666999999999999ccccccccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000333333333333
-ffffffffffffffffff999999ccccccccccccccccccffffffffffffffffff999999666666
-999999666666666666000000000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffff999999ccccccffffffffffff999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffff
-ffffffffffffcccccc000000666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffcccccc000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffff000000
-ffffffffffff000000ffffffffffff000000ffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffff000000000000ffffffffffff000000
-000000ffffff000000000000ffffff000000000000000000000000000000000000ffffff
-ffffff000000000000000000ffffffffffff000000000000ffffff000000ffffff000000
-000000ffffff000000000000ffffff000000000000ffffff000000000000000000ffffff
-ffffff000000000000000000000000ffffffffffff000000000000ffffff000000000000
-000000000000ffffffffffffffffff000000000000000000ffffffffffff000000000000
-ffffffffffffffffff000000000000ffffffffffff000000000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffff999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffff
-000000000000ffffff000000000000ffffff000000ffffff000000ffffffffffff000000
-000000ffffffffffffffffff000000ffffffffffff000000000000ffffffffffffffffff
-ffffff000000ffffff000000000000ffffff000000ffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffff000000ffffffffffff000000000000000000ffffff
-ffffffffffff000000ffffff000000ffffffffffff000000ffffff000000ffffffffffff
-000000ffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffff666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff000000ffffffffffffffffff000000000000000000000000ffffffffffff
-000000ffffffcccccc000000ffffffffffff000000ffffff000000ffffffcccccc000000
-000000ffffffffffffffffff000000ffffffcccccc000000ffffffffffffffffff000000
-000000000000cccccc000000ffffffffffff000000ffffff000000ffffffccccccffffff
-ffffffffffff000000ffffffffffff000000ccccccffffff000000ffffff000000ffffff
-ffffffffffff000000ffffff000000ffffffffffffffffffffffff000000000000000000
-000000ffffffffffff000000000000ffffffcccccc000000000000ffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffff999999000000ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffff
-000000ffffffffffff000000ffffffffffff000000ffffff000000ffffffffffff000000
-000000ffffffffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff
-ffffff000000ffffff000000ffffffffffffffffff000000000000ffffffffffffffffff
-ffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff000000ffffff
-ffffffffffff000000ffffff000000ffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffccccccccccccffffffffffffccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000000000ffffffffffffffffff000000000000000000ffffff000000
-000000000000ffffff000000000000ffffff000000000000000000000000000000ffffff
-ffffff000000000000000000ffffffffffff000000000000000000ffffffffffff000000
-000000000000000000000000000000ffffffffffff000000ffffffffffffffffffffffff
-ffffffffffff000000000000000000ffffffffffff000000000000000000ffffff000000
-000000000000ffffffffffffffffff000000000000000000ffffffffffff000000000000
-000000ffffff000000000000ffffffffffff000000000000ffffffffffffffffff000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffff333333333333333333666666ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffff999999
-ccccccffffffffffffcccccc666666000000333333999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc666666ffffff
-ffffffffffff333333000000000000000000ffffffffffffffffff000000000000333333
-333333ffffffffffffffffff999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffcccccc000000000000000000ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffffff
-ffffff000000000000000000ffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffff999999666666ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffff999999666666ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc
-666666999999666666666666333333333333333333333333333333333333333333999999
-666666999999999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffff999999666666ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999333333000000333333333333
-999999666666999999999999cccccccccccccccccccccccccccccccccccccccccc666666
-999999666666666666333333000000333333999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffff999999333333333333999999999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666333333000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffff
-ffffffffffffcccccc333333666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffcccccc333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000999999
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff999999333333666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffff333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666333333
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-000000ffffffffffffffffff000000ffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffff000000ffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666333333333333999999ccccccffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffcccccc999999666666000000999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffff
-ffffffffffff999999333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffff000000000000ffffff000000000000
-ffffff000000000000000000ffffff000000000000ffffff000000000000ffffff000000
-000000ffffffffffff000000000000ffffff000000000000ffffff000000000000ffffff
-ffffff000000000000ffffff000000000000000000000000000000ffffffffffffffffff
-000000000000000000000000ffffff000000000000ffffff000000ffffff000000000000
-000000ffffffffffff000000000000000000ffffffffffff000000000000ffffffffffff
-ffffff000000000000ffffffffffff000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666333333000000666666666666ccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-999999333333333333333333666666999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffcccccc333333333333666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999666666000000666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000000000000000000000ffffffffffff000000ffffffffffff000000ffffff000000
-000000ffffffffffff000000000000ffffff000000000000ffffff000000ffffffffffff
-ffffff000000ffffffffffff000000000000ffffff000000000000ffffffffffff000000
-ffffffffffff000000000000ffffff000000ffffff000000ffffffffffffffffffffffff
-ffffff000000ffffffffffff000000ffffff000000000000ffffff000000ffffffffffff
-ffffff000000000000ffffffffffff000000ffffff000000ffffffffffff000000ffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999999999333333333333000000333333
-333333666666666666999999999999cccccc999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999cccccc999999999999666666999999333333333333333333000000333333666666
-666666ccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffff
-ffffffffffffffffffffffffffffffcccccc999999333333333333333333666666666666
-ccccccccccccccccccccccccffffffffffffffffffffffffffffffffffffffffff999999
-cccccccccccc999999666666666666000000666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffff000000000000000000000000ffffff000000
-ffffffffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff000000
-000000000000ffffffffffff000000ffffffffffff000000000000000000000000000000
-ffffffffffff000000ffffffffffff000000ffffff000000ffffffffffffffffffffffff
-ffffff000000ffffffffffff000000ffffff000000ffffffffffff000000ffffffffffff
-ffffff000000000000ffffffffffffffffffffffff000000000000000000000000ffffff
-ffffff000000000000ffffffffffff000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-cccccc999999999999666666666666333333333333333333000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-666666333333666666333333999999666666cccccc999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc999999999999
-333333333333333333333333000000000000000000000000000000000000000000333333
-333333333333666666999999999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000ffffffccccccffffffffffffffffff000000ffffffffffffffffffcccccc000000
-ffffffffffffffffff000000ffffffffffff000000ffffffffffff000000000000ffffff
-ffffff000000ccccccffffff000000ffffffffffff000000000000ffffffccccccffffff
-ffffffffffff000000ffffffffffff000000cccccc000000ffffffffffffffffffffffff
-ffffff000000ccccccffffff000000ffffff000000ffffffffffff000000ccccccffffff
-ffffff000000000000ffffffffffffffffffcccccc000000ffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000ffffffffffffffffffffffffffffff000000000000000000000000000000
-000000ffffff000000000000000000ffffff000000000000ffffff000000000000000000
-000000000000000000000000000000000000ffffff000000000000000000000000000000
-ffffff000000000000000000ffffff000000000000000000000000ffffffffffffffffff
-ffffff000000000000000000ffffff000000000000000000ffffffffffff000000000000
-000000ffffffffffff000000000000000000ffffffffffff000000000000000000ffffff
-000000000000ffffffffffff000000000000ffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffcccccc666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffff999999666666ffffffffffffffffffcccccc666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffff999999666666ffffffffffff
-ffffffcccccc666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-999999666666ffffffffffffffffffcccccc666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffffcccccc666666ffffffffffffffffffcccccc666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffffcccccc666666ffffffffffff
-ffffffcccccc666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-cccccc666666ffffffffffffffffffcccccc666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffffcccccc666666ffffffffffffffffffcccccc666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffffcccccc666666ffffffffffff
-ffffffcccccc666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-cccccc666666ffffffffffffffffffcccccc666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffffcccccc666666ffffffffffffffffffcccccc666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffffcccccc666666ffffffffffff
-ffffffcccccc666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-cccccc666666ffffffffffffffffffcccccc666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffffcccccc666666ffffffffffffffffffcccccc666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffffcccccc666666ffffffffffff
-ffffffcccccc666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-cccccc666666ffffffffffffffffffcccccc666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffffcccccc666666ffffffffffffffffffcccccc666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffffcccccc666666ffffffffffff
-ffffffcccccc666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-cccccc666666ffffffffffffffffffcccccc666666ccccccffffffffffffffffff666666
-999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc666666cccccc
-ffffffffffffffffff666666999999ffffffffffffffffffcccccc666666ffffffffffff
-ffffffcccccc666666ccccccffffffffffffffffff666666999999ffffffffffffffffff
-cccccc666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffffffffff333333000000666666ffffffffffff666666000000
-000000ffffffffffffcccccc000000000000999999ffffffffffff333333000000666666
-ffffffffffff666666000000000000ffffffffffffcccccc000000000000999999ffffff
-ffffff333333000000666666ffffffffffff666666000000000000ffffffffffffcccccc
-000000000000999999ffffffffffff333333000000666666ffffffffffff666666000000
-000000ffffffffffffcccccc000000000000999999ffffffffffff333333000000666666
-ffffffffffff666666000000000000ffffffffffffcccccc000000000000999999ffffff
-ffffff333333000000333333ffffffffffff666666000000000000ffffffffffffcccccc
-000000000000999999ffffffffffff333333000000333333ffffffffffff666666000000
-000000ffffffffffffcccccc000000000000999999ffffffffffff333333000000333333
-ffffffffffff666666000000000000ffffffffffffcccccc000000000000999999ffffff
-ffffff333333000000333333ffffffffffff999999000000000000ffffffffffffcccccc
-000000000000999999ffffffffffff333333000000333333ffffffffffff999999000000
-000000ffffffffffffcccccc000000000000999999ffffffffffff333333000000333333
-ffffffffffff999999000000000000ffffffffffffcccccc000000000000999999ffffff
-ffffff333333000000333333ffffffffffff999999000000000000ffffffffffffcccccc
-000000000000999999ffffffffffff333333000000333333ffffffffffff999999000000
-000000ffffffffffffcccccc000000000000999999ffffffffffff333333000000333333
-ffffffffffff999999000000000000ffffffffffffffffff000000000000999999ffffff
-ffffff333333000000333333ffffffffffff999999000000000000ffffffffffffffffff
-000000000000999999ffffffffffff333333000000333333ffffffffffff999999000000
-000000ffffffffffffffffff000000000000666666ffffffffffff333333000000333333
-ffffffffffff999999000000000000ffffffffffffffffff000000000000666666ffffff
-ffffff333333000000333333ffffffffffff999999000000000000ffffffffffffffffff
-000000000000666666ffffffffffff333333000000333333ffffffffffff999999000000
-000000ffffffffffffffffff000000000000666666ffffffffffff666666000000333333
-ffffffffffff999999000000000000ffffffffffffffffff000000000000666666ffffff
-ffffff666666000000333333ffffffffffff999999000000000000ffffffffffffffffff
-000000000000666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333999999ffffffffffffffffff333333
-999999ffffffffffffffffff666666333333ffffffffffffffffff999999333333999999
-ffffffffffffffffff333333999999ffffffffffffffffff666666333333ffffffffffff
-ffffff999999333333999999ffffffffffffffffff333333999999ffffffffffffffffff
-666666333333ffffffffffffffffff999999333333999999ffffffffffffffffff333333
-666666ffffffffffffffffff666666333333ffffffffffffffffff999999333333999999
-ffffffffffffffffff333333666666ffffffffffffffffff666666333333ffffffffffff
-ffffff999999333333999999ffffffffffffffffff333333666666ffffffffffffffffff
-666666333333ffffffffffffffffff999999333333999999ffffffffffffffffff333333
-666666ffffffffffffffffff666666333333ffffffffffffffffff999999333333999999
-ffffffffffffffffff333333666666ffffffffffffffffff666666333333ffffffffffff
-ffffff999999333333999999ffffffffffffffffff333333666666ffffffffffffffffff
-666666333333ffffffffffffffffff999999333333999999ffffffffffffffffff333333
-666666ffffffffffffffffff666666333333ffffffffffffffffff999999333333999999
-ffffffffffffffffff333333666666ffffffffffffffffff666666333333ffffffffffff
-ffffff999999333333999999ffffffffffffffffff333333666666ffffffffffffffffff
-666666333333ffffffffffffffffff999999333333999999ffffffffffffffffff333333
-666666ffffffffffffffffff666666333333ffffffffffffffffff999999333333999999
-ffffffffffffffffff333333666666ffffffffffffffffff666666333333ffffffffffff
-ffffff999999333333999999ffffffffffffffffff333333666666ffffffffffffffffff
-666666333333ffffffffffffffffff999999333333999999ffffffffffffffffff333333
-666666ffffffffffffffffff666666333333ffffffffffffffffff999999333333999999
-ffffffffffffffffff333333666666ffffffffffffffffff666666333333ffffffffffff
-ffffff999999333333999999ffffffffffffffffff333333666666ffffffffffffffffff
-666666333333ffffffffffffffffff999999333333999999ffffffffffffffffff333333
-666666ffffffffffffffffff999999333333ffffffffffffffffff999999333333999999
-ffffffffffffffffff333333666666ffffffffffffffffff999999333333ffffffffffff
-ffffff999999333333999999ffffffffffffffffff333333666666ffffffffffffffffff
-999999333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-
-showpage
-
-% stop using temporary dictionary
-end
-
-% restore original state
-origstate restore
-
-%%Trailer
diff --git a/lib/inviso/doc/src/notes.xml b/lib/inviso/doc/src/notes.xml
deleted file mode 100644
index 661284e786..0000000000
--- a/lib/inviso/doc/src/notes.xml
+++ /dev/null
@@ -1,278 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
-
-<chapter>
- <header>
- <copyright>
- <year>2006</year><year>2011</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- 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
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>Inviso Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- <file>notes.xml</file>
- </header>
- <p>This document describes the changes made to the Inviso application.</p>
-
-
- <section><title>Inviso 0.6.3</title>
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>The <c>inviso</c> application has been deprecated and
- will be removed in the R16 release.</p>
- <p>
- Own Id: OTP-9798</p>
- </item>
- <item>
- <p>
- Eliminate use of deprecated regexp module</p>
- <p>
- Own Id: OTP-9810</p>
- </item>
- </list>
- </section>
-
-</section>
-
-<section><title>Inviso 0.6.2</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p>
- The obsolete guards has now been changed to the new guard
- interface.</p>
- <p>
- Own Id: OTP-8747</p>
- </item>
- </list>
- </section>
-
-</section>
-
-<section><title>Inviso 0.6.1</title>
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>
- The documentation is now built with open source tools
- (xsltproc and fop) that exists on most platforms. One
- visible change is that the frames are removed.</p>
- <p>
- Own Id: OTP-8201</p>
- </item>
- </list>
- </section>
-
-</section>
-
-<section><title>Inviso 0.6</title>
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p>
- In this release the following has been fixed and
- enhanced: Autostart: It is now possible to configure
- modules that shall be loaded by the autostart mechanism.
- This because it is not certain that all application
- systems make use of the OTP boot script to set up paths
- to all Erlang modules. Runtime_tools/Inviso: A bug in the
- fetch_log functionality has been fixed. Further a bug
- that was (supposedly) fixed in a previous patch
- concerning meta-tracer write_ti has been fixed (again) in
- this patch. A bug in inviso_as_lib making remote
- autostart config file updates fail has been fixed.
- Inviso: inviso_tool has been given a flush API.</p>
- <p>
- Own Id: OTP-6918</p>
- </item>
- </list>
- </section>
-
-</section>
-<section><title>Inviso 0.5</title>
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>
- The following bugs/improvements have been done: Internal
- interworking between inviso_rt and inviso_rt_meta. The
- call function used by inviso_rt to call inviso_rt_meta is
- now protected with a monitor. Inviso_rt_meta now includes
- the timestamp of the incoming meta trace message when
- calling the call-callback. (Makes it possible to add a
- "better" timestamp to the ti-file.) Bug in inviso_tool
- making it not remove trace patterns when terminating. Bug
- in internal function h_start_session making inviso_tool
- crash if there were no active nodes to start the session
- on. The user-inviso_tool and inviso API-inviso control
- component request/response gen_server protocols had
- default time-out. Since many trace operations can be time
- consuming, a longer time-out is necessary. Improved
- overload protection. It is now possible to let the
- overload protection renew itself (e.g after an exit from
- an external overload protector). Inviso_rt_meta now fully
- uses the exception_trace match spec action term. Run
- Trace Case API (as in contrast to activate and deactivate
- trace case APIs) in inviso_tool. Flush trace-port API
- added to inviso. Get_session_data API added to
- inviso_tool. Improved inviso_tool:stop making it possible
- to name nodes which shall not have their trace patterns
- removed when inviso_tool terminates. Bug in handling of
- writing multiple ti-entries if returned from a
- call/return_from call-back in inviso_rt_meta Process
- trace flags are no longer explicitly removed by the
- inviso_tool when it terminates. Not necessary.
- Inviso_tool get_autostart_data adopted to standard
- autostarter.</p>
- <p>
- *** INCOMPATIBILITY with Meta trace call-backs are called
- with different arguments now. ***</p>
- <p>
- Own Id: OTP-6881</p>
- </item>
- </list>
- </section>
-
-</section>
-<section><title>Inviso 0.4</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p>
- A bug in inviso_rt_meta caused an ETS table containing
- information on initiated (init_tpm) functions to be lost
- when suspending tracing. Further an enhancement to
- inviso_rt has been introduced making it possible to
- activate process trace flags based on globally registered
- names. It is then not an error to activate a global name
- on a node where the name does not reside. The process
- count in the return value will simply be set to zero
- (hence exactly one node in the NodeResult part of the
- return value will indicate one matching process found). A
- bug was found in fetch_log API. At the same time the
- fetch_log functionality was enhanced to also offer flow
- control making fetcher processes send chunks of
- transferred file data at a slower pace.</p>
- <p>
- Own Id: OTP-6703</p>
- </item>
- </list>
- </section>
-
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>
- Minor Makefile changes.</p>
- <p>
- Own Id: OTP-6689 Aux Id: OTP-6742 </p>
- </item>
- </list>
- </section>
-
-</section>
-
-
-<section><title>Inviso 0.3</title>
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>
- In this release the first working version of the
- inviso_tool is included. Updates and small bugfixes in
- the inviso application.</p>
- <p>
- Own Id: OTP-6677</p>
- </item>
- </list>
- </section>
-
-</section>
-<section><title>Inviso 0.2.1</title>
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>
- Misc improvements.</p>
- <p>
- Own Id: OTP-6576</p>
- </item>
- </list>
- </section>
-
-</section>
-
-<section><title>Inviso 0.2</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p>
- This ticket includes several improvements and bugfixes to
- both runtime_tools and inviso. The overload mechanism can
- now also react to incoming messages. This is useful if
- an external overload watch-dog is used. Some improvements
- of variable bindings has been done to the default
- autostart mechanism - inviso_autostart_server. Autostart
- "jobs" can now be done in parallel, allowing for some
- jobs to hang waiting for some parts of the traced system
- to become ready before proceeding. Previously when using
- named meta-match-specs (tpm_ms) ending up with zero
- match-specs still kept the meta trace pattern active.
- This caused zero match-specs to be equal to unlimited
- meta tracing on that particular function. If the internal
- database becomes empty of meta match specs, meta trace
- pattern is removed for that function. Standard public
- loop data in the inviso runtime meta tracer process is
- now extended to a 2-tuple. The functions ctp/1 and ctpl/1
- are added making it possible to remove trace patterns for
- a list of functions rather than one by one.
- Inviso_rt_meta will now accept a list of binaries to be
- output into the trace information file, in additions to a
- single binary. Further it is also possible to make own
- output to the trace information file using the write_ti/1
- function. An error was discovered in inviso_rt making the
- inviso_rt_meta remain rather than terminate if the
- inviso_rt terminated due to "running alone" (not allowed
- to run without a control component). A new tool,
- inviso_tool, has been added to the inviso application.</p>
- <p>
- Own Id: OTP-6426</p>
- </item>
- </list>
- </section>
-
-</section>
-
- <section>
- <title>Inviso 0.1</title>
- <p>First version.</p>
- </section>
-
-</chapter>
-
diff --git a/lib/inviso/doc/src/part_notes.xml b/lib/inviso/doc/src/part_notes.xml
deleted file mode 100644
index 1cd8b0f1a7..0000000000
--- a/lib/inviso/doc/src/part_notes.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE part SYSTEM "part.dtd">
-
-<part xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2006</year><year>2009</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- 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
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>Inviso Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p><em>Inviso</em>, an Erlang trace tool.</p>
- </description>
- <xi:include href="notes.xml"/>
-</part>
-
diff --git a/lib/inviso/doc/src/ref_man.xml b/lib/inviso/doc/src/ref_man.xml
deleted file mode 100644
index c9e75e4029..0000000000
--- a/lib/inviso/doc/src/ref_man.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE application SYSTEM "application.dtd">
-
-<application xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2006</year><year>2009</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- 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
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>Inviso Reference Manual</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p><em>Inviso</em>, an Erlang trace tool.</p>
- </description>
- <xi:include href="inviso.xml"/>
- <xi:include href="inviso_as_lib.xml"/>
- <xi:include href="inviso_lfm.xml"/>
- <xi:include href="inviso_lfm_tpfreader.xml"/>
- <xi:include href="inviso_rt.xml"/>
- <xi:include href="inviso_rt_meta.xml"/>
-</application>
-
diff --git a/lib/inviso/ebin/.gitignore b/lib/inviso/ebin/.gitignore
deleted file mode 100644
index e69de29bb2..0000000000
--- a/lib/inviso/ebin/.gitignore
+++ /dev/null
diff --git a/lib/inviso/include/.gitignore b/lib/inviso/include/.gitignore
deleted file mode 100644
index e69de29bb2..0000000000
--- a/lib/inviso/include/.gitignore
+++ /dev/null
diff --git a/lib/inviso/info b/lib/inviso/info
deleted file mode 100644
index 3190d6d1cc..0000000000
--- a/lib/inviso/info
+++ /dev/null
@@ -1,2 +0,0 @@
-group: tools
-short: A trace tool for both development and delivered systems.
diff --git a/lib/inviso/priv b/lib/inviso/priv
deleted file mode 100644
index e69de29bb2..0000000000
--- a/lib/inviso/priv
+++ /dev/null
diff --git a/lib/inviso/src/Makefile b/lib/inviso/src/Makefile
deleted file mode 100644
index 292a2bec99..0000000000
--- a/lib/inviso/src/Makefile
+++ /dev/null
@@ -1,104 +0,0 @@
-# ``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
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved via the world wide web at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights 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 $(ERL_TOP)/make/target.mk
-include $(ERL_TOP)/make/$(TARGET)/otp.mk
-
-# ----------------------------------------------------
-# Application version
-# ----------------------------------------------------
-include ../vsn.mk
-VSN=$(INVISO_VSN)
-
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/inviso-$(VSN)
-
-# ----------------------------------------------------
-# Common Macros
-# ----------------------------------------------------
-
-MODULES= \
- inviso \
- inviso_c \
- inviso_lfm \
- inviso_lfm_tpfreader \
- inviso_tool \
- inviso_tool_lib
-
-
-#HRL_FILES= ../include/
-
-ERL_FILES= $(MODULES:%=%.erl)
-
-TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
-
-APP_FILE= inviso.app
-
-APP_SRC= $(APP_FILE).src
-APP_TARGET= $(EBIN)/$(APP_FILE)
-
-APPUP_FILE= inviso.appup
-
-APPUP_SRC= $(APPUP_FILE).src
-APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-ERL_COMPILE_FLAGS += +warn_unused_vars -I../include
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-
-debug opt: $(TARGET_FILES)
-
-clean:
- rm -f $(TARGET_FILES)
- rm -f errs core *~
-
-$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
-
-$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
-
-docs:
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_spec: opt
- $(INSTALL_DIR) "$(RELSYSDIR)/src"
- $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src"
-# $(INSTALL_DIR) "$(RELSYSDIR)/include"
-# $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include"
- $(INSTALL_DIR) "$(RELSYSDIR)/ebin"
- $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin"
-
-release_docs_spec:
-
-
-
-
-
-
-
diff --git a/lib/inviso/src/inviso.app.src b/lib/inviso/src/inviso.app.src
deleted file mode 100644
index 91eaa1b9b2..0000000000
--- a/lib/inviso/src/inviso.app.src
+++ /dev/null
@@ -1,13 +0,0 @@
-{application,inviso,
- [{description, "INVISO trace tool"},
- {vsn, "%VSN%"},
- {modules, [inviso_c,inviso,
- inviso_lfm,inviso_lfm_tpfreader
- ]},
- {registered, [inviso_c]},
- {applications, [kernel, stdlib, runtime_tools]},
- {env, []}
- ]}.
-
-
-
diff --git a/lib/inviso/src/inviso.appup.src b/lib/inviso/src/inviso.appup.src
deleted file mode 100644
index 54a63833e6..0000000000
--- a/lib/inviso/src/inviso.appup.src
+++ /dev/null
@@ -1 +0,0 @@
-{"%VSN%",[],[]}.
diff --git a/lib/inviso/src/inviso.erl b/lib/inviso/src/inviso.erl
deleted file mode 100644
index 07bdf3e649..0000000000
--- a/lib/inviso/src/inviso.erl
+++ /dev/null
@@ -1,1056 +0,0 @@
-%% ``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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights 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$
-%%
-%% Author: Ann-Marie L�f, [email protected]
-%% Lennart �hman, [email protected]
-%%
-%% Description: API module for the inviso system.
-%% Inviso consists of a control component and possibly one or more runtime
-%% components. This module is simply the API to the inviso system. All normal
-%% calls goes through the control component.
-%% ------------------------------------------------------------------------------
-
--module(inviso).
--deprecated(module).
-
-%% ------------------------------------------------------------------------------
-%% Exported API functions.
-%% ------------------------------------------------------------------------------
-
--export([start/0, start/1,
- add_node/1, add_node/2, add_node_if_ref/1, add_node_if_ref/2,
- add_nodes/2, add_nodes/3, add_nodes_if_ref/2, add_nodes_if_ref/3,
- change_options/1, change_options/2,
- init_tracing/1, init_tracing/2,
- stop_tracing/0, stop_tracing/1,
- clear/0, clear/1, clear/2,
- flush/0,flush/1,
- stop/0, stop_nodes/0, stop_nodes/1, stop_all/0,
- tp/1,tp/2,tp/4,tp/5,tp/6,
- tpl/1,tpl/2,tpl/4,tpl/5,tpl/6,
- tpm_localnames/0,tpm_localnames/1,tpm_globalnames/0,tpm_globalnames/1,
- init_tpm/4,init_tpm/5,init_tpm/7,init_tpm/8,
- tpm/4,tpm/5,tpm/6,tpm/8,tpm/9,
- tpm_tracer/4,tpm_tracer/5,tpm_tracer/6,tpm_tracer/8,tpm_tracer/9,
- tpm_ms/5,tpm_ms/6,
- tpm_ms_tracer/5,tpm_ms_tracer/6,
- ctpm_ms/4,ctpm_ms/5,ctpm/3,ctpm/4,
- ctpm_localnames/0,ctpm_localnames/1,ctpm_globalnames/0,ctpm_globalnames/1,
- ctp/1,ctp/2,ctp/3,ctp/4,
- ctpl/1,ctpl/2,ctpl/3,ctpl/4,
- tf/1, tf/2, tf/3,
- ctf/1, ctf/2, ctf/3,
- ctp_all/0, ctp_all/1, ctf_all/0, ctf_all/1,
- suspend/1, suspend/2,
- cancel_suspension/0, cancel_suspension/1,
- get_status/0, get_status/1,
- get_tracerdata/0, get_tracerdata/1,
- list_logs/0, list_logs/1,
- fetch_log/2, fetch_log/3, fetch_log/4,
- delete_log/0, delete_log/1, delete_log/2,
- subscribe/0, subscribe/1,
- unsubscribe/0, unsubscribe/1]).
-
-%% debuging inviso
--export([state/0, state/1]).
-
-%% ------------------------------------------------------------------------------
-%% Macros used in this module.
-%% ------------------------------------------------------------------------------
-
--define(CONTROLLER,inviso_c).
-
-%% Some function calls to runtime components may take long time, we must wait
-%% longer than the standard timeout for a reply from the control component.
--define(CALL_TIMEOUT,60000).
-%% ------------------------------------------------------------------------------
-
-
-
-%% =============================================================================
-%% CONTROL COMPONENT API FUNCTIONS.
-%% =============================================================================
-
-%% start()={ok,pid()}|{error,Reason}
-%% start(Options)={ok,pid()}|{error,Reason}
-%% Options=[Option,...], the options will be default options to runtime components
-%% later started. See add_node about available options.
-%% There are also options consumed by the control component:
-%% Option={subscribe,pid()}
-%%
-%% Starts a control component process on the local node. A control component must
-%% be started before runtime components can be started manually.
-start() ->
- gen_server:start({local,?CONTROLLER},?CONTROLLER,{self(),[]},[]).
-
-start(Options) ->
- gen_server:start({local,?CONTROLLER},?CONTROLLER,{self(),Options},[]).
-%% -----------------------------------------------------------------------------
-
-%% add_node(Reference)=NodeResult|{error, Reason}
-%% add_node(Reference,Options)=NodeResult|{error, Reason}
-%% Reference=PreviousReference = term(),
-%% Options=[Option,...],
-%% Option={dependency,Dep}
-%% Dep=integer()|'infinity'; The timeout before the runtime component will
-%% terminate if abandoned by this control component.
-%% Option={overload,Overload}; controls how and how often overload checks shall
-%% be performed. Instead of specifying a tuple, the atom 'overload' can be
-%% specified to state no loadcheck. The result will actually be the same
-%% if 'infinity' is used as intervall. It is sometimes necessary to
-%% initialize the overlaod check. This can be done with InitMFA. The
-%% loadchecker must then also be removed by using a RemoveMFA.
-%% Overload=Iterval (int() in milliseconds) |
-%% {LoadMF,Interval}|{LoadMF,Interval,InitMFA,RemoveMFA}
-%% LoadMF={Mod,Func}
-%% InitMFA,RemoveMFA={Mod,Func,ArgList}|void
-%% If just Interval is used, it means using a default overload check.
-%% NodeResult={ok,NAns}|{error,Reason}
-%% NAns=new|{adopted,State,Status,PreviousReference}|already_added
-%% Status = running | {suspended, SReason}
-%%
-%% Starts or tries to connect to an existing runtime component at the local
-%% node, regardless if the system is distributed or not.
-%% Options will override any default options specified at start-up of the
-%% control component.
-add_node(Reference) ->
- gen_server:call(?CONTROLLER,{add_nodes,[node()],[],Reference,any_ref},?CALL_TIMEOUT).
-
-add_node(Reference,Options) when is_list(Options) ->
- gen_server:call(?CONTROLLER,{add_nodes,[node()],Options,Reference,any_ref},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% add_node(Reference)=NodeResult|{error,{wrong_reference,OtherRef}}|{error,Reason}
-%% add_node(Reference,Options)=NodeResult|{error,{wrong_reference,OtherRef}}|
-%% {error,Reason}
-%%
-%% As add_node/1,/2 but will only connect to an already existing runtime component
-%% if its reference is the same as the one given as argument.
-add_node_if_ref(Reference) ->
- gen_server:call(?CONTROLLER,{add_nodes,[node()],[],Reference,if_ref},?CALL_TIMEOUT).
-
-add_node_if_ref(Reference,Options) when is_list(Options) ->
- gen_server:call(?CONTROLLER,{add_nodes,[node()],Options,Reference,if_ref},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% add_nodes(Nodes,Reference)={ok,NodeResults}|{error,Reason}
-%% add_nodes(Nodes,Reference,Options)={ok,NodeResults}|{error,Reason}
-%% Nodes=[Node,...],
-%% NodeResults=[{Node,NodeResult},...]
-%%
-%% As add_node/1,/2 but for the nodes specified in Nodes.
-%% It is possible but not intended to use this function in a non-distributed
-%% system. By speicifying node() as the node where the runtime component shall
-%% be started. The return value will then follow the rules of non distributed
-%% returnvalues and not have a node indicator.
-add_nodes(Nodes,Reference) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{add_nodes,Nodes,[],Reference,any_ref},?CALL_TIMEOUT).
-
-add_nodes(Nodes,Reference,Options) when is_list(Nodes),is_list(Options) ->
- gen_server:call(?CONTROLLER,{add_nodes,Nodes,Options,Reference,any_ref},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% add_nodes_if_ref(Nodes,Reference)={ok,NodeResults}|{error,Reason}
-%% add_nodes_if_ref(Nodes,Reference,Options)={ok,NodeResults}|{error,Reason}
-%%
-%% As add_nodes/2,/3 but will only connect to an already existing runtime component
-%% if its reference is the same as the one given as argument.
-add_nodes_if_ref(Nodes,Reference) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{add_nodes,Nodes,[],Reference,if_ref},?CALL_TIMEOUT).
-
-add_nodes_if_ref(Nodes,Reference,Options) when is_list(Nodes),is_list(Options) ->
- gen_server:call(?CONTROLLER,{add_nodes,Nodes,Options,Reference,if_ref},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% change_options(Options)={ok,NodeResults}|NodeResult|{error,Reason}
-%% change_options(Nodes,Options)={ok,NodeResults}|{error,Reason}
-%% Nodes=[Node,...],
-%% Options= see add_node and add_nodes on available options.
-%%
-%% Change options on all or specified Nodes. This may result in for instance
-%% reinitialization of overloadcheck.
-change_options(Options) when is_list(Options) ->
- gen_server:call(?CONTROLLER,{change_options,all,Options},?CALL_TIMEOUT).
-change_options(Nodes,Options) when is_list(Nodes),is_list(Options) ->
- gen_server:call(?CONTROLLER,{change_options,Nodes,Options},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% init_tracing(TracerData)={ok,[{Node,NodeResult}]} | NodeResult | {error,Reason}
-%% init_tracing(TracerList)={ok,[{Node,NodeResult}]} | {error,Reason}
-%% init_tracing(Nodes,TracerData)={ok,[{Node,NodeResult}]}|{error,Reason}
-%% TracerData = [{trace,LogTD} [,{ti,TiTD}]}] | LogTD
-%% LogTD = {HandlerFun,Data} | collector | {relayer,pid()} |
-%% {relayer,CollectingNode} | {ip,IPPortParameters} |
-%% {file,FilePortParameters}
-%% TiTD = {file,FileName} | {file,FileName,TiMFA}
-%% TiMFA = {Module,Function,ArgumentList} initiating a private loopdata
-%% inside the meta-tracer.
-%% TracerList = [{Node,TracerData}],
-%% IPPortParameters = Portno | {Portno, Qsiz}
-%% Qsiz =
-%% FilePortParameters = {Filename, wrap, Tail, {time, WrapTime}, WrapCnt} |
-%% {FileName, wrap, Tail, WrapSize, WrapCnt} |
-%% {FileName, wrap, Tail, WrapSize} |
-%% {FileName, wrap, Tail} | FileName
-%% Tail =/= ""
-%% HandlerFun is a function taking 2 arguments.
-%% Nodes = [node()],
-%% CollectingNode = pid() | node(),
-%% NodeResult = {ok,LogResults} | {error, NReason}
-%% LogResults=[LogResult,...]
-%% LogResult={trace_log,LogRes} | {ti_log,LogRes}
-%% LogRes=ok|{error,Reason}
-%%
-%% Starts tracing on the nodes specified. If just providing a TracerData tracing
-%% will be initiated on all our nodes. If it is the non distributed case, that
-%% means only on the local non distributed node.
-%%
-%% {HandlerFun,Data}
-%% Will use the runtime components own process as tracer and handle all
-%% incomming trace message using HandlerFun.
-%% {relayer,CollectingNode}
-%% The runtime component addressed will act tracer and relay all incomming trace
-%% messages to Node or Pid, if CollectingNode is not a traced node connected
-%% to the controll component, the init_tracing call will return an error.
-%% Note that {relayer, Node} only is syntactical sugar for
-%% {relayer, rpc:call(Node,erlang,whereis,[inviso_rt])}
-%% collector
-%% The runtime component is used as tracer or collector of relayed
-%% trace messages using the default handler writing them to io.
-%% ip | file - will open a trace-port on Node using PortParameters
-init_tracing(TracerDataList) ->
- gen_server:call(?CONTROLLER,{init_tracing,TracerDataList},?CALL_TIMEOUT).
-
-init_tracing(Nodes,TracerData) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{init_tracing,Nodes,TracerData},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% stop_tracing(Nodes)={ok,NodeResults}|{error,Reason}
-%% stop_tracing()={ok,NodeResults}|NodeResult
-%% Nodes=[Node,...],
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,State}|{error,Reason}
-%% State=new|idle
-%% Stops tracing on all or specified Nodes. Flushes trace buffert,
-%% closes trace port and removes all trace flags and meta-patterns.
-%% The nodes are called in parallel.
-stop_tracing() ->
- gen_server:call(?CONTROLLER,{stop_tracing,all},?CALL_TIMEOUT).
-
-stop_tracing(Nodes) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{stop_tracing,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% clear()={ok,NodeResults}|NodeResult
-%% clear(Nodes,Options)={ok,NodeResults}|{error,Reason}
-%% clear(Options)={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node,...],
-%% Options=[Option,...],
-%% Option=keep_trace_patterns|keep_log_files
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,{new,Status}}|{error,Reason}
-%% Status=running|{suspended,SReason}
-%%
-%% Stops all tracing including removing meta-trace patterns. If the node is tracing
-%% or idle, logs belonging to the current tracerdata are removed. Hence the node
-%% is returned to state 'new'. Note that node can still be suspended.
-clear() ->
- gen_server:call(?CONTROLLER,{clear,all,[]},?CALL_TIMEOUT).
-
-clear(Nodes) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{clear,Nodes,[]},?CALL_TIMEOUT).
-
-clear(Nodes,Options) when is_list(Nodes),is_list(Options) ->
- gen_server:call(?CONTROLLER,{clear,Nodes,Options},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% flush()={ok,NodeResults} | NodeResult
-%% flush(Nodes)={ok,NodeResults}
-%% Nodes=[Node,...]
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok | {error,Reason}
-%% Sends a flush request to the trace-port driver on the nodes in Nodes.
-%% There will be an error for nodes that are not tracing. It is not an error to
-%% try to flush runtime components not using a trace-port.
-flush() ->
- gen_server:call(?CONTROLLER,{flush,all},?CALL_TIMEOUT).
-flush(Nodes) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{flush,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% stop()=shutdown
-%%
-%% Stops the controll component. Runtime components are left as is. They will
-%% behave according to their dependency values.
-stop() ->
- case catch gen_server:call(?CONTROLLER,stop,?CALL_TIMEOUT) of
- shutdown ->
- shutdown;
- {'EXIT',{noproc,_}} ->
- shutdown;
- {'EXIT',Reason} ->
- exit(Reason)
- end.
-%% -----------------------------------------------------------------------------
-
-%% stop_nodes()={ok,NodeResults}|NodeResult
-%% stop_nodes(Nodes)={ok,NodeResults}|{error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason}
-%%
-%% Stops runtime component on Nodes. stop_nodes/0 will if the control component
-%% is running on a distributed node stop all runtime components. And if running
-%% on a non distributed node, stop the local and only runtime component.
-stop_nodes() ->
- gen_server:call(?CONTROLLER,{stop_nodes,all},?CALL_TIMEOUT).
-stop_nodes(Nodes) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{stop_nodes,Nodes},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% stop_all()={ok,NodeResults}|NodeResult
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason}
-%%
-%% A combination of stop/0 and stop_nodes/0.
-stop_all() ->
- gen_server:call(?CONTROLLER, stop_all,?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% tp(Nodes,Module,Function,Arity,MatchSpec,Opts)={ok,NodeResults}|{error,Reason}
-%% tp(Nodes,Module,Function,Arity,MatchSpec)={ok,NodeResults}|{error,Reason}
-%% tp(Module,Function,Arity,MatchSpec)={ok,NodeResults}|NodeResult|{error,Reason}
-%% tp(Nodes,PatternList)={ok,NodeResults}|{error,Reason}
-%% tp(PatternList)={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node,...]
-%% Module,Function=atom() | '_'
-%% Arity=integer() | '_'
-%% MatchSpec=true|false|[]| matchspec()
-%% PatternList=[Pattern,...],
-%% Pattern={Module,Function,Arity,MatchSpec,Opts},
-%% Opts=[Opt,...]
-%% Opt='only_loaded'; means that the runtime component shall not try to load
-%% a module should it not already be present in the runtime system.
-%% NodeResults=[NodeResult,...]
-%% NodeResult={ok,[Ans]}|{error,Reason},
-%% Ans=integer()|{error,Reason}
-%%
-%% Set trace pattern (global) on specified or all Nodes. The integer replied
-%% if the call was successfully describes the matched number of functions.
-%% The functions without a Nodes argument means all nodes, in a non-distributed
-%% environment it means the local node.
-%% When calling several nodes, the nodes are called in parallel.
-tp(Nodes,Module,Function,Arity,MatchSpec,Opts) ->
- trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,Opts}],[global]).
-
-tp(Nodes,Module,Function,Arity,MatchSpec) when is_list(Nodes) ->
- trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,[]}],[global]);
-tp(Module,Function,Arity,MatchSpec,Opts) when is_atom(Module) ->
- trace_pattern(all,[{Module,Function,Arity,MatchSpec,Opts}],[global]).
-
-tp(Module,Function,Arity,MatchSpec) ->
- trace_pattern(all,[{Module,Function,Arity,MatchSpec,[]}],[global]).
-
-tp(Nodes,PatternList) ->
- trace_pattern(Nodes,PatternList,[global]).
-
-tp(PatternList) ->
- trace_pattern(all,PatternList,[global]).
-%% -----------------------------------------------------------------------------
-
-%% tpl(Nodes,Module,Function,Arity,MatchSpec)={ok,NodeResults}|{error,Reason}
-%% tpl(Module,Function,Arity,MatchSpec)={ok,NodeResults}|NodeResult|{error,Reason}
-%% tpl(Nodes,PatternList)={ok,NodeResults}|{error,Reason}
-%% tpl(PatternList)={ok,NodeResults}|NodeResult|{error,Reason}
-%% see tp/X for description.
-%%
-%% Set trace pattern (local) on specified or all Nodes. The integer replied
-%% if the command was successfully describes the matched number of functions.
-%% The functions without a Nodes argument means all nodes, in a non-distributed
-%% environment it means the local node.
-%% When calling several nodes, the nodes are called in parallel.
-tpl(Nodes,Module,Function,Arity,MatchSpec,Opts) ->
- trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,Opts}],[local]).
-
-tpl(Nodes,Module,Function,Arity,MatchSpec) when is_list(Nodes) ->
- trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,[]}],[local]);
-tpl(Module,Function,Arity,MatchSpec,Opts) when is_atom(Module) ->
- trace_pattern(all,[{Module,Function,Arity,MatchSpec,Opts}],[local]).
-
-tpl(Module,Function,Arity,MatchSpec) ->
- trace_pattern(all,[{Module,Function,Arity,MatchSpec,[]}],[local]).
-
-tpl(Nodes, PatternList) ->
- trace_pattern(Nodes,PatternList,[local]).
-
-tpl(PatternList) ->
- trace_pattern(all,PatternList,[local]).
-%% -----------------------------------------------------------------------------
-
-%% ctp(Nodes,Module,Function,Arity)={ok,NodeResults}|{error,Reason}
-%% ctp(Module,Function,Arity)={ok,NodeResults}|NodeResult|{error,Reason}
-%% ctp(Nodes,PatternList)={ok,NodeResults}|{error,Reason}
-%% ctp(PatternList)={ok,NodeResults}|NodeResult|{error,Reason}
-%% PatternList=[{Mod,Func,Arity},...]
-%% see tp/X for other argument descriptions.
-%%
-%% Clear trace pattern (global) on specified or all Nodes. The integer replied
-%% if the call was successfully describes the matched number of functions.
-%% The functions without a Nodes argument means all nodes, in a non-distributed
-%% environment it means the local node.
-%% When calling several nodes, the nodes are called in parallel.
-ctp(Nodes,Module,Function,Arity) ->
- trace_pattern(Nodes,[{Module,Function,Arity,false,[only_loaded]}],[global]).
-
-ctp(Module,Function,Arity) ->
- trace_pattern(all,[{Module,Function,Arity,false,[only_loaded]}],[global]).
-
-ctp(Nodes,PatternList) when is_list(PatternList) ->
- trace_pattern(Nodes,
- lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
- [global]).
-
-ctp(PatternList) when is_list(PatternList) ->
- trace_pattern(all,
- lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
- [global]).
-%% -----------------------------------------------------------------------------
-
-%% ctpl(Nodes,Module,Function,Arity)={ok,NodeResults}|{error,Reason}
-%% ctpl(Module,Function,Arity)={ok,NodeResults}|NodeResult|{error,Reason}
-%% ctpl(Nodes,PatternList)={ok,NodeResults}|{error,Reason}
-%% ctpl(PatternList)={ok,NodeResults}|NodeResult|{error,Reason}
-%% see ctp/X for argument description.
-%%
-%% Clear trace pattern (local) on specified or all Nodes. The integer replied
-%% if the call was successfully describes the matched number of functions.
-%% The functions without a Nodes argument means all nodes, in a non-distributed
-%% environment it means the local node.
-%% When calling several nodes, the nodes are called in parallel.
-ctpl(Nodes,Module,Function,Arity) ->
- trace_pattern(Nodes,[{Module,Function,Arity,false,[only_loaded]}],[local]).
-
-ctpl(Module,Function,Arity) ->
- trace_pattern(all,[{Module,Function,Arity,false,[only_loaded]}],[local]).
-
-ctpl(Nodes,PatternList) when is_list(PatternList) ->
- trace_pattern(Nodes,
- lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
- [local]).
-
-ctpl(PatternList) when is_list(PatternList) ->
- trace_pattern(all,
- lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
- [local]).
-%% -----------------------------------------------------------------------------
-
-%% Help function doing the control component calling for all tp/X, tpl/X, ctp/X
-%% and ctpl/X functions.
-trace_pattern(Nodes,Patterns,FlagList) ->
- gen_server:call(?CONTROLLER, {trace_pattern, Nodes, Patterns, FlagList},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-%% -----------------------------------------------------------------------------
-
-%% tf(Nodes,PidSpec,FlagList)={ok,NodeResults}|{error,Reason}
-%% tf(PidSpec,FlagList)={ok,NodeResults}|NodeResult|{error,Reason}
-%% tf(Nodes,TraceConfList)={ok,NodeResults}|{error,Reason}
-%% tf(NodeTraceConfList)={ok,NodeResults}|{error,Reason}
-%% tf(TraceConfList)={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node,...],
-%% NodeTraceConfList=[{Node,TraceConfList}]
-%% TraceConfList=[{PidSpec,FlagList},...],
-%% FlagList=[Flags],
-%% PidSpec=all|new|existing|pid()|locally_registered_name()
-%% Flags= all process trace flags allowed.
-%% NodeResult={ok,[Ans]}|{error,Reason},
-%% Ans=integer() | {error,Reason}
-%%
-%% Set process trace flags on processes on all or specified Nodes. The integer
-%% return if the call was successfully describes the matched number of processes.
-%% The functions without a Nodes argument means all nodes, in a non-distributed
-%% environment it means the local node.
-%% There are many combinations which does not make musch scense. For instance
-%% specifying a certain pid at all nodes. Or an empty TraceConfList for all
-%% nodes.
-%% When calling several nodes, the nodes are called in parallel.
-tf(Nodes,PidSpec,FlagList) when is_list(Nodes),is_list(FlagList) ->
- trace_flags(Nodes,[{PidSpec, FlagList}],true).
-
-tf(Nodes,TraceConfList) when is_list(Nodes),is_list(TraceConfList) ->
- trace_flags(Nodes,TraceConfList,true);
-tf(PidSpec,FlagList) when is_list(FlagList) ->
- trace_flags(all,[{PidSpec,FlagList}],true).
-
-tf(ArgList) when is_list(ArgList) -> % This one has triple functionality!
- case ArgList of
- [{_Process,Flags}|_] when is_list(Flags),is_atom(hd(Flags))-> % A call to all nodes.
- trace_flags(all,ArgList,true);
- [{_Node,TraceConfList}|_] when is_list(TraceConfList),is_tuple(hd(TraceConfList)) ->
- trace_flags(ArgList,true);
- [{_Node,_TraceConfList,_How}|_] ->
- trace_flags(ArgList);
- [] -> % Stupid but allowed.
- trace_flags(all,ArgList,true) % Actually doesn't matter which we choose.
- end.
-%% -----------------------------------------------------------------------------
-
-%% ctf(Nodes,PidSpec,FlagList)={ok,NodeResults}|{error,Reason}
-%% ctf(PidSpec,FlagList)={ok,NodeResults}|NodeResult|{error,Reason}
-%% ctf(Nodes,TraceConfList)={ok,NodeResults}|{error,Reason}
-%% ctf(TraceConfList)={ok,NodeResults}|NodeResult|{error,Reason}
-%% see tf/X for arguments.
-%%
-%% Clear process trace flags on all or specified Nodes. The integer replied
-%% if the command was successfully describes the matched number of processes.
-%% The functions without a Nodes argument means all nodes, in a non-distributed
-%% environment it means the local node.
-%% When calling several nodes, the nodes are called in parallel.
-ctf(Nodes,PidSpec,FlagList) when is_list(Nodes),is_list(FlagList) ->
- trace_flags(Nodes,[{PidSpec,FlagList}],false).
-
-ctf(Nodes,TraceConfList) when is_list(Nodes),is_list(TraceConfList) ->
- trace_flags(Nodes,TraceConfList,false);
-ctf(PidSpec,FlagList) when is_list(FlagList) ->
- trace_flags(all,[{PidSpec,FlagList}],false).
-
-ctf(TraceConfList) when is_list(TraceConfList) ->
- trace_flags(all,TraceConfList,false).
-%% -----------------------------------------------------------------------------
-
-%% ctf_all(Nodes)={ok,NodeResults}|{error,Reason}
-%% ctf_all()={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node,...],
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason},
-%%
-%% Clears all trace flags on all or specified nodes. Just for convenience.
-ctf_all() ->
- gen_server:call(?CONTROLLER,{trace_flags,all,[{all,[all]}],false},?CALL_TIMEOUT).
-
-ctf_all(Nodes) ->
- gen_server:call(?CONTROLLER,{trace_flags,Nodes,[{all,[all]}],false},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% Help function to tf/X and ctf/X making the call to the control component.
-trace_flags(Nodes, TraceConfList, How) ->
- gen_server:call(?CONTROLLER, {trace_flags, Nodes, TraceConfList, How},?CALL_TIMEOUT).
-
-trace_flags(NodeTraceConfList,How) ->
- gen_server:call(?CONTROLLER,{trace_flags,NodeTraceConfList,How},?CALL_TIMEOUT).
-
-trace_flags(NodeTraceConfListHow) ->
- gen_server:call(?CONTROLLER,{trace_flags,NodeTraceConfListHow},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-%% -----------------------------------------------------------------------------
-
-%% tpm_localnames()={ok,NodeResults}|NodeResult|{error,Reason}
-%% tpm_localnames(Nodes)={ok,NodeResults}|{error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,N}|{error,Reason}, Note that N can only be 0 or 1.
-%%
-%% Quick version for setting meta-trace patterns on erlang:register/2. It uses
-%% a default CallFunc and ReturnFunc in the meta-tracer server.
-%% The main purpose of this function is to create ti-log entries for printing
-%% the aliases for process instead of their process identities.
-tpm_localnames() ->
- tpm_localnames(all).
-
-tpm_localnames(Nodes) ->
- gen_server:call(?CONTROLLER,{meta_pattern,Nodes,{local_register,[]}},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% tpm_globalnames()={ok,NodeResults}|NodeResult|{error,Reason}
-%% tpm_globalnames(Nodes)={ok,NodeResults}|{error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={SubResult,SubResult}
-%% SubResult={ok,N}|{error,Reason}, Note that N can only be 0 or 1.
-%% As tpm_locanames/0,/1 but for registering names with global. Note that this
-%% actually involves setting meta trace patterns on two functions in global.
-tpm_globalnames() ->
- tpm_globalnames(all).
-
-tpm_globalnames(Nodes) ->
- gen_server:call(?CONTROLLER,{meta_pattern,Nodes,{global_register,[]}},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% init_tpm(Mod,Func,Arity,CallFunc)={ok,NodeResults}|NodeResult|{error,Reason}
-%% init_tpm(Nodes,Mod,Func,Arity,CallFunc)={ok,NodeResults}|{error,Reason}
-%% init_tpm(Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc)=
-%% {ok,NodeResults}|NodeResult|{error,Reason}
-%% init_tpm(Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc)=
-%% {ok,NodeResults}|{error,Reason}
-%%
-%% Mod,Func=Pointing out the function which shall be meta traced, atom().
-%% Arity=As above, integer().
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason}
-%%
-%% InitFunc,RemoveFunc={Module,Function}|fun(), functions being called when
-%% to initialize the public loopdata structure, and to reset it.
-%% InitFunc(Mod,Func,Arity,PublLD)->{ok,NewPublLD,Output}
-%% Supposed to initialize whatever needs to be done before
-%% handling any incoming meta-trace message for the Mod:Func/Arity.
-%% RemoveFunc(Mod,Func,Arity,PublLD)->{ok,NewPublLD}
-%% Called when meta tracing of Mod:Func/Arity is stopped. It is supposed
-%% to clear datastructures away from the PublLD.
-%% Initializes the public loopdata for this function. Note that we can not use wildcards
-%% here (even if it is perfectly legal in Erlang). It also sets the CallFunc and
-%% ReturnFunc for the meta traced function. The function is hence ready to be
-%% meta traced with either tpm/5 or tpm_ms/5.
-%% When calling several nodes, the nodes are called in parallel.
-init_tpm(Mod,Func,Arity,CallFunc) ->
- init_tpm(all,Mod,Func,Arity,CallFunc).
-
-init_tpm(Nodes,Mod,Func,Arity,CallFunc) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,
- Nodes,
- {init_tpm,
- [Mod,Func,Arity,CallFunc]}},
- ?CALL_TIMEOUT).
-
-init_tpm(Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- init_tpm(all,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc).
-
-init_tpm(Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,
- Nodes,
- {init_tpm,
- [Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc]}},
- ?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% tpm(Mod,Func,Arity,MS)={ok,NodeResults}|NodeResult|{error,Reason}
-%% tpm(Nodes,Mod,Func,Arity,MS)={ok,NodeResults}|{error,Reason}
-%% tpm(Mod,Func,Arity,MS,CallFunc)={ok,NodeResults}|NodeResults|{error,Reason}
-%% tpm(Nodes,Mod,Func,Arity,MS,CallFunc)={ok,NodeResults}|{error,Reason}
-%% tpm(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc)=
-%% {ok,NodeResults}|NodeResults|{error,Reason}
-%% tpm(Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc)=
-%% {ok,NodeResults}|{error,Reason}
-%%
-%% Mod,Func=atom() and not '_'.
-%% Arity=integer()
-%% MS=list(), matchspecification.
-%% Nodes=List of nodenames.
-%% InitFunc,CallFunc,ReturnFunc,RemoveFunc={Module,Function}|fun(),
-%% functions being called when these functions are called by the meta trace
-%% server at certain events.
-%% CallFunc(CallingPid,ActualArgList,PublLD)->{ok,NewPrivLD,Output}
-%% ReturnFunc(CallingPid,ReturnValue,PublLD)->{ok,NewPrivLD,Output}
-%% When a call respectively return_from trace message arrives for the meta
-%% traced function, the corresponding function is called.
-%% The ReturnFunc must handle the fact that a return_from message arrives
-%% for a call which was never noticed. This because the message queue of the
-%% meta tracer may have been emptied.
-%%
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,N}|{error,Reason}, Note that N can only be 0 or 1.
-%%
-%% Activates meta-tracing in the inviso_rt_meta tracer. Except when using tpm/6,/8
-%% and /9 the function must first have been initiated using init_tpm. If running
-%% a non distributed system the variants without Node shall be used. If running
-%% in a distributed environment, without Node means all our nodes.
-%% When calling several nodes, the nodes are called in parallel.
-tpm(Mod,Func,Arity,MS) ->
- tpm(all,Mod,Func,Arity,MS).
-
-tpm(Nodes,Mod,Func,Arity,MS) when is_integer(Arity) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,Nodes,{tpm,[Mod,Func,Arity,MS]}});
-tpm(Mod,Func,Arity,MS,CallFunc) when is_integer(Arity) ->
- tpm(all,Mod,Func,Arity,MS,CallFunc).
-
-tpm(Nodes,Mod,Func,Arity,MS,CallFunc) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,Nodes,{tpm,[Mod,Func,Arity,MS,CallFunc]}}).
-
-tpm(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- tpm(all,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc).
-
-tpm(Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,
- Nodes,
- {tpm,
- [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc]}},
- ?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% Same as tpm/X but the meta tracer will append {tracer,Tracer} to any enable
-%% list in a trace body action term.
-tpm_tracer(Mod,Func,Arity,MS) ->
- tpm_tracer(all,Mod,Func,Arity,MS).
-
-tpm_tracer(Nodes,Mod,Func,Arity,MS) when is_integer(Arity) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,Nodes,{tpm_tracer,[Mod,Func,Arity,MS]}},
- ?CALL_TIMEOUT);
-tpm_tracer(Mod,Func,Arity,MS,CallFunc) when is_integer(Arity) ->
- tpm_tracer(all,Mod,Func,Arity,MS,CallFunc).
-
-tpm_tracer(Nodes,Mod,Func,Arity,MS,CallFunc) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,Nodes,{tpm_tracer,[Mod,Func,Arity,MS,CallFunc]}}).
-
-tpm_tracer(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- tpm_tracer(all,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc).
-
-tpm_tracer(Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,
- Nodes,
- {tpm_tracer,
- [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc]}},
- ?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% tpm_ms(Mod,Func,Arity,MSname,MS)={ok,NodeResults}|NodeResult|{error,Reason}
-%% tpm_ms(Nodes,Mod,Func,Arity,MSname,MS)={ok,NodeResults}|{error,Reason}
-%% Nodes= List of all nodes where the function shall be carried out.
-%% Mod,Func=Pointing out the function to which we shall add a match-spec., atom().
-%% Arity=As above, integer().
-%% MSname=A name to be used if this MS shall be removed later. term().
-%% MatchSpec=List of match specification, Remember {return_trace}
-%% if expecting return_from messages.
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,1}|{ok,0}|{error,Reason} where {ok,1} indicates that
-%% setting the matchspecification for the function succeeded.
-%%
-%% This function adds a list of match-specs to the already existing ones. It
-%% uses an internal database to keep track of existing match-specs. If the
-%% match-spec does not result in any meta traced functions (for whatever reason),
-%% the MS is not saved in the database. The previously known match-specs are
-%% not removed.
-%% The function must previously have been initiated in order for this function
-%% to add a match-spec.
-%% When calling several nodes, the nodes are called in parallel.
-tpm_ms(Mod,Func,Arity,MSname,MS) ->
- tpm_ms(all,Mod,Func,Arity,MSname,MS).
-
-tpm_ms(Nodes,Mod,Func,Arity,MSname,MS) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,Nodes,{tpm_ms,[Mod,Func,Arity,MSname,MS]}},
- ?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% Same as tpm_ms/5, /6 but the meta tracer will append {tracer,Tracer} to any enable
-%% list in a trace body action term.
-tpm_ms_tracer(Mod,Func,Arity,MSname,MS) ->
- tpm_ms_tracer(all,Mod,Func,Arity,MSname,MS).
-
-tpm_ms_tracer(Nodes,Mod,Func,Arity,MSname,MS) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,Nodes,{tpm_ms_tracer,[Mod,Func,Arity,MSname,MS]}},
- ?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% ctpm_ms(Mod,Func,Arity,MSname)={ok,NodeResults}|NodeResult|{error,Reason}
-%% ctpm_ms(Nodes,Mod,Func,Arity,MSname)={ok,NodeResults}|{error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason}
-%%
-%% Removes a named match-spec from the meta traced function. Note that it never
-%% is a fault to remove an MS. Not even from a function which is non existant.
-%% When calling several nodes, the nodes are called in parallel.
-ctpm_ms(Mod,Func,Arity,MSname) ->
- ctpm_ms(all,Mod,Func,Arity,MSname).
-
-ctpm_ms(Nodes,Mod,Func,Arity,MSname) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,Nodes,{ctpm_ms,[Mod,Func,Arity,MSname]}},
- ?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% ctpm(Mod,Func,Arity)={ok,NodeResults}|NodeResult|{error,Reason}
-%% ctpm(Node,Mod,Func,Arity)={ok,NodeResults}|{error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason}
-%%
-%% Removes the meta trace pattern for the function, means stops generating output
-%% for this function. The public LD may be cleared by the previously entered
-%% RemoveFunc.
-%% When calling several nodes, the nodes are called in parallel.
-ctpm(Mod,Func,Arity) ->
- ctpm(all,Mod,Func,Arity).
-
-ctpm(Nodes,Mod,Func,Arity) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,Nodes,{ctpm,[Mod,Func,Arity]}},
- ?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% ctpm_localnames()={ok,NodeResults}|NodeResult|{error,Reason}
-%% ctpm_localnames(Nodes)={ok,NodeResults}|{error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason}
-%%
-%% Removes meta-trace pattern for erlang:register/2, previously set by tpm_localnames.
-%% When calling several nodes, the nodes are called in parallel.
-ctpm_localnames() ->
- ctpm_localnames(all).
-
-ctpm_localnames(Nodes) ->
- gen_server:call(?CONTROLLER,{meta_pattern,Nodes,{remove_local_register,[]}},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% ctpm_localnames()={ok,NodeResults}|NodeResult|{error,Reason}
-%% ctpm_localnames(Nodes)={ok,NodeResults}|{error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={SubResult,Subresult}
-%% SubResult=ok|{error,Reason}
-%%
-%% Removes meta-trace pattern for the register functions in global. Note that there
-%% are two of them.
-%% When calling several nodes, the nodes are called in parallel.
-ctpm_globalnames() ->
- ctpm_globalnames(all).
-
-ctpm_globalnames(Nodes) ->
- gen_server:call(?CONTROLLER,{meta_pattern,Nodes,{remove_global_register,[]}},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% ctp_all(Nodes)={ok,NodeResults}|{error,Reason}
-%% ctp_all()={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node,...],
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason},
-%%
-%% Clears all both global and local trace patterns on all or specified nodes.
-%% Does not effect meta patterns.
-ctp_all() ->
- gen_server:call(?CONTROLLER,{ctp_all,all},?CALL_TIMEOUT).
-
-ctp_all(Nodes) ->
- gen_server:call(?CONTROLLER,{ctp_all,Nodes},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% suspend(Nodes,Reason)={ok,NodeResults}|{error,Reason}
-%% suspend(Reason)={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node],
-%% Reason=term(); supposed to describe the reason why suspended.
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason},
-%%
-%% Suspend all or specified Nodes with reason Reason. Suspend means that all
-%% process trace flags are removed and all meta-patterns.
-suspend(Nodes, Reason) ->
- gen_server:call(?CONTROLLER,{suspend,Reason,Nodes},?CALL_TIMEOUT).
-
-suspend(Reason) ->
- gen_server:call(?CONTROLLER,{suspend,Reason,all},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% cancel_suspension(Nodes)={ok,NodeResults}|{error,Reason}
-%% cancel_suspension()={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node,...],
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason},
-%% Cancel suspension on all or specified Nodes. Note that this does not imply
-%% that "business" is resumed as before. You must reactivate flags and meta-patter
-%% your self.
-cancel_suspension(Nodes) ->
- gen_server:call(?CONTROLLER,{cancel_suspension,Nodes},?CALL_TIMEOUT).
-
-cancel_suspension() ->
- gen_server:call(?CONTROLLER,{cancel_suspension,all},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% get_status(Nodes)={ok,NodeResults}|{error,Reason}
-%% get_status()={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node,...],
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,{State,Status}}|{error,Reason},
-%% State=new|idle|tracing
-%% Status=running|{suspended,SReason}
-%%
-%% Get Status form all or specified runtime components.
-get_status(Nodes) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{get_status,Nodes},?CALL_TIMEOUT).
-
-get_status() ->
- gen_server:call(?CONTROLLER,{get_status,all},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% get_tracerdata()={ok,NodeResults}|NodeResult|{error,Reason}
-%% get_tracerdata(Nodes)={ok,NodeResults}|{error,Reason}
-%% Nodes=[Node,...],
-%% NodeResult={ok,NResult}|{error,Reason},
-%% NResult=TracerData|no_tracerdata
-%% TracerData will be exactly as it was specified when doing init_tracing.
-%%
-%% Get TracerData form all or specified runtime components.
-get_tracerdata() ->
- gen_server:call(?CONTROLLER,{get_tracerdata,all},?CALL_TIMEOUT).
-
-get_tracerdata(Nodes) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{get_tracerdata,Nodes},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% list_logs(TracerData)={ok,NodeResults}|NodeResult|{error,Reason}
-%% list_logs(NodeList)={ok,NodeResults}|{error,Reason}
-%% list_logs()={ok,NodeResults}|NodeResult|{error,Reason}
-%% TracerData see init_tracing/1/2
-%% NodeList=[NodeSpec,...]
-%% NodeSpec=Node|{Node,TracerData}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,FileList}|{ok,no_log}|{error,Reason}
-%% FileList=[FileType,...], one or more of different types.
-%% FileType={trace_log,Dir,Files}|{ti_log,Dir,Files}
-%% Files=[FileNameWithOutPath,...]
-%%
-%% Ask local or specified runtime components for now existing logs given
-%% TracerData. If TracerData is left out, the runtime components TracerData,
-%% if existing, will be used instead.
-list_logs() ->
- gen_server:call(?CONTROLLER,list_logs,?CALL_TIMEOUT).
-
-list_logs(TracerDataOrNodesList) when is_list(TracerDataOrNodesList) ->
- gen_server:call(?CONTROLLER,{list_logs,TracerDataOrNodesList},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% fetch_log(LogSpecList,DestDir,Prefix)={ok,NodeResults}|{error,not_distributed}|
-%% {error,Reason}
-%% fetch_log(DestDir,Prefix)={ok,NodeResults}|{error,not_distributed}|
-%% {error,Reason}
-%% fetch_log(ToNode,DestDir,Prefix)=
-%% fetch_log(ToNode,LogSpecList,DestDir,Prefix)=
-%% DestDir=string(), to where the fetched files shall be placed.
-%% Prefix=string(), prefix on locally saved fetched files.
-%% LogSpecList=[LogSpec,...],
-%% LogSpec={Node,FileSpecList}|Node|{Node,TracerData}
-%% TracerData=see init_tracing/1/2
-%% FileSpecList=[{trace_log,Dir,FileList},{ti_log,Dir,FileList}]
-%% where each tuple-item is optional.
-%% FileList=[RemoteFileName,...]
-%% ToNode=atom()
-%% NodeResult={Conclusion,ResultFileSpec}|no_log|{error,NReason}
-%% Conclusion=complete|incomplete
-%% ResultFileSpec=[{trace_log,FileResults},{ti_log,FileResults}]
-%% FileResults=[FileResult,...]
-%% FileResult={ok,FileName}|{error,FReason}
-%% NReason=own_node|Reason
-%% FReason = {file_open,{posix(),FileName}} |
-%% {file_open,{posix(),RemoteFileName}}
-%% {file_open,{posix(),[DestDir,Prefix,RemoteFileName]}}
-%% {file_write,{posix(),FileName}} |
-%% {truncated,FileName}
-%% {truncated,{Reason,FileName}}
-%% posix() - an atom which is named from the Posix error codes used in
-%% Unix, and in the runtime libraries of most C compilers.
-%% See module file in Kernel Reference manual.
-%%
-%% Copies logfiles over distributed erlang to ToNode. This
-%% function can only be used in a distributed system.
-%% The resulting transfered files will have the prefix Prefix and will be
-%% located in DestDir.
-%% Note that the client process using this function will wait until all files
-%% are moved. The job can be cancelled, causing any already copied files to be
-%% removed, by simply terminating the waiting client process.
-fetch_log(DestDir,Prefix) when is_list(DestDir),is_list(Prefix) ->
- gen_server:call(?CONTROLLER,{fetch_log,node(),all,DestDir,Prefix},infinity).
-
-fetch_log(ToNode,DestDir,Prefix) when is_atom(ToNode),is_list(DestDir),is_list(Prefix) ->
- gen_server:call(?CONTROLLER,{fetch_log,ToNode,all,DestDir,Prefix},infinity);
-
-fetch_log(LogSpecList,DestDir,Prefix) when is_list(LogSpecList),is_list(DestDir),is_list(Prefix) ->
- gen_server:call(?CONTROLLER,{fetch_log,node(),LogSpecList,DestDir,Prefix},infinity).
-
-fetch_log(ToNode,LogSpecList,DestDir,Prefix)
- when is_atom(ToNode),is_list(LogSpecList),is_list(DestDir),is_list(Prefix) ->
- gen_server:call(?CONTROLLER,{fetch_log,ToNode,LogSpecList,DestDir,Prefix},infinity).
-%% ------------------------------------------------------------------------------
-
-%% delete_log(Nodes,TracerData)={ok,NodeResults}|{error,Reason}
-%% delete_log(NodeSpecList)={ok,NodeResults}|{error,Reason}
-%% delete_log(Spec)={ok,NodeResults}|NodeResult|{error,Reason}
-%% delete_log(TracerData)={ok,NodeResults}|NodeResult|{error,Reason}
-%% delete_log()={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node,...],
-%% NodeSpecList=[{Node,Spec},...]
-%% Spec=[AbsPathFileName,...]|LogSpecs
-%% LogSpecs=[LogSpec,...]
-%% LogSpec={trace_log,Dir,[FileNameWithoutPath,...]}|
-%% {ti_log,Dir,[FileNameWithoutPath,...]}
-%% TracerData = see init_tracing/1/2
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,no_log}|{ok,LogInfos}|{ok,FileInfos}
-%% LogInfos=[LogInfo,...]
-%% LogInfo={trace_log,FileInfos}|{ti_log,FileInfos}
-%% FileInfos=[FileInfo,...]
-%% FileInfo={ok,FileName}|{error,Reason} whether FileName contains
-%% full path or not depends on if AbsPathFileName or LogSpec was
-%% used when specifying the files.
-%%
-%% Deletes listed files or files corresponding to TracerData from specified
-%% or all Nodes. If no TracerData or list of files is specified in the call the
-%% TracerData at Node will be used to identify log files to delete.
-delete_log() ->
- gen_server:call(?CONTROLLER,{delete_log,all},?CALL_TIMEOUT).
-delete_log(What) ->
- gen_server:call(?CONTROLLER,{delete_log,What},?CALL_TIMEOUT).
-delete_log(Nodes,Spec) ->
- gen_server:call(?CONTROLLER,{delete_log,Nodes,Spec},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% subscribe()= same as subscribe(self())
-%% subscribe(Pid)=ok|{error,Reason}
-%% Pid=pid(),
-%% Reason =
-%% Add Pid or self() to event sending list. Note that it is possible to add a
-%% pid several times and that the Pid then will receive several event messages.
-%% All events will be sent to all subscribers in the event sending list.
-%% Event={inviso_event,ControllerPid,erlang:localtime(),Msg},
-%% Msg=
-%% {connected, Node, {Tag, {State, Status}}}
-%% {disconnected, Node, not_applicable}
-%% {state_change, Node, {State, Status}}
-%% {port_down, Node, Reason}
-%% Node = node() | local_runtime (when running in a non-distributed
-%% environment)
-subscribe() ->
- gen_server:call(?CONTROLLER,{subscribe,self()},?CALL_TIMEOUT).
-
-subscribe(Pid) ->
- gen_server:call(?CONTROLLER,{subscribe,Pid},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% unsubscribe()= same as unsubscribe(self())
-%% unsubscribe(Pid)=ok
-%% Pid=pid(),
-%%
-%% Remove, if present, first occurrence of Pid or self() from event sending
-%% list. Note that it is not an error to remove a non existing subscription.
-unsubscribe() ->
- gen_server:call(?CONTROLLER,{unsubscribe,self()},?CALL_TIMEOUT).
-
-unsubscribe(Pid) ->
- gen_server:call(?CONTROLLER,{unsubscribe,Pid},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-
-
-%% debuging the controller
-%% ----------------------------------------------------------------------------
-state() ->
- ?CONTROLLER ! state.
-
-%% debuging the runtime component
-state(Node) ->
- ?CONTROLLER ! {state, Node}.
-
-%%% end of file
diff --git a/lib/inviso/src/inviso_c.erl b/lib/inviso/src/inviso_c.erl
deleted file mode 100644
index b1597a7f35..0000000000
--- a/lib/inviso/src/inviso_c.erl
+++ /dev/null
@@ -1,1335 +0,0 @@
-%% ``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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights 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$
-%%
-%% Author: Ann-Marie L�f, [email protected]
-%% Lennart �hman, [email protected]
-%%
-%% Description: The controlling part of the trace tool Inviso
-%%
-%% This code implements the inviso control component meant to be run on an Erlang
-%% node doing any tracing. It can also be used in a non distributed system where
-%% the control component and the runtime component will run on the same virtual
-%% machine.
-%% The control component is not meant to be started by a supervisor but rather
-%% directly by the user when needed.
-%% This module does not provide any APIs to users. Those are found in inviso.erl.
-%% This module merly has the gen_server call-backs.
-%% ------------------------------------------------------------------------------
-
--module(inviso_c).
--behavior(gen_server).
-
-
-%% ------------------------------------------------------------------------------
-%% gen_server callbacks.
-%% ------------------------------------------------------------------------------
--export([init/1,
- handle_call/3,handle_cast/2,handle_info/2,
- terminate/2,
- code_change/3]).
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Exported internal functions (used in spawn of help process).
-%% ------------------------------------------------------------------------------
--export([log_rec_init/4]).
-
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Records.
-%% ------------------------------------------------------------------------------
-
-%% #state
-%% Record used in the loopdata.
--record(state,{
- nodes=[], % [#node,...]
- distributed, % false | true
- subscribers=[], % [{pid(),monitor_ref()},...]
- rt_options=[{dependency,{infinity,node()}},{overload, default}]
- }).
-%% ------------------------------------------------------------------------------
-
-%% #node
-%% Record storing information about a runtime component connected to this control
-%% component.
--record(node,{
- node, % [atom(),...]
- pid, % pid()
- vsn,
- ref, % monitor_ref()
- tag % term()
- }).
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Macros used in this module.
-%% ------------------------------------------------------------------------------
-
--define(RUNTIME,inviso_rt). % The module API name of the runtime.
-%% ------------------------------------------------------------------------------
-
-
-
-%% ==============================================================================
-%% Controller component implmentation.
-%% ==============================================================================
-
-init({_Parent,Options}) ->
- process_flag(trap_exit,true),
- case check_options(Options,start) of
- {ok,Options2} ->
- LoopData=initiate_state(Options2),
- {ok,LoopData};
- Error ->
- {stop,Error}
- end.
-%% ------------------------------------------------------------------------------
-
-handle_call({subscribe,Pid},_From,LD) when is_pid(Pid) ->
- MRef=erlang:monitor(process,Pid),
- {reply,ok,LD#state{subscribers=[{Pid,MRef}|LD#state.subscribers]}};
-handle_call({subscribe,Faulty},_From,LD) ->
- {reply,{error,{badarg,Faulty}},LD};
-handle_call({unsubscribe,Pid},_From,LD) ->
- case lists:keysearch(Pid,1,LD#state.subscribers) of
- {value,{_,MRef}} ->
- erlang:demonitor(MRef),
- {reply,ok,LD#state{subscribers=lists:keydelete(Pid,1,LD#state.subscribers)}};
- false ->
- {reply,ok,LD}
- end;
-handle_call({add_nodes,Nodes,Opts,Tag,Condition},_From,LD) ->
- case check_options(Opts,add_node) of
- {ok,Opts2} ->
- Opts3=merge_options(LD#state.rt_options,Opts2),
- {NewLD,Reply}=do_add_nodes(Nodes,LD,Opts3,Tag,Condition),
- {reply,adapt_reply(NewLD,Reply),NewLD};
- Error ->
- {reply,Error,LD}
- end;
-handle_call({change_options,Nodes,Opts},_From,LD) ->
- case check_options(Opts,add_node) of
- {ok,Opts2} ->
- {reply,adapt_reply(LD,do_change_option(Nodes,Opts2,LD)),LD};
- Error ->
- {reply,Error,LD}
- end;
-handle_call({init_tracing,TracerDataList},_From,LD) ->
- {reply,adapt_reply(LD,do_init_tracing(TracerDataList,LD)),LD};
-handle_call({init_tracing,Nodes,TracerData},_From,LD) when is_list(Nodes) ->
- TracerDataList=
- lists:map(fun(N)->{N,TracerData} end,started_trace_nodes(Nodes,LD)),
- {reply,adapt_reply(LD,do_init_tracing(TracerDataList,LD)),LD};
-handle_call({init_tracing,Nodes,_TracerData},_From,LD) ->
- {reply,{error,{badarg,Nodes}},LD};
-handle_call({trace_pattern,Nodes,Patterns,FlagList},_From,LD) ->
- {reply,adapt_reply(LD,distribute_tp(Nodes,Patterns,FlagList,LD)),LD};
-handle_call({trace_flags,Nodes,Args,How},_From,LD) ->
- {reply,adapt_reply(LD,distribute_tf(Nodes,Args,How,LD)),LD};
-handle_call({trace_flags,NodeArgs,How},_From,LD) ->
- {reply,distribute_tf(NodeArgs,How,LD),LD}; % Always distributed here.
-handle_call({trace_flags,NodeArgHows},_From,LD) ->
- {reply,distribute_tf(NodeArgHows,LD),LD}; % Always distributed here.
-handle_call({meta_pattern,Nodes,Args},_From,LD) ->
- {reply,adapt_reply(LD,distribute_metapattern(Nodes,Args,LD)),LD};
-handle_call({ctp_all,Nodes},_From,LD) ->
- {reply,adapt_reply(LD,do_ctp_all(Nodes,LD)),LD};
-handle_call({suspend,Reason,Nodes},_From,LD) ->
- {reply,adapt_reply(LD,do_suspend(Nodes,Reason,LD)),LD};
-handle_call({cancel_suspension,Nodes},_From,LD) ->
- {reply,adapt_reply(LD,do_cancel_suspension(Nodes,LD)),LD};
-handle_call({stop_tracing,Nodes},_From,LD) ->
- {reply,adapt_reply(LD,do_stop_tracing(Nodes,LD)),LD};
-handle_call({get_status,Nodes},_From,LD) ->
- {reply,adapt_reply(LD,do_get_status(Nodes,LD)),LD};
-handle_call({get_tracerdata,Nodes},_From,LD) ->
- {reply,adapt_reply(LD,do_get_tracerdata(Nodes,LD)),LD};
-handle_call(list_logs,_From,LD) ->
- {reply,adapt_reply(LD,do_list_logs(all,LD)),LD};
-handle_call({list_logs,TracerDataOrNodesList},_From,LD) ->
- {reply,adapt_reply(LD,do_list_logs(TracerDataOrNodesList,LD)),LD};
-handle_call({fetch_log,ToNode,Spec,Dest,Prefix},From,LD) ->
- case LD#state.distributed of
- true -> % It is a distributed system.
- do_fetch_log(ToNode,Spec,Dest,Prefix,From,LD),
- {noreply,LD}; % Reply will come from collector pid.
- false -> % Stupidity! you dont want this!
- {reply,{error,not_distributed},LD}
- end;
-handle_call({delete_log,NodesOrNodeSpecs},_From,LD) ->
- {reply,adapt_reply(LD,do_delete_log(NodesOrNodeSpecs,LD)),LD};
-handle_call({delete_log,Nodes,Specs},_From,LD) when is_list(Nodes) ->
- Reply=do_delete_log(lists:map(fun(N)->{N,Specs} end,Nodes),LD),
- {reply,adapt_reply(LD,Reply),LD};
-handle_call({delete_log,FaultyNodes,_Specs},_From,LD) ->
- {reply,{error,{badarg,FaultyNodes}},LD};
-handle_call({clear,Nodes,Options},_From,LD) ->
- {reply,adapt_reply(LD,do_clear(Nodes,LD,Options)),LD};
-handle_call({flush,Nodes},_From,LD) ->
- {reply,adapt_reply(LD,do_flush(Nodes,LD)),LD};
-handle_call({stop_nodes,Nodes},_From,LD) ->
- {NewLD,Reply}=do_stop_nodes(Nodes,LD),
- {reply,adapt_reply(NewLD,Reply),NewLD};
-handle_call(stop,_From,LD) ->
- {stop,normal,shutdown,LD};
-handle_call(stop_all,_From,LD) ->
- {NewLD,Reply}=do_stop_nodes(started_trace_nodes(all,LD),LD),
- {stop,normal,adapt_reply(NewLD,Reply),NewLD};
-handle_call(Request,_From,LD) -> %% for debug purpose only
- {reply,{error,{invalid_request,Request}},LD}.
-
-handle_cast(_Request,LD) -> % There are no casts.
- {noreply,LD}.
-
-handle_info({connect,_Node,Pid,_VSN,Tag},LD) -> % From connecting runtime.
- {noreply,do_confirm_connection(Pid,Tag,LD)};
-handle_info({trace_event,Event},LD) -> % A runtime component issues an event.
- send_to_subscribers(Event,LD), % Relay to our subscribers.
- {noreply,LD};
-handle_info({'DOWN',Ref,process,_From,Info},LD) -> % A runtime component died?
- {noreply,do_down_msg(Ref,Info,LD)};
-handle_info(state,LD) -> % For debug purposes.
- io:format("trace_c state: ~p~n",[LD]),
- {noreply,LD};
-handle_info(_Msg,LD) ->
- {noreply,LD}.
-
-terminate(_Reason, _LD) ->
- ok.
-
-code_change(_OldVsn, LoopData, _Extra) ->
- {ok, LoopData}.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Handle call help functions.
-%% -----------------------------------------------------------------------------
-
-%% Help function which adapts a reply based on if this is a distributed system
-%% or not. The first argument indicates distribution (='true').
-%% If we are not running a distributed system, all node references are removed.
-adapt_reply(#state{distributed=Distributed},Reply) ->
- adapt_reply(Distributed,Reply);
-adapt_reply(true,Reply) -> % We are distributed.
- Reply;
-adapt_reply(false,{ok,[{_Node,LocalReply}]}) ->
- LocalReply;
-adapt_reply(false,{ok,[]}) ->
- {error,not_an_added_node}; % �R DET H�R VERKLIGEN RIKTIGT?
-adapt_reply(false,Reply) ->
- Reply.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% First level help functions to handler functions.
-%% ==============================================================================
-
-%% Function starting a runtime component at the nodes in Nodes. If doing non
-%% distributed, Nodes will be a list of nonode@nohost.
-%% Returns {NewLD,{ok,Replies}} or {LD,{error,Reason}}.
-do_add_nodes(Nodes,LD,Options,Tag,Condition) ->
- do_add_nodes_2(Nodes,LD,Options,Tag,Condition,[]).
-
-do_add_nodes_2([Node|Tail],LD,Options,Tag,Condition,Replies) ->
- case find(fun is_started/2,LD#state.nodes,Node) of
- {value,true} -> % Already started by us.
- do_add_nodes_2(Tail,LD,Options,Tag,Condition,[{Node,{ok,already_added}}|Replies]);
- no_match ->
- case ?RUNTIME:start(Node,Options,Tag,Condition) of
- {node_info,_Node,Pid,VSN,State,Status,new} ->
- NewLD=do_add_nodes_3(Node,LD,Tag,Pid,VSN,State,Status),
- do_add_nodes_2(Tail,NewLD,Options,Tag,Condition,[{Node,{ok,new}}|Replies]);
- {node_info,_Node,Pid,VSN,State,Status,{tag,Tag2}} ->
- NewLD=do_add_nodes_3(Node,LD,Tag,Pid,VSN,State,Status),
- do_add_nodes_2(Tail,NewLD,Options,Tag,Condition,
- [{Node,{ok,{adopted,State,Status,Tag2}}}|Replies]);
- Error ->
- do_add_nodes_2(Tail,LD,Options,Tag,Condition,[{Node,Error}|Replies])
- end
- end;
-do_add_nodes_2([],LD,_,_,_,Replies) ->
- {LD,{ok,lists:reverse(Replies)}};
-do_add_nodes_2(Faulty,LD,_,_,_,_) -> % Not a list of nodes.
- {LD,{error,{badarg,Faulty}}}.
-
-do_add_nodes_3(Node,LD,Tag,Pid,VSN,State,Status) ->
- MRef=erlang:monitor(process,Pid),
- NodeRec=#node{node=Node,pid=Pid,vsn=VSN,ref=MRef,tag=Tag},
- send_to_subscribers({connected,Node,{Tag,{State,Status}}},LD),
- _NewLD=set_node_rec(NodeRec,LD).
-%% ------------------------------------------------------------------------------
-
-%% Function calling change_options sequensially on all nodes in Nodes.
-%% Returns {ok,Replies} or {error,Reason}.
-do_change_option(Nodes,Options,LD) ->
- do_change_option_2(started_trace_nodes(Nodes,LD#state.nodes),Options,LD,[]).
-
-do_change_option_2([Node|Tail],Options,LD,Replies) ->
- case get_node_rec(Node,LD) of
- Rec when is_record(Rec,node) ->
- Answer=?RUNTIME:change_options(Rec#node.pid,Options),
- do_change_option_2(Tail,Options,LD,[{Node,Answer}|Replies]);
- Error ->
- do_change_option_2(Tail,Options,LD,[{Node,Error}|Replies])
- end;
-do_change_option_2([],_Options,_LD,Replies) ->
- {ok,Replies};
-do_change_option_2(Faulty,_,_,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function which calls the runtime components in TracerDataList and initiates
-%% the tracing. TracerDataList may either be just one tracer data which shall be
-%% applied to all runtime components, or a list of nodes and tracerdata.
-do_init_tracing(TracerDataList,LD) ->
- case inviso_rt_lib:is_tracerdata(TracerDataList) of
- true -> % Then we must add all our nodes.
- List=lists:map(fun(N)->{N,TracerDataList} end,started_trace_nodes(all,LD)),
- do_init_tracing_2(List,LD,[]);
- false -> % Assume it is a list of {Node,Tracerdata}
- do_init_tracing_2(TracerDataList,LD,[])
- end.
-
-do_init_tracing_2([{Node,TracerData}|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- Rec when is_record(Rec,node) ->
- case check_modify_tracerdata(TracerData,LD) of
- {ok,NewTracerData} -> % Tracerdata ok and node->pid.
- Reply=?RUNTIME:init_tracing(Rec#node.pid,NewTracerData),
- do_init_tracing_2(Tail,LD,[{Node,Reply}|Replies]);
- {error,Reason} -> % Unknown tracerdata.
- do_init_tracing_2(Tail,LD,[{Node,{error,Reason}}|Replies])
- end;
- {error,Reason} ->
- do_init_tracing_2(Tail,LD,[{Node,{error,Reason}}|Replies])
- end;
-do_init_tracing_2([_|Tail],LD,Replies) -> % Just ignore item we don't understand.
- do_init_tracing_2(Tail,LD,Replies); % Will not end up in Replies either.
-do_init_tracing_2([],_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_init_tracing_2(What,_LD,_) ->
- {error,{badarg,What}}.
-%% -----------------------------------------------------------------------------
-
-%% Function setting trace patterns on all nodes mentioned in Nodes. Uses a
-%% parallel mechanism in the runtime component.
-%% Returns {ok,Reply} or {error,Reason}.
-distribute_tp(all,Patterns,FlagList,LD) ->
- distribute_tp(started_trace_nodes(all,LD),Patterns,FlagList,LD);
-distribute_tp(Nodes,Patterns,FlagList,LD) when is_list(Nodes) ->
- RTpids=lists:map(fun(N)->case get_node_rec(N,LD) of
- #node{pid=Pid} ->
- {Pid,N};
- Error ->
- {Error,N}
- end
- end,
- Nodes),
- {ok,?RUNTIME:trace_patterns_parallel(RTpids,Patterns,FlagList)};
-distribute_tp(Faulty,_,_,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function which in parallel sets trace flags on all nodes in Nodes. Can be
-%% either a list of node names or 'all'. Note that in the reply list there will be an
-%% error indicating nodes not reachable. Either because such a node disappeared or
-%% because it is not one of "our" nodes.
-%% Returns {ok,Reply} or {error,Reason}.
-distribute_tf(all,Args,How,LD) ->
- distribute_tf(started_trace_nodes(all,LD),Args,How,LD);
-distribute_tf(Nodes,Args,How,LD) when is_list(Nodes) ->
- RTpids=lists:map(fun(Node)->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- {Pid,Node};
- Error -> % Not an added node then!
- {Error,Node}
- end
- end,
- Nodes),
- {ok,?RUNTIME:trace_flags_parallel(RTpids,Args,How)};
-distribute_tf(Faulty,_,_,_) ->
- {error,{badarg,Faulty}}.
-
-%% As above but specific args for each node.
-distribute_tf(NodeArgs,How,LD) when is_list(NodeArgs) ->
- RTpidArgs=lists:map(fun({Node,Args})->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- {Pid,Node,Args};
- Error -> % Not an added node then!
- {Error,Node}
- end
- end,
- NodeArgs),
- {ok,?RUNTIME:trace_flags_parallel(RTpidArgs,How)};
-distribute_tf(Faulty,_,_) ->
- {error,{badarg,Faulty}}.
-
-%% As above but both specific args for each node and How (set or remove flag).
-distribute_tf(NodeArgHows,LD) when is_list(NodeArgHows) ->
- RTpidArgHows=
- lists:map(fun({Node,Args,How}) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- {Pid,Node,Args,How};
- Error -> % Not an added node then!
- {Error,Node}
- end
- end,
- NodeArgHows),
- {ok,?RUNTIME:trace_flags_parallel(RTpidArgHows)};
-distribute_tf(Faulty,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function making a parallel call to all nodes in Nodes, calling the generic
-%% meta-tracer function stated in Args.
-%% Returns {ok,Reply} or {error,Reason}.
-distribute_metapattern(all,Args,LD) ->
- distribute_metapattern(started_trace_nodes(all,LD),Args,LD);
-distribute_metapattern(Nodes,Args,LD) when is_list(Nodes) ->
- RTpids=lists:map(fun(N)->case get_node_rec(N,LD) of
- #node{pid=Pid} ->
- {Pid,N};
- Error ->
- {Error,N}
- end
- end,
- Nodes),
- {ok,?RUNTIME:meta_tracer_call_parallel(RTpids,Args)};
-distribute_metapattern(Faulty,_,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function clearing all trace patterns on all node mentioned in Nodes.
-%% Returns {ok,Reply} or {error,Reason}.
-do_ctp_all(Nodes,LD) ->
- do_ctp_all_2(started_trace_nodes(Nodes,LD),LD,[]).
-
-do_ctp_all_2([Node|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- do_ctp_all_2(Tail,LD,[{Node,?RUNTIME:clear_all_tp(Pid)}|Replies]);
- Error ->
- do_ctp_all_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_ctp_all_2([],_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_ctp_all_2(Faulty,_,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function suspending all runtime components mentioned in Nodes.
-%% Returns {ok,Reply} or {error,Reason}.
-do_suspend(Nodes,Reason,LD) ->
- do_suspend_2(started_trace_nodes(Nodes,LD),Reason,LD,[]).
-
-do_suspend_2([Node|Tail],Reason,LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- Answer=?RUNTIME:call_suspend(Pid,Reason),
- do_suspend_2(Tail,Reason,LD,[{Node,Answer}|Replies]);
- Error ->
- do_suspend_2(Tail,Reason,LD,[{Node,Error}|Replies])
- end;
-do_suspend_2([],_Reason,_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_suspend_2(Faulty,_,_,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function cancelling a suspension at the runtime components mentioned in Nodes.
-%% Returns {ok,Reply} or {error,Reason}.
-do_cancel_suspension(Nodes,LD) ->
- do_cancel_suspension_2(started_trace_nodes(Nodes,LD),LD,[]).
-
-do_cancel_suspension_2([Node|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- Answer=?RUNTIME:cancel_suspension(Pid),
- do_cancel_suspension_2(Tail,LD,[{Node,Answer}|Replies]);
- Error ->
- do_cancel_suspension_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_cancel_suspension_2([],_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_cancel_suspension_2(Faulty,_,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function which stops tracing on all nodes in Nodes. The function is performed
-%% in parallel over the nodes to get a more precise stop-time.
-%% Return {ok,Reply} or {error,Reason}.
-do_stop_tracing(all,LD) ->
- do_stop_tracing(started_trace_nodes(all,LD),LD);
-do_stop_tracing(Nodes,LD) when is_list(Nodes) ->
- RTpids=lists:map(fun(N)->case get_node_rec(N,LD) of
- #node{pid=Pid} ->
- {Pid,N};
- Error ->
- {Error,N}
- end
- end,
- Nodes),
- {ok,?RUNTIME:stop_tracing_parallel(RTpids)};
-do_stop_tracing(Faulty,_) ->
- {error,{badarg,Faulty}}.
-%% -----------------------------------------------------------------------------
-
-%% Function fetching the current status of the runtime components in Nodes.
-%% Returns {ok,Reply} or {error,Reason}.
-do_get_status(Nodes,LD) ->
- do_get_status_2(started_trace_nodes(Nodes,LD),LD,[]).
-
-do_get_status_2([Node|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- do_get_status_2(Tail,LD,[{Node,?RUNTIME:get_status(Pid)}|Replies]);
- Error ->
- do_get_status_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_get_status_2([],_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_get_status_2(Faulty,_,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function retrieving the tracerdata for the nodes in Nodes.
-%% Returns {ok,Reply} or {error,Reason}.
-do_get_tracerdata(Nodes,LD) ->
- do_get_tracerdata_2(started_trace_nodes(Nodes,LD),LD,[]).
-
-do_get_tracerdata_2([Node|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- do_get_tracerdata_2(Tail,LD,[{Node,?RUNTIME:get_tracerdata(Pid)}|Replies]);
- Error ->
- do_get_tracerdata_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_get_tracerdata_2([],_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_get_tracerdata_2(Faulty,_,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function that lists all logfiles associated with a certain tracerdata
-%% or the current tracerdata should no tracerdata be mentioned.
-%% Returns {ok,Replies} or {error,Reason}.
-do_list_logs(all,LD) -> % When doing all known nodes.
- do_list_logs_2(started_trace_nodes(all,LD),LD,[]);
-do_list_logs(TracerDataOrNodesList,LD) ->
- case inviso_rt_lib:is_tracerdata(TracerDataOrNodesList) of
- true -> % It is tracerdata for this node.
- do_list_logs_2([{node(),TracerDataOrNodesList}],LD,[]);
- false ->
- do_list_logs_2(TracerDataOrNodesList,LD,[])
- end.
-
-do_list_logs_2([{Node,TD}|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- case check_modify_tracerdata(TD,LD) of
- {ok,TracerData} ->
- Answer=?RUNTIME:list_logs(Pid,TracerData),
- do_list_logs_2(Tail,LD,[{Node,Answer}|Replies]);
- Error ->
- do_list_logs_2(Tail,LD,[{Node,Error}|Replies])
- end;
- Error ->
- do_list_logs_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_list_logs_2([Node|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- Answer=?RUNTIME:list_logs(Pid),
- do_list_logs_2(Tail,LD,[{Node,Answer}|Replies]);
- Error ->
- do_list_logs_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_list_logs_2([],_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_list_logs_2(Other,_LD,_Replies) ->
- {error,{badarg,Other}}.
-%% -----------------------------------------------------------------------------
-
-%% Function fetching logfiles using distributed erlang. This function does not
-%% return anything significant. This since the reply to the client is always
-%% sent by the CollectPid unless there is badarg fault detected before the
-%% CollectPid is spawned. Note that this function sends a list of fetchers from
-%% which the CollectPid shall expect replies.
-%% We try to catch some bad arguments like Destination and Prefix not being
-%% strings. However the fact that they are lists does not guarantee they are
-%% proper strings.
-do_fetch_log(ToNode,all,Dest,Prefix,From,LD) ->
- do_fetch_log(ToNode,started_trace_nodes(all,LD),Dest,Prefix,From,LD);
-do_fetch_log(ToNode,Specs,Dest,Prefix,From,LD) when is_list(Dest),is_list(Prefix) ->
- CollectPid=spawn_link(ToNode,?MODULE,log_rec_init,[self(),Dest,Prefix,From]),
- do_fetch_log_2(Specs,LD,CollectPid,[],[]);
-do_fetch_log(_ToNode,_Specs,Dest,Prefix,From,_LD) ->
- gen_server:reply(From,{error,{badarg,[Dest,Prefix]}}).
-
-do_fetch_log_2([{Node,Spec}|Rest],LD,CollectPid,Fetchers,Replies) ->
- if
- Node==node() -> % This is plain stupid!
- do_fetch_log_2(Rest,LD,CollectPid,Fetchers,[{Node,{error,own_node}}|Replies]);
- true ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- {NewFetchers,NewReplies}=
- do_fetch_log_3(Fetchers,
- Replies,
- Node,
- ?RUNTIME:fetch_log(Pid,CollectPid,Spec)),
- do_fetch_log_2(Rest,LD,CollectPid,NewFetchers,NewReplies);
- Error -> % Most likely the node does not exist.
- do_fetch_log_2(Rest,LD,CollectPid,Fetchers,[{Node,Error}|Replies])
- end
- end;
-do_fetch_log_2([Node|Rest],LD,CollectPid,Fetchers,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- {NewFetchers,NewReplies}=
- do_fetch_log_3(Fetchers,Replies,Node,?RUNTIME:fetch_log(Pid,CollectPid)),
- do_fetch_log_2(Rest,LD,CollectPid,NewFetchers,NewReplies);
- Error -> % Most likely the node does not exist.
- do_fetch_log_2(Rest,LD,CollectPid,Fetchers,[{Node,Error}|Replies])
- end;
-do_fetch_log_2([],_,CollectPid,Fetchers,Replies) ->
- CollectPid ! {?MODULE,self(),Fetchers,Replies};
-do_fetch_log_2(FaultySpec,_,CollectPid,_Fetchers,_Replies) ->
- CollectPid ! {?MODULE,self(),[],{error,{badarg,FaultySpec}}}.
-
-do_fetch_log_3(Fetchers,Replies,Node,{ok,Fetcher}) ->
- {[{Node,Fetcher}|Fetchers],Replies};
-do_fetch_log_3(Fetchers,Replies,Node,{complete,no_log}) ->
- {Fetchers,[{Node,{complete,no_log}}|Replies]};
-do_fetch_log_3(Fetchers,Replies,Node,{error,Reason}) ->
- {Fetchers,[{Node,{error,Reason}}|Replies]}.
-%% -----------------------------------------------------------------------------
-
-%% Function removing files from the runtime components. We can either ask for
-%% all files associated with the current tracerdata to be removed, or provide
-%% tracerdata or a list of files to be removed.
-%% Returns the client reply.
-do_delete_log(all,LD) ->
- do_delete_log_2(started_trace_nodes(all,LD),LD,[]);
-do_delete_log(NodeSpecs,LD) ->
- case inviso_rt_lib:is_tracerdata(NodeSpecs) of
- true -> % It is tracerdata for all nodes.
- do_delete_log_2(lists:map(fun(N)->{N,NodeSpecs} end,
- started_trace_nodes(all,LD)),
- LD,[]);
- false ->
- if
- is_list(NodeSpecs),is_list(hd(NodeSpecs)) -> % A list of files.
- do_delete_log_2(lists:map(fun(N)->{N,NodeSpecs} end,
- started_trace_nodes(all,LD)),
- LD,[]);
- true -> % Then use it as is.
- do_delete_log_2(NodeSpecs,LD,[])
- end
- end.
-
-do_delete_log_2([{Node,Spec}|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- do_delete_log_2(Tail,LD,[{Node,?RUNTIME:delete_log(Pid,Spec)}|Replies]);
- Error ->
- do_delete_log_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_delete_log_2([Node|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- do_delete_log_2(Tail,LD,[{Node,?RUNTIME:delete_log(Pid)}|Replies]);
- Error ->
- do_delete_log_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_delete_log_2([],_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_delete_log_2(Faulty,_,_) ->
- {error,{badarg,Faulty}}.
-%% -----------------------------------------------------------------------------
-
-%% Function removing files from runtime components.
-%% Returns {ok,Replies} or {error,Reason}.
-do_clear(Nodes,LD,Options) ->
- do_clear_2(started_trace_nodes(Nodes,LD),LD,Options,[]).
-
-do_clear_2([Node|Tail],LD,Options,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- do_clear_2(Tail,LD,Options,[{Node,?RUNTIME:clear(Pid,Options)}|Replies]);
- Error ->
- do_clear_2(Tail,LD,Options,[{Node,Error}|Replies])
- end;
-do_clear_2([],_LD,_Options,Replies) ->
- {ok,lists:reverse(Replies)};
-do_clear_2(FaultyNodes,_LD,_Options,_Replies) ->
- {error,{badarg,FaultyNodes}}.
-%% -----------------------------------------------------------------------------
-
-%% Function doing a flush trace-port.
-%% Returns {ok,Replies} or {error,Reason}.
-do_flush(Nodes,LD) ->
- do_flush_2(started_trace_nodes(Nodes,LD),LD,[]).
-
-do_flush_2([Node|Rest],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- do_flush_2(Rest,LD,[{Node,?RUNTIME:flush(Pid)}|Replies]);
- Error ->
- do_flush_2(Rest,LD,[{Node,Error}|Replies])
- end;
-do_flush_2([],_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_flush_2(FaultyNodes,_LD,_Replies) ->
- {error,{badarg,FaultyNodes}}.
-%% -----------------------------------------------------------------------------
-
-%% Function stopping runtime components. We can only stop runtime components
-%% belonging to this control component.
-%% Returns {NewLoopdata,Reply}.
-do_stop_nodes(Nodes,LD) ->
- do_stop_nodes_2(started_trace_nodes(Nodes,LD),LD,[]).
-
-do_stop_nodes_2([Node|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{ref=MRef} ->
- erlang:demonitor(MRef),
- case ?RUNTIME:stop(Node) of
- ok ->
- NewLD=delete_node_rec(Node,LD),
- send_to_subscribers({disconnected,Node,void},LD),
- do_stop_nodes_2(Tail,NewLD,[{Node,ok}|Replies]);
- Error ->
- do_stop_nodes_2(Tail,LD,[{Node,Error}|Replies])
- end;
- Error ->
- do_stop_nodes_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_stop_nodes_2([],LD,Replies) ->
- {LD,{ok,Replies}};
-do_stop_nodes_2(Faulty,LD,_) ->
- {LD,{error,{badarg,Faulty}}}.
-%% -----------------------------------------------------------------------------
-
-%% Function being called when a runtime component sends a connect message to
-%% the controlcomponent. The control component then confirms that is has indeed
-%% taken on that runtime component.
-%% Returns a new loopdata structure.
-do_confirm_connection(Pid,Tag,LD) ->
- case ?RUNTIME:confirm_connection(Pid,Tag) of
- {node_info,Node,_Pid,VSN,State,Status,_Tag} ->
- MRef=erlang:monitor(process,Pid), % We must monitor it from now on.
- Rec=#node{node=Node,vsn=VSN,tag=Tag,ref=MRef,pid=Pid},
- send_to_subscribers({connected,Node,{Tag,{State,Status}}},LD),
- set_node_rec(Rec,LD); % Makes new loopdata.
- _Error -> % Strange, it wanted us as control!?
- LD
- end.
-%% ------------------------------------------------------------------------------
-
-%% Function handling an incomming DOWN message. This can be one of our runtime
-%% components terminating, or a process subscribing to events. Send a trace-event
-%% to subscribers if it was a runtime terminating and remove it from
-%% our list of runtime components.
-%% Note that if a subscriber has subscribed multiple times to events, we will get
-%% multiple DOWN messages too, since we have monitored that process multiple
-%% times. It is therefore sufficient to remove just one subscription entry here
-%% each time (remove on the monitor reference!).
-%% Returns a new LoopData structure.
-do_down_msg(Ref,Info,LD) ->
- case find(fun ref/2,LD#state.nodes,Ref) of
- {value,Node} -> % Yes it was one of our nodes.
- send_to_subscribers({disconnected,Node,Info},LD),
- delete_node_rec(Node,LD);
- no_match -> % Not one of our nodes.
- case lists:keysearch(Ref,2,LD#state.subscribers) of
- {value,{_Pid,_}} -> % It was a subscriber terminating.
- LD#state{subscribers=lists:keydelete(Ref,2,LD#state.subscribers)};
- false -> % Not one of our subscribers either.
- LD % Do nothing then.
- end
- end.
-%% ------------------------------------------------------------------------------
-
-
-
-%% ==============================================================================
-%% Help functions.
-%% ==============================================================================
-
-
-%% Help function which inspects options to start and add_node. Returns a new
-%% list of options in {ok,Options} or {error,Reason}.
-check_options(Options, Context) ->
- check_options_2(Options, Context, []).
-
-check_options_2([],_Context,Result) ->
- {ok,Result};
-check_options_2([{subscribe,Pid}|OptionsTail],start,Result) when is_pid(Pid) ->
- check_options_2(OptionsTail,start,[{subscribe,Pid}|Result]);
-check_options_2([{unsubscribe,Pid}|OptionsTail],start,Result) when is_pid(Pid) ->
- check_options_2(OptionsTail,start,[{unsubscribe,Pid}|Result]);
-check_options_2([{dependency,How}|OptionsTail],Context,Result) ->
- check_options_2(OptionsTail,Context,[{dependency,How}|Result]);
-check_options_2([{overload,How}|OptionsTail],Context,Result) ->
- check_options_2(OptionsTail,Context,[{overload,How}|Result]);
-check_options_2([overload|OptionsTail],Context,Result) ->
- check_options_2(OptionsTail,Context,[overload|Result]);
-check_options_2([UnKnown|_],_Context,_Result) ->
- {error,{unknown_option,UnKnown}};
-check_options_2(UnKnown,_Context,_Result) ->
- {error,{unknown_option,UnKnown}}.
-%% ------------------------------------------------------------------------------
-
-%% Help function initiating the #state structure, i.e the loopdata. Since there
-%% are some initial values from the record defaults when creating a new #state,
-%% those must be compared with possibly specified Options. Specified Options shall
-%% of course override defaults.
-%% Note that it is not the control component's responsibility to understand all
-%% options later given to a runtime component. It mearly stores them in rt_options
-%% so they can be passed to the runtime component at start-up.
-%% Returns a loopdata structure.
-initiate_state(Options) ->
- ResultingOptions=merge_options((#state{})#state.rt_options,Options),
- LD1=initiate_state_2(ResultingOptions,#state{rt_options=[]}),
- case node() of % Finally set the distribution flag.
- nonode@nohost ->
- LD1#state{distributed=false};
- _ ->
- LD1#state{distributed=true}
- end.
-
-initiate_state_2([{subscribe,Proc}|Tail],LD) when is_pid(Proc);is_atom(Proc)->
- MRef=erlang:monitor(process,Proc),
- initiate_state_2(Tail,LD#state{subscribers=[{Proc,MRef}|LD#state.subscribers]});
-initiate_state_2([Opt|Tail],LD) when is_tuple(Opt),size(Opt)>=1 ->
- initiate_state_2(Tail,initiate_state_3(element(1,Opt),Opt,LD));
-initiate_state_2([Opt|Tail],LD) when is_atom(Opt) ->
- initiate_state_2(Tail,initiate_state_3(Opt,Opt,LD));
-initiate_state_2([_|Tail],LD) ->
- initiate_state_2(Tail,LD);
-initiate_state_2([],LD) ->
- LD.
-
-initiate_state_3(OptName,Opt,LD) ->
- case initiate_state_is_rt_option(OptName) of
- true -> % Yes, it shall be part of the rt_options.
- LD#state{rt_options=[Opt|LD#state.rt_options]};
- false -> % Ignore the option then.
- LD
- end.
-
-%% This is the only place you have to change should there be more rt_options
-%% introduced.
-initiate_state_is_rt_option(overload) -> true;
-initiate_state_is_rt_option(dependency) -> true;
-initiate_state_is_rt_option(_) -> false.
-%% ------------------------------------------------------------------------------
-
-%% Help function which takes a list of default options and a list of overriding
-%% options. The function builds a return value consisting of all default options
-%% unless they are overridden by a overriding option.
-%% An option can be either {Param,.....} or Param. I.e either a tuple with zero
-%% or more values associated with the Parameter, or just an atom.
-merge_options([], Options) ->
- Options;
-merge_options([T|DefaultTail],Options) when is_tuple(T),size(T)>=1 ->
- merge_options(DefaultTail,merge_options_2(element(1,T),T,Options));
-merge_options([Param|DefaultTail],Options) when is_atom(Param) ->
- merge_options(DefaultTail,merge_options_2(Param,Param,Options));
-merge_options([_|DefaultTail],Options) -> % Strange, bad default option!
- merge_options(DefaultTail,Options).
-
-merge_options_2(Param,Opt,Options) ->
- case merge_options_find(Param,Options) of
- true ->
- Options;
- false ->
- [Opt|Options]
- end.
-
-merge_options_find(Param,[T|_]) when is_tuple(T),element(1,T)==Param ->
- true;
-merge_options_find(Param,[Param|_]) ->
- true;
-merge_options_find(Param,[_|Rest]) ->
- merge_options_find(Param,Rest);
-merge_options_find(_,[]) ->
- false.
-%% ------------------------------------------------------------------------------
-
-%% Help function which transforms certain parts of a tracer data. Those are
-%% parts which must be transformed at the controlnode like node to pid mappings.
-%% It also checks the formatting of the tracerdata since runtime components
-%% does not accept too badly formatted tracerdata.
-%% Returns {ok,NewTraceData} or {error,Reason}.
-check_modify_tracerdata(TracerData,LoopData) when is_list(TracerData) ->
- case lists:keysearch(trace,1,TracerData) of
- {value,{_,TraceTD}} -> % Examine the trace part.
- case check_modify_tracerdata(TraceTD,LoopData) of
- {ok,NewTraceTD} ->
- {ok,lists:keyreplace(trace,1,TracerData,{trace,NewTraceTD})};
- {error,Reason} -> % The trace part was faulty.
- {error,Reason} % Ruins everything :-)
- end;
- false -> % Unusual, but no trace part.
- {ok,TracerData} % No modifications necessary.
- end;
-check_modify_tracerdata(collector,_LoopData) ->
- {ok, collector};
-check_modify_tracerdata({relayer,Collector},_LoopData) when is_pid(Collector) ->
- {ok,{relayer,Collector}};
-check_modify_tracerdata({relayer,Collector},LoopData) when is_atom(Collector) ->
- case get_node_rec(Collector,LoopData) of
- Rec when is_record(Rec,node) -> % Collector is a known node.
- {ok,{relayer,Rec#node.pid}};
- {error,not_an_added_node} ->
- {error,{not_an_added_node,Collector}}
- end;
-check_modify_tracerdata({Type,Data},_LoopData) when Type==ip;Type==file ->
- {ok,{Type,Data}};
-check_modify_tracerdata({Handler,Data},_LoopData) when is_function(Handler) ->
- {ok,{Handler,Data}};
-check_modify_tracerdata(Data,_LoopData) ->
- {error,{bad_tracerdata,Data}}.
-%% -----------------------------------------------------------------------------
-
-%% Help function sending an event to all processes subscribing to events.
-%% Note that the function manipulates the from Node indicator incase we are not
-%% running in a distributed system.
-%% Returns nothing significant.
-send_to_subscribers(Msg={Event,_Node,Data},LD) ->
- AdaptedMsg=
- case LD#state.distributed of
- true ->
- Msg;
- false ->
- {Event,local_runtime,Data}
- end,
- TraceEvent={inviso_event,self(),erlang:localtime(),AdaptedMsg},
- send_to_subscribers_2(LD#state.subscribers,TraceEvent).
-
-send_to_subscribers_2([],_) ->
- ok;
-send_to_subscribers_2([{Subscriber,_}|Tail],TraceEvent) ->
- Subscriber ! TraceEvent,
- send_to_subscribers_2(Tail,TraceEvent).
-%% -----------------------------------------------------------------------------
-
-%% Help function converting the Nodes parameter to known nodes. Actually today
-%% it only converts the all atom to all known nodes.
-%% Returns a list of nodes.
-started_trace_nodes(all,LoopData) ->
- lists:map(fun(N)->N#node.node end,LoopData#state.nodes);
-started_trace_nodes(Nodes,_) ->
- Nodes.
-%% ------------------------------------------------------------------------------
-
-%% Help function searching through a list of elements looking for an element
-%% containing Key. How the element shall be interpreted is done by the Fun.
-%% Returns {value,Element} or 'no_match'.
-%% Fun=fun(Element,Key)={return,Value} | false
-find(_,[],_Key) ->
- no_match;
-find(Fun,[H|T],Key) ->
- case Fun(H,Key) of
- {return,Value}->
- {value,Value};
- _ ->
- find(Fun,T,Key)
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Functions handling the nodes datastructure part of the #state.
-%% #state.nodes is a list of #node.
-%% -----------------------------------------------------------------------------
-
-%% Function used to build find fun, looking for a certain #node with its monitoring
-%% reference set to Ref. Useful when finding out if a DOWN message comes from one
-%% of our runtime components.
-ref(#node{ref=Ref,node=Node},Ref) ->
- {return,Node};
-ref(_,_) ->
- false.
-%% -----------------------------------------------------------------------------
-
-%% use in find/3
-%% Function used to build find fun, finding out if we have a node with the node
-%% name Node.
-is_started(#node{node=Node},Node) ->
- {return,true};
-is_started(_,_) ->
- false.
-%% -----------------------------------------------------------------------------
-
-%% Help function replacing or adding an entry for a node. Works on either a list
-%% of #node or a loopdata structure. Returns a new list of #node or a loopdata struct.
-set_node_rec(Rec,LD=#state{nodes=NodeList}) ->
- LD#state{nodes=set_node_rec_2(Rec,NodeList)}.
-
-set_node_rec_2(Rec,[]) ->
- [Rec];
-set_node_rec_2(Rec,[NodeRec|Tail]) when NodeRec#node.node==Rec#node.node ->
- [Rec|Tail];
-set_node_rec_2(Rec,[NodeRec|Tail]) ->
- [NodeRec|set_node_rec_2(Rec,Tail)].
-%% ------------------------------------------------------------------------------
-
-%% Help function finding a node record for Node in a list of #node or in loopdata.
-%% Returns the #node in question or {error,not_an_added_node}.
-get_node_rec(Node,NodeList) when is_list(NodeList) ->
- get_node_rec_2(Node,NodeList);
-get_node_rec(Node,#state{nodes=NodeList}) ->
- get_node_rec_2(Node,NodeList).
-
-get_node_rec_2(_Node,[]) ->
- {error,not_an_added_node};
-get_node_rec_2(Node,[NodeRec|_]) when NodeRec#node.node==Node ->
- NodeRec;
-get_node_rec_2(Node,[_NodeRec|Tail]) ->
- get_node_rec_2(Node,Tail).
-%% ------------------------------------------------------------------------------
-
-%% Help function removing a #node from either a list of #node or from a loopdata
-%% structure. Returns a new list of #node or a new loopdata structure.
-delete_node_rec(Node,LD=#state{nodes=NodeList}) ->
- LD#state{nodes=delete_node_rec_2(Node,NodeList)};
-delete_node_rec(Node,NodeList) when is_list(NodeList) ->
- delete_node_rec_2(Node,NodeList).
-
-delete_node_rec_2(_,[]) ->
- [];
-delete_node_rec_2(#node{node=Node},[#node{node=Node}|Tail]) ->
- Tail;
-delete_node_rec_2(Node,[#node{node=Node}|Tail]) ->
- Tail;
-delete_node_rec_2(Node,[NRec|Tail]) ->
- [NRec|delete_node_rec_2(Node,Tail)].
-%% ------------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Implementation of the help process receiving all logs from the runtime
-%% components. This process is referred to as the CollectPid.
-%% It is responsible for sending the reply back to the control component
-%% client. If a runtime component becomes suspended, the CollectPid is
-%% alerted by the DOWN message.
-%% Note that it may take some time before this process responds back to the client.
-%% Therefore the client must wait for 'infinity'. The job of transferring the
-%% files can be costly. Therefore it is a good idea to stop if no one is really
-%% interested in the result. This collector process monitors the From client in
-%% order to learn if the job can be cancelled. That will also be a possibility
-%% for a client to willfully cancel a fetch job.
-%% =============================================================================
-
-%% Intitial function on which the control component spawns. Note that the start
-%% must be done in two steps since the runtime components must be informed of
-%% the CollectPid. But the CollectPid must also know from which runtime components
-%% it can expect files from.
-%% InitialReplies: contains {Node,Result} for nodes from where there will be no
-%% files, but which must be part of the final reply.
-log_rec_init(Parent,Dest,Prefix,From={ClientPid,_}) ->
- receive
- {?MODULE,Parent,Fetchers,InitialReplies} ->
- RTs=lists:map(fun({N,F})->
- {N,erlang:monitor(process,F),void,void,void}
- end,
- Fetchers),
- CMRef=erlang:monitor(process,ClientPid), % Monitor the client.
- case log_rec_loop(Dest,Prefix,RTs,InitialReplies,CMRef) of
- Reply when is_list(Reply) -> % It is an ok value.
- gen_server:reply(From,{ok,Reply});
- {error,Reason} ->
- gen_server:reply(From,{error,Reason});
- false -> % The client terminated, no response.
- true % Simply terminate, fetchers will notice.
- end
- end.
-
-log_rec_loop(_Dest,_Prefix,[],Replies,_CMRef) -> % All nodes done!
- Replies; % This is the final reply.
-log_rec_loop(Dest,Prefix,RTs,Replies,CMRef) ->
- receive
- {Node,open,{FType,RemoteFName}} ->
- case lists:keysearch(Node,1,RTs) of
- {value,{_,MRef,_,_,_}} ->
- {NewRTs,NewReplies}=
- log_rec_open(Dest,Prefix,Node,MRef,FType,RemoteFName,RTs,Replies),
- log_rec_loop(Dest,Prefix,NewRTs,NewReplies,CMRef);
- false ->
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end;
- {Node,open_failure,{FType,RemoteFName}} ->
- case lists:keysearch(Node,1,RTs) of
- {value,{_,MRef,_,_,_}} ->
- {NewRTs,NewReplies}=
- log_rec_open_failure(Node,MRef,FType,RemoteFName,RTs,Replies),
- log_rec_loop(Dest,Prefix,NewRTs,NewReplies,CMRef);
- false ->
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end;
- {Node,payload,Bin,FPid} -> % A chunk of data from a fetcher.
- case lists:keysearch(Node,1,RTs) of
- {value,{_,_,_,_,void}} -> % Node has no file open here.
- FPid ! {self(),cancel_transmission}, % No use sending more to me.
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef); % Simply ignore payload.
- {value,{_Node,MRef,FType,FName,FD}} ->
- case log_rec_payload(Node,MRef,FType,FName,FD,Bin,RTs,Replies) of
- {ok,{NewRTs,NewReplies}} ->
- FPid ! {self(),chunk_ack}, % For flow control.
- log_rec_loop(Dest,Prefix,NewRTs,NewReplies,CMRef);
- {error,{NewRTs,NewReplies}} ->
- FPid ! {self(),cancel_transmission}, % No use sending more to me.
- log_rec_loop(Dest,Prefix,NewRTs,NewReplies,CMRef)
- end;
- false -> % Node is not part of transfere.
- FPid ! {self(),cancel_transmission}, % No use sending more to me.
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end;
- {Node,end_of_file} ->
- case lists:keysearch(Node,1,RTs) of
- {value,{_,_,_,_,void}} -> % Node has no file open here.
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef);
- {value,{_,MRef,FType,FName,FD}} ->
- {NewRTs,NewReplies}=
- log_rec_eof(Node,MRef,FType,FName,FD,RTs,Replies),
- log_rec_loop(Dest,Prefix,NewRTs,NewReplies,CMRef);
- false ->
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end;
- {Node,end_of_transmission} -> % This runtime is done!
- case lists:keysearch(Node,1,RTs) of
- {value,{_Node,MRef,_,_,_}} ->
- erlang:demonitor(MRef),
- log_rec_loop(Dest,Prefix,
- lists:keydelete(Node,1,RTs),
- log_rec_mkreply(Node,complete,Replies),
- CMRef);
- false -> % Strange, not one of our nodes.
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end;
- {Node,incomplete} -> % This runtime is done (with errors).
- case lists:keysearch(Node,1,RTs) of
- {value,{_,MRef,FType,FName,FD}} ->
- erlang:demonitor(MRef),
- {NewRTs,NewReplies}=
- log_rec_incomplete(Node,FType,FName,FD,RTs,Replies),
- log_rec_loop(Dest,Prefix,NewRTs,NewReplies,CMRef);
- false -> % Not our, or not anylonger.
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end;
- {Node,{error,Reason}} -> % Remote file read_error.
- case lists:keysearch(Node,1,RTs) of
- {value,{_,MRef,FType,FName,FD}} ->
- {NewRTs,NewReplies}=
- log_rec_error(Node,MRef,FType,FName,FD,RTs,Reason,Replies),
- log_rec_loop(Dest,Prefix,NewRTs,NewReplies,CMRef);
- false ->
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end;
- {'DOWN',CMRef,process,_,_} -> % The client got tired waiting.
- log_rec_cancel(Dest,RTs,Replies), % Close and remove all files.
- false; % Indicate no response message.
- {'DOWN',Ref,process,_P,_Info} ->
- case lists:keysearch(Ref,2,RTs) of
- {value,{Node,_,FType,FName,FD}} ->
- {NewRTs,NewReplies}=
- log_rec_incomplete(Node,FType,FName,FD,RTs,Replies),
- log_rec_loop(Dest,Prefix,NewRTs,NewReplies,CMRef);
- false -> % Not our, or not anylonger.
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end;
- _ ->
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end.
-
-%% Help function opening a new target file on the receiver. It returns
-%% {NewRTs,NewReplies}.
-%% Note that we must protect us against that some of the strings are not proper
-%% strings, but contains garbage.
-log_rec_open(Dest,Prefix,Node,MRef,FType,RemoteFName,RTs,Replies) ->
- case catch log_rec_open_2(Dest,Prefix,Node,MRef,FType,RemoteFName,RTs,Replies) of
- {'EXIT',Reason} ->
- NewRTs=lists:keyreplace(Node,1,RTs,{Node,MRef,void,void,void}),
- NewReplies=
- log_rec_addreply(Node,
- FType,
- {error,{file_open,{Reason,[Dest,Prefix,RemoteFName]}}},Replies),
- {NewRTs,NewReplies};
- Result ->
- Result
- end.
-
-log_rec_open_2(Dest,Prefix,Node,MRef,FType,RemoteFName,RTs,Replies) ->
- FName=Prefix++RemoteFName, % Our file name.
- case file:open(filename:join([Dest,FName]),[write]) of
- {ok,FD} ->
- NewRTs=lists:keyreplace(Node,1,RTs,{Node,MRef,FType,FName,FD}),
- {NewRTs,Replies};
- {error,Reason} ->
- NewRTs=lists:keyreplace(Node,1,RTs,{Node,MRef,void,void,void}),
- NewReplies=
- log_rec_addreply(Node,FType,{error,{file_open,{Reason,FName}}},Replies),
- {NewRTs,NewReplies}
- end.
-
-%% Help function adding a file that was unsuccessfully opened as failed.
-log_rec_open_failure(Node,MRef,FType,RemoteFName,RTs,Replies) ->
- NewRTs=lists:keyreplace(Node,1,RTs,{Node,MRef,void,void,void}),
- NewReplies=
- log_rec_addreply(Node,
- FType,
- {error,{remote_open,RemoteFName}},Replies),
- {NewRTs,NewReplies}.
-
-%% Help function whih writes the Bin to the FD file. If writing was unsuccessful,
-%% close the file and modify RTs and add a reply to Replies. Note that we can not
-%% stop the runtime from sending us more data belonging to this file. But we will
-%% simply just inore it from now on.
-%% Returns {SuccessCode,{NewRTs,NewReplies}}.
-log_rec_payload(Node,MRef,FType,FName,FD,Bin,RTs,Replies) ->
- case file:write(FD,Bin) of
- ok ->
- {ok,{RTs,Replies}};
- {error,Reason} ->
- file:close(FD),
- NewRTs=lists:keyreplace(Node,1,RTs,{Node,MRef,void,void,void}),
- NewReplies=
- log_rec_addreply(Node,FType,{error,{file_write,{Reason,FName}}},Replies),
- {error,{NewRTs,NewReplies}}
- end.
-
-%% Help function whih shall be used when a file has been successfully transfered.
-%% This function closes the output file and updates RTs and the Replies.
-%% Returns {NewRTs,NewReplies}.
-log_rec_eof(Node,MRef,FType,FName,FD,RTs,Replies) ->
- file:close(FD),
- NewRTs=lists:keyreplace(Node,1,RTs,{Node,MRef,void,void,void}),
- {NewRTs,log_rec_addreply(Node,FType,{ok,FName},Replies)}.
-
-%% Help function which, if there is an open file, indicates it as truncated in the
-%% replies. And finalize the reply for Node assuming that the node is in total incomplete
-%% Returns {NewRTs,NewReplies}.
-log_rec_incomplete(Node,_FType,_FName,void,RTs,Replies) ->
- NewRTs=lists:keydelete(Node,1,RTs), % The node is done.
- {NewRTs,log_rec_mkreply(Node,incomplete,Replies)};
-log_rec_incomplete(Node,FType,FName,FD,RTs,Replies) ->
- file:close(FD), % Not going to write anymore in this file.
- NewRTs=lists:keydelete(Node,1,RTs), % The node is done.
- NewReplies=log_rec_addreply(Node,FType,{error,{truncated,FName}},Replies),
- {NewRTs,log_rec_mkreply(Node,incomplete,NewReplies)}.
-
-%% Help function handling the case when runtime component experiences an error
-%% transferering the file. That means that there will be no more chunks of this
-%% file. Hence it works a bit like EOF.
-%% Returns {NewRTs,NewReplies}.
-log_rec_error(Node,MRef,FType,FName,FD,RTs,Reason,Replies) ->
- file:close(FD),
- NewRTs=lists:keyreplace(Node,1,RTs,{Node,MRef,void,void,void}),
- {NewRTs,log_rec_addreply(Node,FType,{error,{truncated,{Reason,FName}}},Replies)}.
-%% -----------------------------------------------------------------------------
-
-%% Help function adding a reply to the list of replies.
-%% Replies is a list {Node,FType,Reply} for each file handled, sucessfully or not.
-%% The list may also contain finalized nodes, which will be on the format:
-%% {Node,{Conclusion,[{trace_log,TraceLogReplies},{ti_log,TiLogReplies}]}}.
-log_rec_addreply(Node,FType,Reply,Replies) ->
- [{Node,FType,Reply}|Replies].
-
-%% Help function which converts the {Node,FType,Reply} tuples in Replies to
-%% a finalized reply.
-log_rec_mkreply(Node,Conclusion,Replies) ->
- {RemainingReplies,TiReplies,TraceReplies}=
- log_rec_mkreply_node_ftype(Node,Replies,[],[],[]),
- [{Node,{Conclusion,[{trace_log,TraceReplies},{ti_log,TiReplies}]}}|
- RemainingReplies].
-
-%% Help function taking out the ti_log and trace_log file-types replies for
-%% Node. Returns {RemainingReplies,Ti,Trace}.
-log_rec_mkreply_node_ftype(Node,[{Node,ti_log,Result}|Rest],Replies,Ti,Trace) ->
- log_rec_mkreply_node_ftype(Node,Rest,Replies,[Result|Ti],Trace);
-log_rec_mkreply_node_ftype(Node,[{Node,trace_log,Result}|Rest],Replies,Ti,Trace) ->
- log_rec_mkreply_node_ftype(Node,Rest,Replies,Ti,[Result|Trace]);
-log_rec_mkreply_node_ftype(Node,[Reply|Rest],Replies,Ti,Trace) ->
- log_rec_mkreply_node_ftype(Node,Rest,[Reply|Replies],Ti,Trace);
-log_rec_mkreply_node_ftype(_,[],Replies,Ti,Trace) ->
- {Replies,Ti,Trace}.
-%% -----------------------------------------------------------------------------
-
-%% If the fetching job shall be cancelled, we must close all open files and
-%% remove them including all already closed files. Returns nothing significant.
-log_rec_cancel(Dest,RTs,Replies) ->
- log_rec_cancel_open(Dest,RTs), % First close and remove all open files.
- log_rec_cancel_finished(Dest,Replies). % Remove all already closed files.
-
-log_rec_cancel_open(Dest,[{_Node,_MRef,_FType,_FName,void}|Rest]) ->
- log_rec_cancel_open(Dest,Rest); % There is no open file to close.
-log_rec_cancel_open(Dest,[{_Node,_MRef,_FType,FName,FD}|Rest]) ->
- file:close(FD),
- catch file:delete(filename:join(Dest,FName)), % Will just try to do my best.
- log_rec_cancel_open(Dest,Rest);
-log_rec_cancel_open(_Dest,[]) ->
- true.
-
-log_rec_cancel_finished(Dest,[{_N,_FT,Reply}|Rest]) ->
- [FName]=log_rec_cancel_finished_get_fname([Reply]),
- catch file:delete(filename:join(Dest,FName)),
- log_rec_cancel_finished(Dest,Rest);
-log_rec_cancel_finished(Dest,[{_N,{_Conclusion,[{_,Replies1},{_,Replies2}]}}|Rest]) ->
- FNames1=log_rec_cancel_finished_get_fname(Replies1),
- lists:foreach(fun(FName)->
- catch file:delete(filename:join(Dest,FName))
- end,
- FNames1),
- FNames2=log_rec_cancel_finished_get_fname(Replies2),
- lists:foreach(fun(FName)->
- catch file:delete(filename:join(Dest,FName))
- end,
- FNames2),
- log_rec_cancel_finished(Dest,Rest);
-log_rec_cancel_finished(_Dest,[]) ->
- true.
-
-%% Help function going through all possible reply values for a file. So
-%% consequently there must be a clause here for every possible log_rec_addreply
-%% call above. Returns a list of filenames that shall be removed in order to
-%% restore the disk since the fetch job is cancelled.
-log_rec_cancel_finished_get_fname([{error,{file_open,{_,FName}}}|Rest]) ->
- [FName|log_rec_cancel_finished_get_fname(Rest)];
-log_rec_cancel_finished_get_fname([{error,{file_write,{_,FName}}}|Rest]) ->
- [FName|log_rec_cancel_finished_get_fname(Rest)];
-log_rec_cancel_finished_get_fname([{ok,FName}|Rest]) ->
- [FName|log_rec_cancel_finished_get_fname(Rest)];
-log_rec_cancel_finished_get_fname([{error,{truncated,{_,FName}}}|Rest]) ->
- [FName|log_rec_cancel_finished_get_fname(Rest)];
-log_rec_cancel_finished_get_fname([{error,{truncated,FName}}|Rest]) ->
- [FName|log_rec_cancel_finished_get_fname(Rest)];
-log_rec_cancel_finished_get_fname([_|Rest]) -> % This shall not happend.
- log_rec_cancel_finished_get_fname(Rest);
-log_rec_cancel_finished_get_fname([]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% EOF
diff --git a/lib/inviso/src/inviso_lfm.erl b/lib/inviso/src/inviso_lfm.erl
deleted file mode 100644
index 085048518c..0000000000
--- a/lib/inviso/src/inviso_lfm.erl
+++ /dev/null
@@ -1,431 +0,0 @@
-%% ``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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights 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$
-%%
-%% Author: Lennart �hman, [email protected]
-%%
-%% INVISO LogFile Merger.
-%%
-%% Merges all log-entries in all files in Files in chronological order
-%% into what ever is handled by WorkHandlerFun. Note that Files can contain
-%% several files. Both in the sence that it can be a wrapset. But also because
-%% the log is spread over more than one LogFiles (i.e trace_log + ti_log).
-%% It is further possible to use another reader-process (for the logfiles)
-%% than the default one. This is useful if the logfiles are formatted in
-%% another way than as done by a trace-port.
-
--module(inviso_lfm).
-
-%% -----------------------------------------------------------------------------
-%% API exports.
-%% -----------------------------------------------------------------------------
-
--export([merge/2,merge/3,merge/4,merge/5,merge/6]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Default handler exports.
-%% -----------------------------------------------------------------------------
-
--export([outfile_opener/1,outfile_writer/4,outfile_closer/1]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Formatting functions.
-%% -----------------------------------------------------------------------------
-
--export([format_arguments/3,format_argument_string/2]).
-%% -----------------------------------------------------------------------------
-%% Internal exports.
-%% -----------------------------------------------------------------------------
-
--export([init_receiver/7]).
-%% -----------------------------------------------------------------------------
-
-%% merge(Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerData)=
-%% {ok,Count} | {error,Reason}
-%% merge(Files,OutputFile) =
-%%
-%% Files=[FileDescription,...]
-%% FileDescription=FileSet | {reader,Mod,Func,FileSet}
-%% FileSet={Node,LogFiles} | {Node,[LogFiles,...]}
-%% in the latter case the LogFiles must be sorted, beginning with the oldest.
-%% LogFiles=[{trace_log,Files} [,{ti_log,[FileName]}] ]
-%% either just trace_log or trace_log and ti_log.
-%% Files=[FileName] | [FileName,...]
-%% in the latter case it is a wrapset.
-%% BeginHandlerFun= ( fun(HandlerData)->{ok,NewHandleData} | {error,Reason} )
-%% WorkHandlerFun= ( fun(Node,Term,PidMappings,HandlerData)->
-%% {ok,NewHandlerData} | {error,Reason}
-%% EndHandlerFun= ( fun(HandlerData)->ok | {error,Reason} )
-%% Count=integer(), the total number of handled log entries.
-%%
-%% Merges all logfiles in Files together into one common log file, in chronological
-%% order according to the time-stamps in each log. Each entry is also marked with
-%% the node name in the merged log.
-%% Configuration:
-%% If a non-default reader shall be used, Mod:Func(ReceiverPid,LogFiles) shall
-%% spawn a reader process complying to the receiver/reader message protocoll.
-%% The default reader reads logs generated by a trace-port.
-%% BeginHandler is called before any logentries are processed, typically to open
-%% the out-file, if any.
-%% WorkHandlerFun is called for every log-entry. It typically writes the output.
-%% EndHandlerFun is called when the last reader has finished, typically to
-%% close the outfile.
-%%
-%% Using merge/2 assumes you want to use default handlers writing to a file.
-merge(Files,OutputFile) when is_list(OutputFile) ->
- merge(Files,fun outfile_opener/1,fun outfile_writer/4,fun outfile_closer/1,OutputFile,off).
-merge(Files,WorkHandlerFun,HandlerData) when is_function(WorkHandlerFun) ->
- merge(Files,void,WorkHandlerFun,void,HandlerData,off);
-merge(Files,OutputFile,Dbg) when is_list(OutputFile) ->
- merge(Files,fun outfile_opener/1,fun outfile_writer/4,fun outfile_closer/1,OutputFile,Dbg).
-merge(Files,WorkHandlerFun,HandlerData,Dbg) when is_function(WorkHandlerFun) ->
- merge(Files,void,WorkHandlerFun,void,HandlerData,Dbg).
-merge(Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerData) ->
- merge(Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerData,off).
-merge(Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerData,Dbg) ->
- ReceiverPid=spawn_link(?MODULE,
- init_receiver,
- [self(),Files,BeginHandlerFun,WorkHandlerFun,
- EndHandlerFun,HandlerData,Dbg]),
- wait_for_response(ReceiverPid).
-
-wait_for_response(ReceiverPid) ->
- receive
- {reply,ReceiverPid,Reply} ->
- Reply;
- {'EXIT',ReceiverPid,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% =============================================================================
-%% Code for the receiver process.
-%% =============================================================================
-
-%% Initial function for the receiver process. This function must be exported.
-init_receiver(From,Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerData,Dbg) ->
- case setup_readers(Files) of % Create the reader processes.
- {ok,Readers} ->
- process_flag(trap_exit,true),
- if
- is_function(BeginHandlerFun) ->
- case catch BeginHandlerFun(HandlerData) of
- {ok,NewHandlerData} ->
- init_receiver_2(From,WorkHandlerFun,EndHandlerFun,
- NewHandlerData,Dbg,Readers);
- {error,Reason} -> % Faulty begin-function.
- From ! {reply,self(),{error,{begin_handler,Reason}}};
- {'EXIT',Reason} ->
- From ! {reply,self(),{error,{begin_handler,Reason}}}
- end;
- true -> % There is no begin-handler.
- init_receiver_2(From,WorkHandlerFun,EndHandlerFun,HandlerData,Dbg,Readers)
- end;
- {error,Reason} ->
- From ! {reply,self(),{error,{files,Reason}}}
- end.
-
-init_receiver_2(From,WorkHandlerFun,EndHandlerFun,HandlerData,Dbg,Readers) ->
- {NewReaders,EntryStruct}=mk_entrystruct(Readers,Dbg),
- {Reply,NewHandlerData}=
- loop(From,WorkHandlerFun,HandlerData,NewReaders,EntryStruct,Dbg,0),
- if
- is_function(EndHandlerFun) ->
- case EndHandlerFun(NewHandlerData) of
- ok ->
- From ! {reply,self(),Reply};
- {error,_Reason} ->
- From ! {reply,self(),Reply}
- end;
- true -> % Reply directly then, no finish fun.
- From ! {reply,self(),Reply}
- end.
-
-%% Function that spawns a help process for each group of files in the list.
-%% The help process will read entries from the input files in the correct order
-%% and deliver them to the receiver process.
-%% Note that there is a possibility to design your own readers. The default
-%% reader understands trace-port generated logfiles.
-%% Returns a list of {Node,Pid}.
-setup_readers(Files) ->
- setup_readers_2(Files,[]).
-
-setup_readers_2([{reader,Mod,Func,{Node,FileStruct}}|Rest],Acc) ->
- Pid=spawn_link(Mod,Func,[self(),FileStruct]),
- setup_readers_2(Rest,[{Node,Pid}|Acc]);
-setup_readers_2([{Node,FileStruct}|Rest],Acc) ->
- Pid=spawn_link(inviso_lfm_tpfreader,init,[self(),FileStruct]),
- setup_readers_2(Rest,[{Node,Pid}|Acc]);
-setup_readers_2([],Acc) ->
- {ok,Acc};
-setup_readers_2([Faulty|_],_Acc) ->
- {error,{bad_reader_spec,Faulty}}.
-%% -----------------------------------------------------------------------------
-
-%% This is the workloop that polls each reader for messages and writes them
-%% in the correct order.
-loop(From,WorkHFun,HData,Readers,EntryStruct,Dbg,Count) ->
- case find_oldest_entry(EntryStruct) of
- {Pid,Node,PidMappings,Term} ->
- case get_and_insert_new_entry(From,Node,Pid,Readers,EntryStruct,Dbg) of
- {ok,{NewReaders,NewEntryStruct}} ->
- case WorkHFun(Node,Term,PidMappings,HData) of
- {ok,NewHData} ->
- loop(From,WorkHFun,NewHData,NewReaders,NewEntryStruct,Dbg,Count+1);
- {error,Reason} -> % Serious, we cant go on then.
- stop_readers(NewReaders),
- {{error,{writing_output_file,Reason}},HData}
- end;
- {stop,_Reason} -> % The original caller is no longer there!
- stop_readers(Readers),
- {error,HData}
- end;
- done -> % No more readers.
- {{ok,Count},HData}
- end.
-
-%% Help function which finds the oldest entry in the EntryStruct. Note that the
-%% timestamp can actually be the atom 'false'. This happens for instance if it is
-%% a dropped-messages term. But since 'false' is smaller than any tuple, that
-%% term will be consumed immediately as soon as it turns up in EntryList.
-find_oldest_entry(EntryStruct) ->
- case list_all_entries(EntryStruct) of
- [] -> % The we are done!
- done;
- EntryList when is_list(EntryList) -> % Find smallest timestamp in here then.
- {Pid,Node,PidMappings,_TS,Term}=
- lists:foldl(fun({P,N,PMap,TS1,T},{_P,_N,_PMap,TS0,_T}) when TS1<TS0 ->
- {P,N,PMap,TS1,T};
- (_,Acc) ->
- Acc
- end,
- hd(EntryList),
- EntryList),
- {Pid,Node,PidMappings,Term}
- end.
-
-%% Help function which signals all reader process to clean-up and terminate.
-%% Returns nothing significant.
-stop_readers([Pid|Rest]) ->
- Pid ! {stop,self()},
- stop_readers(Rest);
-stop_readers([]) ->
- ok.
-%% -----------------------------------------------------------------------------
-
-%% Help function which tries to replace the entry by Pid in EntryStruct with
-%% a new one from that process. If one is returned on request, it replaces
-%% the old one in EntryStruct. If Pid is done or otherwise dissapears, Pid
-%% is simply removed from Readers and the EntryStruct.
-get_and_insert_new_entry(Node,Pid,Readers,EntryStruct,Dbg) ->
- get_and_insert_new_entry(void,Node,Pid,Readers,EntryStruct,Dbg).
-
-get_and_insert_new_entry(From,Node,Pid,Readers,EntryStruct,Dbg) ->
- Pid ! {get_next_entry,self()},
- receive
- {'EXIT',From,Reason} -> % No one is waiting for our reply!
- {stop,Reason}; % No use continuing then.
- {next_entry,Pid,PidMappings,TS,Term} -> % We got a next entry from Pid!
- ets:insert(EntryStruct,{Pid,Node,PidMappings,TS,Term}),
- {ok,{Readers,EntryStruct}};
- {next_entry,Pid,{error,_Reason}} -> % Reading an entry went wrong.
- get_and_insert_new_entry(From,Node,Pid,Readers,EntryStruct,Dbg);
- {'EXIT',Pid,_Reason} -> % The process has terminated.
- ets:delete(EntryStruct,Pid),
- NewReaders=lists:delete(Pid,Readers),
- {ok,{NewReaders,EntryStruct}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function which from a list of reader processes creates the private
-%% storage where the oldest entry from each reader is always kept.
-%% Returns {Readers,EntryStruct}.
-mk_entrystruct(Pids,Dbg) ->
- TId=ets:new(list_to_atom("inviso_lfm_tab_"++pid_to_list(self())),[set]),
- mk_entrystruct_2(Pids,lists:map(fun({_,P})->P end,Pids),Dbg,TId).
-
-mk_entrystruct_2([{Node,Pid}|Rest],Readers,Dbg,EntryStruct) ->
- {ok,{NewReaders,NewEntryStruct}}=
- get_and_insert_new_entry(Node,Pid,Readers,EntryStruct,Dbg),
- mk_entrystruct_2(Rest,NewReaders,Dbg,NewEntryStruct);
-mk_entrystruct_2([],Readers,_Dbg,EntryStruct) ->
- {Readers,EntryStruct}.
-%% -----------------------------------------------------------------------------
-
-%% Help function that returns a list of our oldest entry structure.
-%% [{Pid,Node,PidMappings,TimeStamp,Term},...]
-list_all_entries(EntryStruct) ->
- ets:tab2list(EntryStruct).
-%% -----------------------------------------------------------------------------
-
-%% =============================================================================
-%% Default handlers for the receiver
-%% =============================================================================
-
-%% These functions are also exported in order to make them available when creating
-%% other funs in other modules.
-
-%% Default begin-handler.
-outfile_opener(FileName) ->
- case file:open(FileName,[write]) of
- {ok,FD} ->
- {ok,FD}; % Let the descriptor be handlerdata.
- {error,Reason} ->
- {error,{open,Reason}}
- end.
-
-%% Default work-handler.
-%% DEN H�R �R L�NGT IFR�N F�RDIG!!!
-outfile_writer(Node,Term,PidMappings,FD) ->
- io:format(FD,"~w ~w ~w~n",[Node,PidMappings,Term]),
- {ok,FD}.
-
-%% Default end-handler.
-outfile_closer(FD) ->
- file:close(FD),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% =============================================================================
-%% Formatting functions.
-%% =============================================================================
-
-%% This section contains a useful formatting function formatting an (function)
-%% argument list. It also offers a working example of how to write
-%% own datatype translators (which will be used by the formatting function to
-%% further enhance the output).
-
-%% format_arguments(Args,FOpts,Transaltors)=Args2 | <failure>
-%% Args=list(), list of the argument as usually given in a trace message,
-%% a stack trace or similar.
-%% FOpts=term(), formatting options understood by the translation functions.
-%% Translations=[Translator,...]
-%% Translator=fun(Term,FOpts)=TResult | {M,F}, where M:F(Term,FOpts)=TResult
-%% TResult={ok,TranslationString} | false
-%% Arg2=list(), list of Args where terms may be replaced by own representations.
-%% Note that terms not effected will remain as is, but if an own representation
-%% is choosen, that must be a string in order for any io format function to
-%% print it exactly as formatted here.
-format_arguments([Arg|Rest],FOpts,Translators) -> % More than one argument.
- [format_argument(Arg,FOpts,Translators)|format_arguments(Rest,FOpts,Translators)];
-format_arguments([],_FOpts,_Translators) ->
- []. % The empty list.
-
-%% Help function handling the various Erlang datatypes. There must hence be one
-%% clause here for every existing datatype.
-format_argument(List,FOpts,Translators) when is_list(List) ->
- case format_argument_own_datatype(List,FOpts,Translators) of
- {true,TranslationStr} ->
- TranslationStr;
- false ->
- format_argument_list(List,FOpts,Translators)
- end;
-format_argument(Tuple,FOpts,Translators) when is_tuple(Tuple) ->
- case format_argument_own_datatype(Tuple,FOpts,Translators) of
- {true,TranslationStr} -> % It was one of our special datatypes.
- TranslationStr;
- false -> % Regular tuple.
- format_argument_tuple(Tuple,FOpts,Translators)
- end;
-format_argument(Binary,FOpts,Translators) when is_binary(Binary) ->
- case format_argument_own_datatype(Binary,FOpts,Translators) of
- {true,TranslationStr} -> % It was one of our special datatypes..
- TranslationStr;
- false -> % Regular binary.
- format_argument_binary(Binary,FOpts,Translators)
- end;
-format_argument(Atom,_FOpts,_Translators) when is_atom(Atom) ->
- Atom;
-format_argument(Integer,_FOpts,_Translators) when is_integer(Integer) ->
- Integer;
-format_argument(Float,_FOpts,_Translators) when is_float(Float) ->
- Float;
-format_argument(Pid,_FOpts,_Translators) when is_pid(Pid) ->
- Pid;
-format_argument(Port,_FOpts,_Translators) when is_port(Port) ->
- Port;
-format_argument(Ref,_FOpts,_Translators) when is_reference(Ref) ->
- Ref;
-format_argument(Fun,_FOpts,_Translators) when is_function(Fun) ->
- Fun.
-
-%% Help function handling the case when an element is a list.
-format_argument_list([Element|Rest],FOpts,Translators) ->
- [format_argument(Element,FOpts,Translators)|
- format_argument_list(Rest,FOpts,Translators)];
-format_argument_list([],_FOpts,_Translators) ->
- [].
-
-%% Help function handling the case when an element is a tuple.
-format_argument_tuple(Tuple,FOpts,Translators) ->
- list_to_tuple(format_argument_tuple(Tuple,FOpts,Translators,size(Tuple),[])).
-
-format_argument_tuple(_,_,_,0,List) ->
- List;
-format_argument_tuple(Tuple,FOpts,Translators,Index,List) ->
- E=format_argument(element(Index,Tuple),FOpts,Translators),
- format_argument_tuple(Tuple,FOpts,Translators,Index-1,[E|List]).
-
-%% Help function handling the case when an element is a binary.
-format_argument_binary(Binary,_FOpts,_Translators) ->
- Binary.
-
-%% Help function trying to use the translations.
-format_argument_own_datatype(Term,FOpts,[Fun|Rest]) when is_function(Fun) ->
- case catch Fun(Term,FOpts) of
- {ok,TranslationStr} ->
- {true,TranslationStr};
- _ ->
- format_argument_own_datatype(Term,FOpts,Rest)
- end;
-format_argument_own_datatype(Term,FOpts,[{M,F}|Rest]) ->
- case catch M:F(Term,FOpts) of
- {ok,TranslationStr} ->
- {true,TranslationStr};
- _ ->
- format_argument_own_datatype(Term,FOpts,Rest)
- end;
-format_argument_own_datatype(Term,FOpts,[_|Rest]) ->
- format_argument_own_datatype(Term,FOpts,Rest);
-format_argument_own_datatype(_Term,_FOpts,[]) -> % There is no applicable format.
- false.
-%% -----------------------------------------------------------------------------
-
-%% format_argument_string(String,_FOpts)={ok,QuotedString} | false
-%% String=string() | term()
-%% QuotedString="String"
-%% Example of datatype checker that checks, in this case, that its argument is
-%% a string. If it is, it returns a deep list of the characters to print in order
-%% to make it a quoted string.
-format_argument_string(List=[_|_],_FOpts) -> % Must be at least one element.
- case format_argument_string_2(List) of
- true ->
- {ok,[$",List,$"]};
- false ->
- false
- end;
-format_argument_string(_,_FOpts) ->
- false.
-
-format_argument_string_2([C|Rest]) when (((C<127) and (C>=32)) or ((C>=8) and (C=<13))) ->
- format_argument_string_2(Rest);
-format_argument_string_2([_|_]) ->
- false;
-format_argument_string_2([]) ->
- true.
-%% -----------------------------------------------------------------------------
diff --git a/lib/inviso/src/inviso_lfm_tpfreader.erl b/lib/inviso/src/inviso_lfm_tpfreader.erl
deleted file mode 100644
index 6de4d11fe0..0000000000
--- a/lib/inviso/src/inviso_lfm_tpfreader.erl
+++ /dev/null
@@ -1,388 +0,0 @@
-%% ``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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights 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$
-%%
-%% Author: Lennart �hman, [email protected]
-
-%%
-%% INVISO LogFileMerger TracePort File READER.
-%%
-%% This module implements a reader process capable of reading traceport files
-%% and feeding them according to the logfile merger process message protocoll
-%% to the logfile merger process.
-%% This module can also serve as example for writing file readers for other
-%% file formats.
-%%
-%% A reader process must:
-%% Support the reader-receiver protocoll.
-%% receive next_entry message: {get_next_entry,ReceiverPid}
-%% recieve stop message should the receiver wish to quit: {stop,ReceiverPid}.
-%% send next_entry message, either with entry or fault-code.
-%% next_entry message contains:{next_entry,self(),PidMappings,Timestamp,Term}
-%% {next_entry,self(),Error}
-%% recognize receiver termination (EXIT-signal).
-%% Understand logfile structure, both filename structure and content.
-%% Understand content (log-entry) details to extract the entry and entry
-%% components as timestamp and originating pid (to make pid-mappings).
-%% Understand any trace information files (ti).
-%%
-%% The logfile structure written by inviso_rt_meta is:
-%% {Pid,Alias,Op,TimeStamp} where:
-%% Pid=pid(), if Alias==unalias: pid()|other_than_pid()
-%% Op=alias|unalias,
-%% TimeStamp=now()
-%% -----------------------------------------------------------------------------
--module(inviso_lfm_tpfreader).
-
--export([init/2]).
-%% -----------------------------------------------------------------------------
-
--export([handle_logfile_sort_wrapset/1]). % Exported as a service to other readers.
-%% -----------------------------------------------------------------------------
-
-%% init(RecPid,FileStruct)=N/A
-%% RecPid=pid(), the process id of the log file merger.
-%% FileStruct=LogFiles | [LogFiles,...]
-%% LogFiles=[{trace_log,[File,...]} [,{ti_log,[File]}] ]
-%% File=string()
-%% Spawn on this function to start a reader process for trace-port generated
-%% logfiles, possibly with inviso-generated ti-files.
-init(RecPid,LogFiles=[Tuple|_]) when is_tuple(Tuple) -> % Only one LogFiles.
- init(RecPid,[LogFiles]);
-init(RecPid,FileStruct) when is_list(FileStruct) ->
- logfiles_loop(RecPid,FileStruct).
-%% -----------------------------------------------------------------------------
-
-logfiles_loop(RecPid,[LogFiles|Rest]) ->
- {TIalias,TIunalias}=handle_ti_file(LogFiles),% If there is a ti-file, read it.
- Files=handle_logfiles(LogFiles), % Returns a sorted list of logfiles.
- case open_next_file(Files) of
- {ok,FileName,FD,NewFiles} ->
- case loop(RecPid,FileName,NewFiles,TIalias,TIunalias,FD) of
- next ->
- logfiles_loop(RecPid,Rest);
- stop ->
- true % Terminate normally.
- end;
- done -> % Hmm, already out of files.
- true; % Then lets terminate normally.
- {error,Reason} -> % Couldn't even open the first file.
- exit(Reason)
- end;
-logfiles_loop(_RecPid,[]) -> % No more files in LogFiles.
- true. % Terminate normally.
-
-%% This workloop reads an entry from the input file upon request from the merger
-%% process and sends it back to the merger process (Parent). If the file ends
-%% there are more files to open and read in Files, the next file will be opened.
-loop(RecPid,FileName,Files,TIalias,TIunalias,FD) ->
- receive
- {get_next_entry,RecPid} -> % The receiver request the next entry.
- case fetch_next(FileName,FD,Files) of
- {ok,Term,NewCurrFile,NewFiles,NewFD} ->
- TS=find_timestamp_in_term(Term),
- PidMappings=make_pid_mappings(Term,TIalias,TIunalias,TS),
- RecPid ! {next_entry,self(),PidMappings,TS,Term},
- loop(RecPid,NewCurrFile,NewFiles,TIalias,TIunalias,NewFD);
- {error,Reason} -> % Not a properly formatted entry.
- RecPid ! {next_entry,self(),{error,Reason}},
- loop(RecPid,FileName,Files,TIalias,TIunalias,FD);
- done -> % No more files to read in this LogFiles.
- next % Are there more Files in FileStruct?
- end;
- {stop,RecPid} -> % The receiver process is done.
- file:close(FD), % Close file and terminate normally.
- stop
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function which reads the next trace-entry from the file handled by FD, or if
-%% that file reaches EOF opens the next file in Files. Files must be sorted in
-%% the correct order.
-%% Returns {ok,Term,NewFileName,NewFiles,NewFD}, {error,Reason} or 'done'.
-fetch_next(FileName,FD,Files) ->
- case read_traceport_file(FileName,FD) of
- {ok,Term} -> % There were more terms in the file.
- {ok,Term,FileName,Files,FD}; % No changes necessary then.
- eof -> % This file is empty, try next file!
- file:close(FD),
- case open_next_file(Files) of
- {ok,NewFileName,NewFD,NewFiles} -> % A new file has been opened.
- fetch_next(NewFileName,NewFD,NewFiles); % Try again.
- done -> % No more files.
- done;
- {error,Reason} -> % Problems opening files.
- {error,Reason}
- end;
- {error,Reason} -> % Problems reading the file.
- {error,Reason}
- end.
-
-read_traceport_file(FileName,FD) ->
- case file:read(FD,5) of % Trace-port file entries start with 5 bytes.
- {ok,<<0,Size:32>>} -> % Each entry in a traceport file begins.
- case file:read(FD,Size) of
- {ok,Bin} when is_binary(Bin),size(Bin)=:=Size ->
- try binary_to_term(Bin) of
- Term -> % Bin was a properly formatted term!
- {ok,Term}
- catch
- error:_Reason -> % Not a properly formatted term!
- {error,{binary_to_term,[FileName,Bin]}}
- end;
- {ok,Bin} -> % Incorrect length.
- {error,{faulty_length,[FileName,Size,Bin]}};
- eof -> % This is premature end of file!
- {error,{premature_eof,FileName}}
- end;
- {ok,<<1,DroppedMsgs:32>>} ->
- {ok,{drop,DroppedMsgs}};
- {ok,JunkBin} -> % Don't understand, report it as error.
- {error,{junk,[FileName,JunkBin]}};
- eof -> % A correct end of file!
- eof
- end.
-
-%% Help function which opens a file in raw binary mode and returns
-%% {ok,FileName,FD,Rest} or {error,Reason}.
-open_next_file([]) -> % There are no more files to open.
- done;
-open_next_file([FileName|Rest]) ->
- case file:open(FileName,[read,raw,binary]) of
- {ok,FD} ->
- {ok,FileName,FD,Rest};
- {error,Reason} ->
- {error,{open,[FileName,Reason]}}
- end.
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Help functions.
-%% ==============================================================================
-
-
-%% Help function which extract the originating process id from the log entry
-%% term and returns a list of all associations to the PID found in TIalias.
-make_pid_mappings(_,void,_,_) -> % Trace Information is not used.
- []; % Simply no pid mappings then!
-make_pid_mappings(Term,TIalias,TIunalias,TS)
- when element(1,Term)==trace;element(1,Term)==trace_ts ->
- Pid=element(2,Term), % The pid.
- TempAliases=find_aliases(ets:lookup(TIalias,Pid),TS),
- remove_expired_aliases(TempAliases,TIalias,TIunalias,TS),
- lists:map(fun({_,_,Alias})->Alias end,
- find_aliases(ets:lookup(TIalias,Pid),TS));
-make_pid_mappings(_Term,_TIalias,_TIunalias,_TS) -> % Don't understand Term.
- []. % Simply no translations then!
-
-%% Help function traversing a list of ets-alias-table entries and returning a
-%% list of those old enough to have happend before TS.
-%% Note that it is possible to have an Offset in microseconds. This because an
-%% association may end up in the ti-file a short time after logentries starts
-%% to appear in the log file for the process in question. We therefore like to
-%% allow some slack,
-find_aliases(List,TS) ->
- lists:filter(fun({_,Now,_}) when Now<TS -> true;
- (_) -> false
- end,
- List).
-%% ------------------------------------------------------------------------------
-
-%% Help function which removes aliases that are no longer valid from the
-%% ETS table. It uses unalias entries which are older than TS but younger than
-%% the alias association.
-%% Returns nothing significant.
-remove_expired_aliases([{Pid,Now1,Alias}|Rest],TIalias,TIunalias,TS) ->
- Candidates=ets:lookup(TIunalias,Alias),
- lists:foreach(fun({_,Now2,P})
- when (Now2>Now1) and
- (Now2<TS) and
- ((P==Pid) or (not(is_pid(P)))) ->
- ets:delete_object(TIalias,{Pid,Now1,Alias}),
- true; % This alias is infact no longer.
- (_) ->
- false
- end,
- Candidates),
- remove_expired_aliases(Rest,TIalias,TIunalias,TS);
-remove_expired_aliases([],_,_,_) ->
- true.
-%% ------------------------------------------------------------------------------
-
-find_timestamp_in_term({trace_ts,_,_,_,TS}) ->
- TS;
-find_timestamp_in_term({trace_ts,_,_,_,_,TS}) ->
- TS;
-find_timestamp_in_term(_) -> % Don't know if there is a timestamp.
- false.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Help function handling a trace-information file and building the TIstruct storage.
-%% -----------------------------------------------------------------------------
-
-%% Help function which opens a standard ti-file, reads its content and
-%% builds two ETS-table where PID is primary index in the one for aliases, and
-%% the alias is primary index in the one for unalias.
-%% Returns a handle to the two ETS tables.
-%%
-%% This function currently handles:
-%% (1) plain straight raw binary files.
-handle_ti_file(FileStruct) ->
- case lists:keysearch(ti_log,1,FileStruct) of
- {value,{_,[FileName]}} when is_list(FileName) -> % There is one ti-file in this set.
- case file:open(FileName,[read,raw,binary]) of
- {ok,FD} ->
- TIdAlias=ets:new(list_to_atom("inviso_ti_atab_"++pid_to_list(self())),
- [bag]),
- TIdUnalias=ets:new(list_to_atom("inviso_ti_utab_"++pid_to_list(self())),
- [bag]),
- handle_ti_file_2(FD,TIdAlias,TIdUnalias), % Fill the table.
- file:close(FD),
- {TIdAlias,TIdUnalias};
- {error,_Reason} -> % Hmm, unable to open the file.
- {void,void} % Treat it as no ti-file.
- end;
- {value,_} -> % Some other file-set.
- {void,void}; % Pretend we don't understand.
- false -> % No ti-file in this set.
- {void,void}
- end.
-
-handle_ti_file_2(FD,TIdAlias,TIdUnalias) ->
- case file:read(FD,5) of % First read the header.
- {ok,<<_,Size:32>>} ->
- case file:read(FD,Size) of % Read the actual term.
- {ok,Bin} when size(Bin)=:=Size ->
- try binary_to_term(Bin) of
- {Pid,Alias,alias,NowStamp} -> % Save this association.
- ets:insert(TIdAlias,{Pid,NowStamp,Alias}),
- handle_ti_file_2(FD,TIdAlias,TIdUnalias);
- {Pid,Alias,unalias,NowStamp} ->
- ets:insert(TIdUnalias,{Alias,NowStamp,Pid}),
- handle_ti_file_2(FD,TIdAlias,TIdUnalias);
- _Term -> % Don't understand!
- handle_ti_file_2(FD,TIdAlias,TIdUnalias)
- catch
- error:_Reason -> % Badly formatted term
- handle_ti_file_2(FD,TIdAlias,TIdUnalias)
- end;
- {ok,_JunkBin} -> % To short probably.
- handle_ti_file_2(FD,TIdAlias,TIdUnalias); % Just drop it.
- eof -> % Should not come here, but
- {TIdAlias,TIdUnalias} % not much we can do, drop it and stop.
- end;
- {ok,_} -> % Also an error.
- handle_ti_file_2(FD,TIdAlias,TIdUnalias);
- eof -> % This is the normal eof point.
- {TIdAlias,TIdUnalias}
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Help functions sorting out what kind of logfiles we have to deal with.
-%% -----------------------------------------------------------------------------
-
-%% Help function which takes the filestruct argument and retrieves the names
-%% of all log-files mentioned there. If there are several logfiles, this function
-%% sorts them beginning with the oldest. That means that this function must
-%% have knowledge of how wrap-sets and so on works.
-%% Today known set-types:
-%% (1) file: One plain file.
-%% (2) wrap_set: List of files belonging to a wrap-set. Must be sorted.
-handle_logfiles(FileStruct) ->
- handle_logfiles_2(lists:keysearch(trace_log,1,FileStruct)).
-
-handle_logfiles_2({value,{_,[FileName]}}) when is_list(FileName)-> % One single plain file.
- [FileName];
-handle_logfiles_2({value,{_,Files}}) when is_list(Files) -> % A wrap-set.
- handle_logfile_sort_wrapset(Files);
-handle_logfiles_2(_) ->
- []. % Pretend there were no files otherwise.
-
-%% Help function which sorts the files in WrapSet beginning with the oldest.
-%% It assumes that a logfile is Name++SeqNo++Suffix.
-%% First the Name and Suffix must be established. We look at all files to find
-%% that out.
-%% Returns a list of sorted filenames.
-%% This function is exported since it might turn useful in own implemented
-%% readers.
-handle_logfile_sort_wrapset(Set=[_FileName]) -> % Only one file! Done then :-)
- Set;
-handle_logfile_sort_wrapset([]) -> % Also pretty simple :-)
- [];
-handle_logfile_sort_wrapset(FileSet) ->
- Prefix=find_common_prefix(FileSet),
- Suffix=find_common_prefix(lists:map(fun(Str)->lists:reverse(Str) end,FileSet)),
- find_hole_in_wrapset(FileSet,length(Prefix),length(Suffix)).
-
-%% Help function which finds the longest common prefix of all strings in the
-%% argument-list. Returns that string.
-find_common_prefix(Files=[[FirstChar|_]|_]) ->
- find_common_prefix_2(Files,FirstChar,[],[]);
-find_common_prefix([_|_]) -> % Means that prefix is "".
- "".
-
-find_common_prefix_2([[CurrChar|RestString]|Rest],CurrChar,Files,RevPrefix) ->
- find_common_prefix_2(Rest,CurrChar,[RestString|Files],RevPrefix);
-find_common_prefix_2([_String|_],_CurrChar,_Files,RevPrefix) ->
- lists:reverse(RevPrefix); % Found a difference.
-find_common_prefix_2([],CurrChar,Files=[[FirstChar|_]|_],RevPrefix) ->
- find_common_prefix_2(Files,FirstChar,[],[CurrChar|RevPrefix]);
-find_common_prefix_2([],CurrChar,_,RevPrefix) ->
- lists:reverse([CurrChar|RevPrefix]). % Actually, prefix was entire string!
-
-%% Help function which returns a sorted list of FileSet with the oldest first.
-find_hole_in_wrapset(FileSet,PreLen,SufLen) ->
- NumberedFiles=find_hole_in_wrapset_2(FileSet,PreLen,SufLen),
- find_hole_in_wrapset_3(lists:sort(NumberedFiles),0,[]). % Wrap-sets start at 0.
-
-find_hole_in_wrapset_2([FileName|Rest],PreLen,SufLen) ->
- [{list_to_integer(lists:sublist(FileName,PreLen+1,length(FileName)-PreLen-SufLen)),
- FileName}|
- find_hole_in_wrapset_2(Rest,PreLen,SufLen)];
-find_hole_in_wrapset_2([],_,_) ->
- [].
-
-find_hole_in_wrapset_3([{N,FileName}|Rest],N,Acc) ->
- find_hole_in_wrapset_3(Rest,N+1,[FileName|Acc]);
-find_hole_in_wrapset_3([{_,FileName}|Rest],_N,Acc) -> % FileName is the oldest one.
- [FileName|lists:map(fun({_,FN})->FN end,Rest)]++lists:reverse(Acc);
-find_hole_in_wrapset_3([],_,Acc) -> % Means all were in order.
- lists:reverse(Acc).
-%% -----------------------------------------------------------------------------
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/inviso/src/inviso_tool.erl b/lib/inviso/src/inviso_tool.erl
deleted file mode 100644
index 7d3cfb9da0..0000000000
--- a/lib/inviso/src/inviso_tool.erl
+++ /dev/null
@@ -1,3255 +0,0 @@
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2011. 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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%% Description:
-%% The inviso_tool implementation. A tool that uses inviso.
-%%
-%% Authors:
-%% Lennart Öhman, [email protected]
-%% -----------------------------------------------------------------------------
-
--module(inviso_tool).
-
-
-%% This is the inviso tool, which is a tool using the inviso trace application.
-%% It is developed to make tracing using trace cases possible in an environment
-%% of distributed Erlang nodes.
-%% A current restriction is that the Erlang nodes are supposed to have the same
-%% code. This since inviso tool can at this point not handle subsets of nodes.
-%% Instead all participating Erlang nodes are treated the same.
-%%
-%% The main functionality of the inviso tool are:
-%%
-%% (1) Handles start and stop of tracing at participating nodes.
-%% (2) Interprets trace-case files at a distributed network level.
-%% (The inviso runtime component is responsible for interpreting
-%% trace cases at a local level, if run in an autostart).
-%% (3) Keeps a command history log from which:
-%% (a) Sequences easily can be repeated.
-%% (b) Autostart configuration files can be created (understood by the
-%% default inviso autostart mechanism).
-%% (4) Performs reactivation in case tracing is suspended (manually or by
-%% an overload mechanism).
-%% (5) Can reconnect crashed nodes and by using the history bringing them
-%% up to speed.
-
-%% Distributed Erlang
-%% ------------------
-%% Inviso is built to run in a distributed environment.
-%% The inviso tool can also be used in a non distributed environment.
-
-%% Short description
-%% -----------------
-%% Start-up of the inviso tool
-%% During the start-up of the tool, the tool starts runtime components at
-%% all participating nodes. A runtime component can already be running at
-%% a particular node and will then simply be adopted.
-%%
-%% Session
-%% A session is said to start when tracing is initiated, and ends when
-%% made to stop by the user. When a session is stopped, tracing is stopped
-%% at all participating nodes. Note that participating nodes may come and
-%% go though the time-frame of a session. That means that if a node is
-%% reconnected it may resume its tracing in the current session through
-%% a 'restart_session'. A runtime component that is already tracing at the
-%% time start-session will simply be part of the session without its
-%% ingoing tracing being changed.
-%%
-%% Reactivation
-%% A node that is suspended can be reactivated to resume tracing. Note that
-%% tracing has in this situation never been stopped at the node in question.
-%% The inviso tool resumes the node and applies the history to it.
-%%
-%% Reconnect
-%% A node that is "down" from the inviso tool's perspective can be
-%% reconnected. During reconnection the tool restarts the runtime component
-%% at that node but does not (re)initiate tracing. The latter is called
-%% restart_session and must be done explicitly, unless the node in question
-%% is in fact already tracing. If the node is already tracing (due to an autostart
-%% for instance), it automatically becomes part of the ongoing session (if
-%% there is an ongoing session).
-%%
-%% Restart Session
-%% A node that has been down and has been reconnected can be made to
-%% initialize and resume its tracing. This is done by starting the session
-%% at the node in question and redoing the current history.
-
-%% Trace files within a session
-%% Since it is possible to init-tracing (from an inviso perspective) several
-%% times within the same session, a session may leave several trace log files
-%% behind. This must be resolved by the tracer data generator function
-%% (user supplied) by marking filenames in a chronological order but still
-%% making them possible to identify as part of the same session
-
-
-
-%% -----------------------------------------------------------------------------
-%% API exports.
-%% -----------------------------------------------------------------------------
-
--export([start/0,start/1,stop/0,stop/1]).
--export([reconnect_nodes/0,reconnect_nodes/1,
- start_session/0,start_session/1,
- reinitiate_session/0,reinitiate_session/1,
- restore_session/0,restore_session/1,restore_session/2,
- stop_session/0,
- reset_nodes/0,reset_nodes/1,
- atc/3,sync_atc/3,sync_atc/4,
- sync_rtc/2,sync_rtc/3,
- dtc/2,sync_dtc/2,sync_dtc/3,
- inviso/2]).
--export([reactivate/0,reactivate/1,
- save_history/1,
- get_autostart_data/1,get_autostart_data/2,
- get_activities/0,get_node_status/0,get_node_status/1,get_session_data/0]).
--export([flush/0,flush/1]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Debug exports.
-%% -----------------------------------------------------------------------------
-
--export([get_loopdata/0]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% OTP exports and call backs.
-%% -----------------------------------------------------------------------------
-
--export([init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Internal exports.
-%% -----------------------------------------------------------------------------
-
--export([tc_executer/4,reactivator_executer/6]).
--export([std_options_generator/1]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Constants.
-%% -----------------------------------------------------------------------------
-
-%% Defines the inviso function calls that shall be possible to do through the
-%% inviso API in this tool.
--define(INVISO_CMDS,
- [{tp,5},{tp,4},{tp,1},{tpl,5},{tpl,4},{tpl,1},
- {ctp,1},{ctp,2},{ctp,3},{ctpl,1},{ctpl,2},{ctpl,3},
- {tf,2},{tf,1},{ctf,2},{ctf,1},{ctf_all,0},
- {init_tpm,4},{init_tpm,7},
- {tpm,4},{tpm,5},{tpm,8},
- {tpm_tracer,4},{tpm_tracer,5},{init_tpm,8},
- {tpm_ms,5},{tpm_ms_tracer,5},
- {ctpm_ms,4},{ctpm,3},
- {tpm_localnames,0},{ctpm_localnames,0},
- {tpm_globalnames,0},{ctpm_globalnames,0},
- {ctp_all,0},
- {suspend,1},{cancel_suspension,0}]).
-%% -----------------------------------------------------------------------------
-
-%% These inviso functions shall be included in the command history log. Others
-%% are not relevant to be redone during a recactivation, a restart session or
-%% exported to an autostart file.
--define(INVISO_CMD_HISTORY,
- [{tp,5},{tp,4},{tp,1},{tpl,5},{tpl,4},{tpl,1},
- {ctp,1},{ctp,2},{ctp,3},{ctpl,1},{ctpl,2},{ctpl,3},
- {tf,2},{tf,1},{ctf,2},{ctf,1},{ctf_all,0},
- {init_tpm,4},{init_tpm,7},
- {tpm,4},{tpm,5},{tpm,8},
- {tpm_tracer,4},{tpm_tracer,5},{init_tpm,8},
- {tpm_ms,5},{tpm_ms_tracer,5},
- {ctpm_ms,4},{ctpm,3},
- {tpm_localnames,0},{ctpm_localnames,0},
- {tpm_globalnames,0},{ctpm_globalnames,0},
- {ctp_all,0}]).
-%% -----------------------------------------------------------------------------
-
-%% Since many function calls to inviso may take long time, especially if they
-%% involve difficult and many trace patterns to set, the default gen_server:call
-%% time out can not be used. We just do not want to get stuck for ever if some
-%% error occurs.
--define(CALL_TIMEOUT,60000).
-
-%% Default max time to wait for a trace case called synchronously to return.
--define(SYNC_TC_TIMEOUT,10000).
-
-%% Runtime components shall terminate when the tool terminates.
--define(DEFAULT_DEPENDENCY,{dependency,0}).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Record definitions.
-%% -----------------------------------------------------------------------------
-
-%% The loopdata record.
--record(ld,{
- dir=".", % Working dir of the tool.
- nodes=down, % The nodesD database, defaults to non-distr.
- c_node, % Location of inviso_c.
- c_pid, % The inviso control component.
- regexp_node, % Node for regexp expansions.
- tc_dict, % Trace case definition db.
- chl, % Command history log.
- session_state=passive, % passive | tracing
- tdg={inviso_tool_lib,std_tdg,[]}, % Tracer data generator func.
- tracer_data, % Current session nr and TDGargs.
- reactivators=[], % Pids of now running reactivators.
- tc_def_file, % Trace case definition file.
- optg={?MODULE,std_options_generator,[]}, % Generates options to add_nodes/3.
- initial_tcs=[], % Initial trace cases.
- started_initial_tcs=[], % Cases that must be stopped when stop_tracing.
- history_dir, % File path for history file.
- keep_nodes=[], % Nodes that shall not be cleared when stopping.
- debug=false % Internal debug mode
- }).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% API
-%% =============================================================================
-
-%% start()={ok,Pid} | {error,{already_started,pid()}}
-%% start(Config)
-%% Config=[{Opt,Value},...], list of tuple options.
-%% Opt=dir|nodes|c_node|regexp_node|tdg|tc_def_file|optg|initial_tcs|
-%% history_dir|keep_nodes
-%% Starts the inviso_tool process. Options in Config are the same as those
-%% which are kept in the #ld structure.
-start() ->
- start([]).
-start(Config) ->
- gen_server:start({local,?MODULE},?MODULE,Config,[]).
-%% -----------------------------------------------------------------------------
-
-%% stop(UntouchedNodes)=
-%% stop()={ok,NodeResults} | NodeResult | {error,Reason}
-%% UntouchedNodes=list(), nodes where any trace patterns shall not be removed.
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok | {error,Reason} | patterns_untouched
-%% Stops the inviso tool and the inviso control component. Runtime components are
-%% stopped by them selves depending on their dependcy of the control component.
-%% All runtime components that are not marked as to be kept will have their
-%% trace patterns cleared before the inviso control component is shutdown.
-%% The NodeResults indicates which nodes were successfullt handled.
-stop() ->
- stop([]).
-stop(UntouchedNodes) ->
- gen_server:call(?MODULE,{stop,UntouchedNodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reconnect_nodes()=NodeResult; function for the nod-distributed case.
-%% reconnect_nodes(Nodes)={ok,NodesResults}
-%% NodesResults=[{Node,NodeResult},...]
-%% NodeResult={ok,{State,Status}} | {error,NReason}
-%% State=tracing | inactive
-%% Status=running | suspended
-%% NReason=unknown_node | already_connected | down
-%% (Re)starts the inviso runtime components at Nodes. Depending on its state
-%% (new,idle or tracing) and if the tool is running a session or not, it becomes
-%% part of the tool's ongoing session. If the newly reconnected node is not
-%% tracing but the tool runs a session, the node must be reinitiated to become
-%% tracing.
-reconnect_nodes() ->
- gen_server:call(?MODULE,{reconnect_nodes,local_runtime},?CALL_TIMEOUT).
-reconnect_nodes(Node) when is_atom(Node) ->
- reconnect_nodes([Node]);
-reconnect_nodes(Nodes) when is_list(Nodes) ->
- gen_server:call(?MODULE,{reconnect_nodes,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% start_session()={ok,{SessionNr,InvisoReturn}} | {error,Reason}
-%% start_session(MoreTDGargs)=
-%% MoreTDGargs=list(), prepended to the fixed list of args used when calling the
-%% tracer data generator function.
-%% SessionNr=integer(), trace sessions are numbered by the tool.
-%% InvisoReturn=If successful inviso call, the returnvalue from inviso.
-%% Note that individual nodes may be unsuccessful. See inviso:init_tracing/1
-%% Initiates tracing at all participating nodes.
-start_session() ->
- start_session([]).
-start_session(MoreTDGargs) ->
- gen_server:call(?MODULE,{start_session,MoreTDGargs},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reinitiate_session(Nodes)={ok,InvisoReturn} | {error,Reason}
-%% InvisoReturn=If successful inviso call, the returnvalue from inviso:init_tracing/1.
-%% Note that individual nodes may be unsuccessful. Mentioned nodes not part
-%% of the tool or not in state inactive will be marked as failing by the
-%% tool in the InvisoReturn.
-%% To reinitate a node means to (inviso) init tracing at it according to saved
-%% tracer data generator arguments for the current session and then redo the current
-%% history to bring it up to speed. Note that the tool must be running a session
-%% for reinitiate to work.
-reinitiate_session() ->
- gen_server:call(?MODULE,{reinitiate_session,local_runtime},?CALL_TIMEOUT).
-reinitiate_session(Nodes) ->
- gen_server:call(?MODULE,{reinitiate_session,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% restore_session()=
-%% restore_session(MoreTDGargs)=
-%% restore_session(FileName)=
-%% restore_session(FileName,MoreTDGargs)={ok,{SessionNr,InvisoReturn}} | {error,Reason}
-%% The two first clauses will start a new session using the last history. This
-%% implies that there must have been a session running prior.
-%% The two last clauses starts a session and reads a history file and executes the
-%% tracecases in it at all inactive nodes.
-%% In both cases the reused or read history becomes the current histoy, just if the
-%% session had been initiated manually. The tool may not
-%% have a session ongoing, and nodes already tracing (nodes which were adopted)
-%% are not effected. Just like when starting a session manually.
-restore_session() ->
- restore_session([]).
-restore_session([]) -> % This cant be a filename.
- gen_server:call(?MODULE,{restore_session,[]},?CALL_TIMEOUT);
-restore_session(FileNameOrMoreTDGargs) ->
- case is_string(FileNameOrMoreTDGargs) of
- true -> % Interpret it as a filename.
- restore_session(FileNameOrMoreTDGargs,[]);
- false -> % The we want to use last session history!
- gen_server:call(?MODULE,{restore_session,FileNameOrMoreTDGargs},?CALL_TIMEOUT)
- end.
-restore_session(FileName,MoreTDGargs) ->
- gen_server:call(?MODULE,{restore_session,{FileName,MoreTDGargs}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% stop_session()={ok,{SessionNr,Result}} | {error,Reason}
-%% SessionNr=integer()
-%% Result=[{Node,NodeResult},...] | NonDistributedNodeResult
-%% NodeResult=ok | {error,Reason}
-%% NonDistributedNodeResult=[ok] | []
-%% Stops inviso tracing at all participating nodes. The inviso runtime components
-%% will go to state idle. It is now time to fetch the logfiles. Will most often
-%% succeed. Will only return an error if the entire inviso call returned an
-%% error. Not if an individual node failed stop tracing successfully.
-%% Any running trace case, including reactivator processes will be terminated.
-stop_session() ->
- gen_server:call(?MODULE,stop_session,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reset_nodes()=NodeResult | {error,Reason}
-%% reset_nodes(Nodes)={ok,NodeResults} | {error,Reason}
-%% NodeResults and NodeResult as returned by inviso:clear/1 and /0.
-%% Clear nodes from trace flags, trace patterns and meta trace patterns. The tool
-%% must not be having a running session.
-reset_nodes() ->
- gen_server:call(?MODULE,{reset_nodes,local_runtime},?CALL_TIMEOUT).
-reset_nodes(Nodes) ->
- gen_server:call(?MODULE,{reset_nodes,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% atc(TC,Id,Vars)=ok | {error,Reason}
-%% TC=atom(), name of the trace case.
-%% Id=term(), given name of this usage of TC.
-%% Vars=list(), list of variable bindings [{Var,Value},...], Var=atom(),Value=term().
-%% Function activating a trace case. The trace case must be defined in the
-%% trace case dictionary. The 'ok' return value is only a signal that the
-%% trace case has started successfully. It may then run for as long as it is
-%% programmed to run. An erroneous return value does not necessarily mean that
-%% the trace case has not been executed. It rather means that is undetermined
-%% what happend.
-atc(TC,Id,Vars) ->
- gen_server:call(?MODULE,{atc,{TC,Id,Vars}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% sync_atc(TC,Id,Vars)=Result | {error,Reason}
-%% sync_atc(TC,Id,Vars,TimeOut)=
-%% Result=term(), what ever is returned be the last expression in the trace case.
-%% TimeOut=interger() | infinity, the max wait time for the trace case to finnish.
-%% As atc/3 but waits for the trace case to finish.
-sync_atc(TC,Id,Vars) ->
- gen_server:call(?MODULE,{sync_atc,{TC,Id,Vars,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
-sync_atc(TC,Id,Vars,TimeOut) ->
- gen_server:call(?MODULE,{sync_atc,{TC,Id,Vars,TimeOut}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% sync_rtc(TC,Vars)=Result | {error,Reason}
-%% sync_rtc(TC,Vars,TimeOut)=
-%% Result=term(), what ever is returned be the last expression in the trace case.
-%% TimeOut=interger() | infinity, the max wait time for the trace case to finnish.
-%% As sync_atc/3 but the trace case is not marked as activated. It is mearly placed
-%% in the history. Hence with sync_rtc a trace case can be "activated" multiple time.
-sync_rtc(TC,Vars) ->
- gen_server:call(?MODULE,{sync_rtc,{TC,Vars,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
-sync_rtc(TC,Vars,TimeOut) ->
- gen_server:call(?MODULE,{sync_rtc,{TC,Vars,TimeOut}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% dtc(TC,Id)=ok | {error,Reason}
-%% Deactivates a previosly activated trace case. This function can only be used
-%% on trace cases that has a deactivation defined in the trace case dictionary.
-%% There is of course really no difference between a file containing an activation
-%% compared to a deactivation. But to be able cancelling activations out from the
-%% history log, a defined deactivation is essential.
-%% As with activation, the returned 'ok' simply indicates the start of the trace
-%% case.
-dtc(TC,Id) ->
- gen_server:call(?MODULE,{dtc,{TC,Id}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% sync_dtc(TC,Id)=Result | {error,Reason}
-%% sync_dtc(TC,Id,TimeOut)=
-%% Synchronous deactivation of trace case. See dtc/2 and sync_atc/3 for
-%% parameters.
-sync_dtc(TC,Id) ->
- gen_server:call(?MODULE,{sync_dtc,{TC,Id,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
-sync_dtc(TC,Id,TimeOut) ->
- gen_server:call(?MODULE,{sync_dtc,{TC,Id,TimeOut}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% inviso(Cmd,Args)=Result
-%% Cmd=atom(), the (inviso) function name that shall be called.
-%% Args=list(), the arguments to Cmd.
-%% Result=term(), the result from the inviso function call.
-%% This function executes a Cmd in the inviso tool context. The inviso call will
-%% be logged in history log and thereby repeated in case of a reactivation.
-%% Note that this function is intended for use with inviso function API without
-%% specifying any nodes, since the function call is supposed to be carried out on
-%% all nodes.
-%% When these functions are written to an autostart config file by the tool there
-%% is supposed to be a translation to inviso_rt functions.
-inviso(Cmd,Args) ->
- gen_server:call(?MODULE,{inviso,{Cmd,Args}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reactivate()=ok | {error,Reason}
-%% reactivate(Node)=ok | {error,Reason}
-%% Moves a runtime component from suspended to the state running. This can be
-%% done for both tracing and inactive nodes. The later is necessary since you
-%% may have stopped tracing with a node suspended.
-%% In case the node is tracing, commands in the command history log are redone at
-%% the node in questions.
-%% Note that this function returns 'ok' before the node is running. This because the
-%% the reactivated history is done by a separate process and there is no guarantee
-%% when it will be ready. The reactivated node will not be marked as running in
-%% the tool until done reactivating.
-%% Further it is important to understand that if there are "ongoing" tracecases
-%% (i.e tracecase scripts that are currently executing) and this node was running
-%% at the time that tracecase script started to execute, the list of nodes bound
-%% to the Nodes variable in that script executer includes this node. Making it
-%% no longer suspended makes it start executing inviso commands from where ever
-%% such are called. Hence the reactivation may be interferred by that tracecase.
-reactivate() -> % Non-distributed API.
- reactivate(node()).
-reactivate(Node) ->
- gen_server:call(?MODULE,{reactivate,Node},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% save_history(FileName)={ok,AbsFileName} | {error,Reason}
-%% Saves the currently collected command history log to a file. The file will
-%% be a binary-file. If FileName is an absolute path, it will be saved to that
-%% file. Otherwise the history dir will be used. If no history dir was specified
-%% the tool dir will be used, prepended to FileName.
-save_history(FileName) ->
- gen_server:call(?MODULE,{save_history,FileName},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_autostart_data(Nodes,Dependency)={ok,{AutoStartData,NodeResults} |
-%% {ok,{AutoStartData,NodeResult}} | {error,Reason}
-%% Dependency=inviso dependency parameter which will be used for every
-%% autostarted runtime component (included in Options).
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,{Options,{tdg,{M,F,CompleteTDGargs}}}} | {error,Reason}
-%% Options=add_nodes options to the inviso runtime component.
-%% M,F=atom(), the module and function for tracerdata generation.
-%% CompleteTDGargs=list(), all arguments as they are given to the tracer
-%% data generator function.
-%% AutostartData=[CaseSpec,...]
-%% CaseSpec={file,{FileName,Bindings}} | {mfa,{M,F,Args}}
-%% FileName=string(), pointing out the trace case file. Note that this
-%% is the same as the path used by the tool.
-%% Bindings=Var bindings used according to the history for the
-%% invocation.
-%% M,F=atom(), the function that shall be called (normally some inviso).
-%% Args=list(), the actual arguments. Note that this may contain things
-%% which can not be written to file (ports, pids,...).
-%% Function returning information on how to autostart a node to make it trace
-%% according to the current history. The inviso_tool does not know how to write
-%% the necessary files at the nodes in question. That must be done by the user
-%% of the tool, guided by the return value from this function.
-%% Note that there will be two types of trace case files. Regular trace case
-%% files and binaries returned from this function. The latter contains the
-%% inviso commands which have been executed. Note that the order amongst the
-%% trace cases and binaries is of importance (otherwise they will be redone in
-%% an incorrect order).
-get_autostart_data(Dependency) ->
- gen_server:call(?MODULE,{get_autostart_data,Dependency},?CALL_TIMEOUT).
-get_autostart_data(Nodes,Dependency) ->
- gen_server:call(?MODULE,{get_autostart_data,{Nodes,Dependency}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_activities()={ok,Ongoing} | {error,Reason}
-%% Ongoing=list(); [ [TraceCases] [,Reactivators] ]
-%% TraceCases={tracecases,TraceCaseList}
-%% TraceCaseList=[{{TCname,Id},Phase},...]
-%% Phase=activating | deactivating
-%% Reactivators={reactivating_nodes,ReactivatingNodes}
-%% ReactivatingNodes=[Node,...]
-%% Returns a list of assynchronous tracecases and nodes doing reactivation at
-%% this momement. This can be useful to implement "home brewn" synchronization,
-%% waiting for the runtime components to reach a certain state.
-get_activities() ->
- gen_server:call(?MODULE,get_activities,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_status(Node)={ok,StateStatus} | {error,Reason}
-%% StateStatus={State,Status} | reactivating | down
-%% State=tracing | inactive | trace_failure
-%% Status=running | suspended
-get_node_status() ->
- get_node_status(local_runtime).
-get_node_status(Node) ->
- gen_server:call(?MODULE,{get_node_status,Node},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_session_data()={ok,{Status,SessionNr,TDGargs}} | {error,Reason}
-%% Status=tracing | not_tracing, info about current/last session.
-%% SessionNr=integer()
-%% TDGargs=list(), list of the arguments that will be given to the tracer data
-%% generator function (not including the leading Nodes list).
-%% Returns data about the current or last session.
-get_session_data() ->
- gen_server:call(?MODULE,get_session_data,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% flush()={ok,NodeResults} | NodeResult | {error,Reason}
-%% flush(Nodes)={ok,NodesResults} | {error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok | {error,Reason}
-%% Makes runtime components flush their trace ports.
-flush() ->
- gen_server:call(?MODULE,flush,?CALL_TIMEOUT).
-flush(Nodes) ->
- gen_server:call(?MODULE,{flush,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_loopdata()=#ld
-%% Debug API returning the internal loopdata structure. See #ld above for details.
-get_loopdata() ->
- gen_server:call(?MODULE,get_loopdata,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Internal APIs.
-%% -----------------------------------------------------------------------------
-
-%% tc_executer_reply(To,Reply)=nothing significant
-%% To=pid()
-%% Reply=term()
-%% Internal API used by a trace case executer process to signal its completion.
-tc_executer_reply(To,Reply) ->
- gen_server:cast(To,{tc_executer_reply,Reply}).
-%% -----------------------------------------------------------------------------
-
-%% Internal API used by a reactivator process indicating it is done with the
-%% history log it has got so far.
-%% Timeout set to infinity since the tool may be busy, then the reactivator just
-%% have to wait. If the tool crashes the reactivator will be go down too automatically.
-reactivator_reply(TPid,Counter) ->
- gen_server:call(TPid,{reactivator_reply,{Counter,self()}},infinity).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% gen_server implementation.
-%% =============================================================================
-
-init(Config) ->
- case fetch_configuration(Config) of % From conf-file and Config.
- {ok,LD} when is_record(LD,ld) ->
- case start_inviso_at_c_node(LD) of
- {ok,CPid} ->
- LD2=start_runtime_components(LD),
- LD3=read_trace_case_definitions(LD2),
- process_flag(trap_exit,true),
- start_subscribe_inviso_events(LD3#ld.c_node),
- {ok,LD3#ld{c_pid=CPid}};
- {error,Reason} -> % Most likely already running.
- {stop,{error,Reason}}
- end;
- {error,Reason} ->
- {stop,{error,{start_up,Reason}}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function starting the inviso control component at node c_node, or "here"
-%% if it is not a distributed network.
-start_inviso_at_c_node(#ld{c_node=undefined}) -> % Non distributed case.
- case inviso:start() of
- {ok,Pid} ->
- {ok,Pid};
- {error,Reason} ->
- {error,Reason}
- end;
-start_inviso_at_c_node(#ld{c_node=CNode}) ->
- case rpc:call(CNode,inviso,start,[]) of
- {ok,Pid} ->
- {ok,Pid};
- {error,{already_started,_}} -> % A control component already started.
- {error,{inviso_control_already_running,CNode}};
- {error,Reason} ->
- {error,Reason};
- {badrpc,Reason} ->
- {error,{inviso_control_node_error,Reason}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function starting the runtime components at all particapting nodes.
-%% It also updates the nodes structure in the #ld to indicate which nodes where
-%% successfully started. Returns a new #ld.
-%% Note that a runtime component may actually be running at one or several nodes.
-%% This is supposed to be the result of an (wanted) autostart. Meaning that the
-%% inviso tool can not handle the situation if a runtime component is not doing
-%% what it is supposed to do. In case a runtime component is already running it
-%% will be adopted and therefore marked as running.
-start_runtime_components(LD=#ld{c_node=undefined}) ->
- start_runtime_components_2(local_runtime,undefined,LD);
-start_runtime_components(LD=#ld{c_node=CNode,nodes=NodesD}) ->
- start_runtime_components_2(get_all_nodenames_nodes(NodesD),CNode,LD).
-start_runtime_components(Nodes,LD=#ld{c_node=CNode}) ->
- start_runtime_components_2(Nodes,CNode,LD).
-
-start_runtime_components_2(local_runtime,CNode,LD=#ld{optg=OptG}) ->
- Opts=start_runtime_components_mk_opts(local_runtime,OptG),
- case inviso:add_node(mk_rt_tag(),Opts) of
- {ok,NAnsw} -> % Should be more clever really!
- NewNodesD=update_added_nodes(CNode,{ok,NAnsw},LD#ld.nodes),
- LD#ld{nodes=NewNodesD};
- {error,_Reason} ->
- LD
- end;
-start_runtime_components_2([Node|Rest],CNode,LD=#ld{optg=OptG}) ->
- Opts=start_runtime_components_mk_opts(Node,OptG),
- case rpc:call(CNode,inviso,add_nodes,[[Node],mk_rt_tag(),Opts]) of
- {ok,NodeResults} ->
- NewNodesD=update_added_nodes(CNode,NodeResults,LD#ld.nodes),
- start_runtime_components_2(Rest,CNode,LD#ld{nodes=NewNodesD});
- {error,_Reason} ->
- start_runtime_components_2(Rest,CNode,LD);
- {badrpc,_Reason} ->
- start_runtime_components_2(Rest,CNode,LD)
- end;
-start_runtime_components_2([],_,LD) ->
- LD.
-
-start_runtime_components_mk_opts(Node,{M,F,Args}) ->
- case catch apply(M,F,[Node|Args]) of
- {ok,Opts} when is_list(Opts) ->
- start_runtime_component_mk_opts_add_dependency(Opts);
- _ ->
- [?DEFAULT_DEPENDENCY]
- end.
-
-%% The options generator is not supposed to generate the dependency. Hence this
-%% function adds and if necessary removes an incorrectly added dependency tag.
-start_runtime_component_mk_opts_add_dependency(Opts) ->
- case lists:keysearch(dependency,1,Opts) of
- {value,_} -> % Not allowed!!!
- [?DEFAULT_DEPENDENCY|lists:keydelete(dependecy,1,Opts)];
- false ->
- [?DEFAULT_DEPENDENCY|Opts]
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function subscribing to inviso events from the inviso controller. This
-%% will make it possible to follow runtime components going down.
-start_subscribe_inviso_events(undefined) ->
- inviso:subscribe();
-start_subscribe_inviso_events(CNode) ->
- rpc:call(CNode,inviso,subscribe,[self()]). % Don't want the rpc-proc to subscribe!
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% gen_server handle call back functions.
-%% -----------------------------------------------------------------------------
-
-handle_call({stop,UntouchedNodes},_From,LD=#ld{nodes=NodesD,c_node=CNode,keep_nodes=KeepNodes})
- when is_list(UntouchedNodes) ->
- {stop,
- normal,
- remove_all_trace_patterns(CNode,
- UntouchedNodes++KeepNodes,
- get_available_nodes(NodesD)),
- LD};
-handle_call({stop,BadArg},_From,LD) ->
- {reply,{error,{badarg,BadArg}},LD};
-
-handle_call({reconnect_nodes,Nodes},_From,LD) ->
- case h_reconnect_nodes(Nodes,LD) of
- {ok,{Nodes2,NodesErr,NewLD}} ->
- if
- Nodes==local_runtime ->
- {reply,
- build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes),
- NewLD};
- is_list(Nodes) ->
- {reply,
- {ok,build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes)},
- NewLD}
- end;
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
-
-handle_call({start_session,MoreTDGargs},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- false -> % No session running.
- if
- is_list(MoreTDGargs) ->
- DateTime=calendar:universal_time(),
- {M,F,Args}=LD#ld.tdg,
- TDGargs=inviso_tool_lib:mk_tdg_args(DateTime,MoreTDGargs++Args),
- case h_start_session(M,F,TDGargs,LD) of
- {ok,{SessionNr,ReturnVal,NewLD}} -> % No nodes to initiate.
- NewLD2=add_initial_tcs_to_history(NewLD#ld.initial_tcs,
- NewLD#ld{chl=mk_chl(LD#ld.chl)}),
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD2#ld{session_state=tracing_sessionstate()}};
- {ok,{SessionNr,ReturnVal,Nodes2,NewLD}} ->
- NewLD2=do_initial_tcs(NewLD#ld.initial_tcs,
- Nodes2,
- NewLD#ld{chl=mk_chl(LD#ld.chl)}),
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD2#ld{session_state=tracing_sessionstate()}};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true -> % Faulty TDGargs.
- {reply,{error,{badarg,MoreTDGargs}},LD}
- end;
- true ->
- {reply,{error,session_already_started},LD}
- end;
-
-handle_call({reinitiate_session,Nodes},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true -> % The tool must be tracing.
- {M,F,_Args}=LD#ld.tdg,
- TDGargs=get_latest_tdgargs_tracer_data(LD#ld.tracer_data),
- case h_reinitiate_session(Nodes,M,F,TDGargs,LD) of
- {ok,{NodesErr,ReturnVal,NewLD}} ->
- {reply,
- {ok,build_reinitiate_session_reply(Nodes,NodesErr,ReturnVal)},
- NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- false -> % Must have a running session!
- {reply,{error,no_session},LD}
- end;
-
-handle_call({restore_session,{FileName,MoreTDGargs}},_From,LD=#ld{chl=OldCHL})
- when is_list(MoreTDGargs) ->
- case is_tracing(LD#ld.session_state) of
- false ->
- case catch make_absolute_path(FileName,LD#ld.dir) of
- AbsFileName when is_list(AbsFileName) ->
- case file:read_file(AbsFileName) of
- {ok,Bin} ->
- if
- is_list(MoreTDGargs) ->
- case catch replace_history_chl(OldCHL,
- binary_to_term(Bin)) of
- {ok,CHL} -> % The file was well formatted.
- case h_restore_session(MoreTDGargs,
- LD#ld{chl=CHL}) of
- {ok,{SessionNr,ReturnVal,NewLD}} ->
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD#ld{session_state=
- tracing_sessionstate()}};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- Error -> % Badly formatted file.
- {reply,
- {error,{bad_file,{AbsFileName,Error}}},
- LD}
- end;
- true ->
- {reply,{error,{badarg,MoreTDGargs}},LD}
- end;
- {error,Reason} ->
- {reply,{error,{read_file,Reason}},LD}
- end;
- Error ->
- {reply,{error,{bad_filename,{FileName,Error}}},LD}
- end;
- true ->
- {reply,{error,session_already_started},LD}
- end;
-%% This is doing restore session on the current history.
-handle_call({restore_session,MoreTDGargs},_From,LD=#ld{chl=CHL}) ->
- case is_tracing(LD#ld.session_state) of
- false ->
- case history_exists_chl(CHL) of
- true -> % There is a history to redo.
- if
- is_list(MoreTDGargs) ->
- case h_restore_session(MoreTDGargs,LD) of
- {ok,{SessionNr,ReturnVal,NewLD}} ->
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD#ld{session_state=tracing_sessionstate()}};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,MoreTDGargs}},LD}
- end;
- false ->
- {reply,{error,no_history},LD}
- end;
- true ->
- {reply,{error,session_already_started},LD}
- end;
-
-%% To stop tracing means stop_tracing through the inviso API. But we must also
-%% remove any help processes executing inviso commands (trace case executers
-%% and reactivators).
-%% Note that to be really sure we should actually wait for EXIT-signals from those
-%% processes before returning a successful returnvalue to the caller. In theory
-%% those processes could issue an inviso call effecting a new trace session started
-%% with init_tracing shortly after the call to stop_tracing. But too complicated! :-)
-%% Further, stop-tracing is done on all nodes in our nodes structure. Regardless
-%% if the node is tracing or not
-handle_call(stop_session,_From,LD=#ld{session_state=SState,chl=CHL,reactivators=ReAct}) ->
- case is_tracing(SState) of
- true ->
- NewCHL=stop_all_tc_executer_chl(CHL), % Stop any running trace case proc.
- NewReAct=stop_all_reactivators(ReAct), % Stop any running reactivators.
- case h_stop_session(LD) of
- {ok,{SessionNr,Result}} ->
- NewNodesD=set_inactive_nodes(Result,LD#ld.nodes),
- {reply,
- {ok,{SessionNr,Result}},
- LD#ld{session_state=passive_sessionstate(),
- nodes=NewNodesD,
- chl=NewCHL,
- reactivators=NewReAct,
- started_initial_tcs=[]}};
- {error,Reason} -> % Now we're really in deep shit :-)
- {reply,{error,{unrecoverable,Reason}},LD}
- end;
- false ->
- {reply,{error,no_session},LD}
- end;
-
-handle_call({reset_nodes,Nodes},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- false -> % We can not be in a session.
- {reply,h_reset_nodes(Nodes,LD#ld.c_node),LD};
- true ->
- {reply,{error,session_active},LD}
- end;
-
-%% Calling a trace-case, or "turning it on".
-handle_call({atc,{TC,Id,Vars}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of % Check that we are tracing now.
- true ->
- case h_atc(TC,Id,Vars,LD) of
- {ok,NewLD} -> % Trace case executed.
- {reply,ok,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- false -> % Can't activate if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({sync_atc,{TC,Id,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true ->
- if
- is_integer(TimeOut);TimeOut==infinity ->
- case h_sync_atc(TC,Id,Vars,TimeOut,LD) of
- {ok,NewLD,Result} ->
- {reply,Result,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,TimeOut}},LD}
- end;
- false ->
- {reply,{error,no_session},LD}
- end;
-
-handle_call({sync_rtc,{TC,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true ->
- if
- is_integer(TimeOut);TimeOut==infinity ->
- case h_sync_rtc(TC,Vars,TimeOut,LD) of
- {ok,NewLD,Result} ->
- {reply,Result,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,TimeOut}},LD}
- end;
- false ->
- {reply,{error,no_session},LD}
- end;
-
-
-handle_call({dtc,{TC,Id}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of % Check that we are tracing now.
- true ->
- case h_dtc(TC,Id,LD) of
- {ok,NewLD} ->
- {reply,ok,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- false -> % Can't activate if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({sync_dtc,{TC,Id,TimeOut}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of % Check that we are tracing now.
- true ->
- if
- is_integer(TimeOut);TimeOut==infinity ->
- case h_sync_dtc(TC,Id,TimeOut,LD) of
- {ok,NewLD,Result} ->
- {reply,Result,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,TimeOut}},LD}
- end;
- false -> % Can't activate if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({inviso,{Cmd,Args}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true ->
- if
- is_list(Args) ->
- case h_inviso(Cmd,Args,LD) of
- {ok,{Reply,NewLD}} ->
- {reply,Reply,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,Args}},LD}
- end;
- false -> % Can't do if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({reactivate,Node},_From,LD=#ld{nodes=NodesD,c_node=CNode}) ->
- case get_state_nodes(Node,NodesD) of
- {trace_failure,_} ->
- {reply,{error,trace_failure},LD};
- {State,suspended} -> % The node is infact suspended.
- case h_reactivate(Node,CNode) of
- ok ->
- case {State,is_tracing(LD#ld.session_state)} of
- {tracing,true} -> % Only then shall we redo cmds.
- {reply,ok,redo_cmd_history(Node,LD)};
- _ -> % All other just no longer suspended.
- {reply,ok,LD#ld{nodes=set_running_nodes(Node,NodesD)}}
- end;
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- reactivating ->
- {reply,{error,reactivating},LD};
- {_,running} ->
- {reply,{error,already_running},LD};
- down ->
- {reply,{error,not_available},LD};
- false ->
- {reply,{error,unknown_node},LD}
- end;
-
-handle_call({save_history,FileName},_From,LD=#ld{chl=CHL,dir=Dir,history_dir=HDir}) ->
- case lists:keysort(2,get_loglist_chl(CHL)) of
- [] -> % Empty history or no history.
- {reply,{error,no_history},LD};
- Log ->
- case h_save_history(HDir,Dir,FileName,Log) of
- {ok,AbsFileName} ->
- {reply,{ok,AbsFileName},LD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end
- end;
-
-handle_call({get_autostart_data,{Nodes,Dependency}},_From,LD=#ld{chl=CHL}) ->
- {ok,ASD} = build_autostart_data(lists:keysort(2,get_loglist_chl(CHL)),LD#ld.tc_dict),
- TDGargs=get_latest_tdgargs_tracer_data(LD#ld.tracer_data),
- {M,F,_}=LD#ld.tdg,
- OptsG=LD#ld.optg, % Addnodes options generator.
- {reply,
- h_get_autostart_data(Nodes,LD#ld.c_node,Dependency,ASD,M,F,TDGargs,OptsG),
- LD};
-
-handle_call({get_autostart_data,Dependency},From,LD=#ld{c_node=undefined}) ->
- handle_call({get_autostart_data,{local_runtime,Dependency}},From,LD);
-handle_call({get_autostart_data,Dependency},From,LD=#ld{nodes=NodesD}) ->
- Nodes=get_all_nodenames_nodes(NodesD),
- handle_call({get_autostart_data,{local_runtime,{Nodes,Dependency}}},From,LD);
-
-handle_call(get_activities,_From,LD=#ld{chl=CHL,reactivators=Reactivators}) ->
- TraceCases=get_ongoing_chl(CHL),
- RNodes=get_all_nodes_reactivators(Reactivators),
- ReturnList1=
- if
- TraceCases==[] ->
- [];
- true ->
- [{tracecases,TraceCases}]
- end,
- ReturnList2=
- if
- RNodes==[] ->
- ReturnList1;
- true ->
- [{reactivating_nodes,RNodes}|ReturnList1]
- end,
- {reply,{ok,ReturnList2},LD};
-
-handle_call({get_node_status,Node},_Node,LD) ->
- case get_state_nodes(Node,LD#ld.nodes) of
- false ->
- {reply,{error,unknown_node},LD};
- StateStatus ->
- {reply,{ok,StateStatus},LD}
- end;
-
-handle_call(get_session_data,_From,LD=#ld{session_state=SState,tracer_data=TD}) ->
- case get_latest_session_nr_tracer_data(TD) of
- undefined ->
- {reply,{error,no_session},LD};
- SessionNr ->
- TDGargs=get_latest_tdgargs_tracer_data(TD),
- case is_tracing(SState) of
- true ->
- {reply,{ok,{tracing,SessionNr,TDGargs}},LD};
- false ->
- {reply,{ok,{not_tracing,SessionNr,TDGargs}},LD}
- end
- end;
-
-handle_call(flush,_From,LD=#ld{c_node=CNode,nodes=NodesD}) ->
- Nodes=get_tracing_nodes(NodesD),
- {reply,h_flush(CNode,Nodes),LD};
-handle_call({flush,Nodes},_From,LD=#ld{c_node=CNode}) ->
- {reply,h_flush(CNode,Nodes),LD};
-
-handle_call(get_loopdata,_From,LD) ->
- {reply,LD,LD};
-
-%% Internal handle_call callbacks.
-
-handle_call({reactivator_reply,{Counter,RPid}},_From,LD=#ld{chl=CHL}) ->
- HighestUsedCounter=get_highest_used_counter_chl(CHL),
- if
- HighestUsedCounter>Counter -> % There are now more log entries.
- NewUnsortedLog=get_loglist_chl(CHL),
- {reply,{more,NewUnsortedLog},LD};
- true -> % No Counter is youngest log entry.
- NodesD=LD#ld.nodes,
- Node=get_node_reactivators(RPid,LD#ld.reactivators),
- {reply,
- done,
- LD#ld{nodes=set_running_nodes(Node,NodesD),
- reactivators=del_reactivators(RPid,LD#ld.reactivators)}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Handling a notification from a trace case execution process. Receiving this
-%% indicated that this phase of the trace case is finnished.
-handle_cast({tc_executer_reply,{Phase,ProcH,Result}},LD) ->
- case Phase of
- activating -> % The trace case is running now.
- {ok,NewLD}=h_tc_activation_done(ProcH,Result,LD),
- {noreply,NewLD};
- stopping ->
- {ok,NewLD}=h_tc_stopping_done(ProcH,Result,LD),
- {noreply,NewLD};
- _ ->
- {noreply,LD}
- end;
-handle_cast(_,LD) ->
- {noreply,LD}.
-%% -----------------------------------------------------------------------------
-
-%% This is the case when a runtime component goes down. We stop all running
-%% reactivators for this node. Note that there can also be tracecases ongoing
-%% where this node is part of the Nodes variable. But there is not much we can
-%% do about that. Other then informing the user that it is unwise to reconnect
-%% this node before those tracecases have stopped being ongoing.
-handle_info({inviso_event,_CNode,_Time,{disconnected,Node,_}},LD) ->
- {noreply,LD#ld{nodes=set_down_nodes(Node,LD#ld.nodes),
- reactivators=stop_node_reactivators(Node,LD#ld.reactivators)}};
-
-%% This is the case when a runtime component gets suspended. Much of the same
-%% problem as described above applies.
-handle_info({inviso_event,_CNode,_Time,{state_change,Node,{_,{suspended,_}}}},LD) ->
- {noreply,LD#ld{nodes=set_suspended_nodes(Node,LD#ld.nodes),
- reactivators=stop_node_reactivators(Node,LD#ld.reactivators)}};
-
-handle_info(_,LD) ->
- {noreply,LD}.
-%% -----------------------------------------------------------------------------
-
-%% Called when the tool server stops. First clause, termination is initiated by
-%% our self and therefore controlled another way. In the second case we are
-%% stopping for some external reason, and we must then do more here in terminate/2.
-terminate(normal,#ld{c_node=CNode}) -> % This is when we are stopping our self.
- stop_inviso_at_c_node(CNode);
-terminate(_,#ld{c_node=CNode,nodes=NodesD,keep_nodes=KeepNodes}) ->
- remove_all_trace_patterns(CNode,KeepNodes,get_all_nodenames_nodes(NodesD)),
- stop_inviso_at_c_node(CNode).
-%% -----------------------------------------------------------------------------
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% =============================================================================
-%% Handler first level help functions.
-%% =============================================================================
-
-%% -----------------------------------------------------------------------------
-%% reconnect_nodes
-%% -----------------------------------------------------------------------------
-
-%% Help function reconnecting the nodes in Nodes. Listed nodes must be part of
-%% the set of nodes handled by the tool. It is not possible to reconnect a node
-%% that is not marked as down. This partly because we otherwise risk losing the
-%% trace_failure state (which can not be rediscovered).
-h_reconnect_nodes(local_runtime,LD=#ld{nodes=NodesD}) -> % Non-distributed.
- case get_state_nodes(local_runtime,NodesD) of
- down ->
- {ok,{local_runtime,[],start_runtime_components(local_runtime,LD)}};
- _ -> % Allready connected!
- {ok,{[],{error,already_connected},LD}}
- end;
-h_reconnect_nodes(Nodes,LD=#ld{nodes=NodesD}) when is_list(Nodes) ->
- {Nodes2,NodesErr}=
- lists:foldl(fun(N,{Nodes2,NodesErr})->
- case get_state_nodes(N,NodesD) of
- down -> % Yes this node can be reconnected.
- {[N|Nodes2],NodesErr};
- false -> % Not part of the node-set!
- {Nodes2,[{N,{error,unknown_node}}|NodesErr]};
- _ -> % Allready connected!
- {Nodes2,[{N,{error,already_connected}}|NodesErr]}
- end
- end,
- {[],[]},
- Nodes),
- LD2=start_runtime_components(Nodes2,LD), % Inpect the #ld.nodes for result.
- {ok,{Nodes2,NodesErr,LD2}};
-h_reconnect_nodes(Nodes,_LD) ->
- {error,{badarg,Nodes}}.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% start_session
-%% -----------------------------------------------------------------------------
-
-%% Help function starting the tracing at all nodes. Note that the tracer data
-%% is calculated using a user defined function. This is how for instance the
-%% file names (of the log files) are determined.
-%% Before the nodes are initiated their (possibly remaining) trace patterns are
-%% cleared, both local and global.
-h_start_session(M,F,TDGargs,LD=#ld{c_node=CNode,nodes=NodesD,tracer_data=TDs}) ->
- case get_inactive_running_nodes(NodesD) of
- [] -> % There are no nodes to initiate!
- h_start_session_nonodes(TDGargs,LD,[]);
- Nodes -> % List of nodes or 'local_runtime'.
- case h_start_session_ctp_all(CNode,Nodes) of
- {ok,Errors,[]} -> % Now no nodes to initiate!
- h_start_session_nonodes(TDGargs,LD,Errors);
- {ok,Errors,Nodes2} -> % Now these nodes are fresh.
- case call_tracer_data_generator(CNode,M,F,TDGargs,Nodes2) of
- {ok,TracerList} -> % Generated our tracerdata.
- case h_start_session_2(CNode,TracerList,Errors) of
- {ok,ReturnValue} -> % Some nodes are initialized now.
- {NewNodesD,Nodes3}=
- set_tracing_running_nodes(CNode,ReturnValue,NodesD),
- {SessionNr,NewTDs}=insert_td_tracer_data(TDGargs,TDs),
- {ok,{SessionNr,
- ReturnValue,
- Nodes3, % The nodes that shall get initial tracases.
- LD#ld{nodes=NewNodesD,tracer_data=NewTDs}}};
- {error,Reason} ->
- {error,Reason}
- end;
- {error,Reason} -> % Faulty tracer data generator func.
- {error,{bad_tdg,Reason}}
- end;
- {error,Reason} -> % Error clearing patterns.
- {error,Reason}
- end
- end.
-
-h_start_session_nonodes(TDGargs,LD=#ld{c_node=CNode,tracer_data=TDs},Errors) ->
- {SessionNr,NewTDs}=insert_td_tracer_data(TDGargs,TDs),
- if
- CNode==undefined ->
- {ok,{SessionNr,[],LD#ld{tracer_data=NewTDs}}};
- true ->
- {ok,{SessionNr,{ok,Errors},LD#ld{tracer_data=NewTDs}}}
- end.
-
-%% Help function clearing all trace patterns on all nodes.
-h_start_session_ctp_all(CNode,Nodes) ->
- case remove_all_trace_patterns(CNode,[],Nodes) of
- ok -> % Non-distributed case1.
- {ok,[],local_runtime};
- {error,Reason} -> % Non-distributed case2 and general failure.
- {error,Reason};
- {ok,NodeResults} ->
- h_start_session_ctp_all_2(NodeResults,[],[])
- end.
-
-h_start_session_ctp_all_2([{Node,{error,Reason}}|Rest],Errors,Nodes) ->
- h_start_session_ctp_all_2(Rest,[{Node,{error,Reason}}|Errors],Nodes);
-h_start_session_ctp_all_2([{Node,_OkOrPatternsUntouched}|Rest],Errors,Nodes) ->
- h_start_session_ctp_all_2(Rest,Errors,[Node|Nodes]);
-h_start_session_ctp_all_2([],Errors,Nodes) ->
- {ok,Errors,Nodes}.
-
-%% Help function doing the actual init_tracing.
-h_start_session_2(undefined,TracerData,_Errors) -> % Non distributed case.
- case inviso:init_tracing(TracerData) of
- {ok,LogResult} when is_list(LogResult) ->
- {ok,{ok,LogResult}};
- {error,already_initated} -> % Perhaps adopted!?
- {ok,{error,already_initiated}}; % Not necessarily wrong.
- {error,Reason} ->
- {error,Reason}
- end;
-h_start_session_2(CNode,TracerList,Errors) ->
- case rpc:call(CNode,inviso,init_tracing,[TracerList]) of
- {ok,NodeResults} ->
- {ok,{ok,Errors++NodeResults}};
- {error,Reason} ->
- {error,Reason};
- {badrpc,Reason} ->
- {error,{inviso_control_node_error,Reason}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function starting all initial trace cases. They are actually handled
-%% the same way as user started trace cases. We actually only start initial
-%% tracecases at Nodes (if Nodes is a list of nodes). This because we may have
-%% adopted some nodes some already tracing nodes, and such are supposed to have
-%% the correct patterns and flags set.
-do_initial_tcs([{TC,Vars}|Rest],Nodes,LD) ->
- Id=make_ref(), % Trace case ID.
- case h_atc(TC,Id,Vars,LD,Nodes) of % Start using regular start methods.
- {ok,NewLD} -> % Trace case was successfully started.
- NewInitialTcs=add_initial_tcs(TC,Id,NewLD#ld.started_initial_tcs),
- do_initial_tcs(Rest,Nodes,NewLD#ld{started_initial_tcs=NewInitialTcs});
- {error,_Reason} ->
- do_initial_tcs(Rest,Nodes,LD)
- end;
-do_initial_tcs([_|Rest],Nodes,LD) ->
- do_initial_tcs(Rest,Nodes,LD);
-do_initial_tcs([],_Nodes,LD) ->
- LD.
-%% -----------------------------------------------------------------------------
-
-%% This help functio is used instead of do_initial_tcs/3 if there actually are no
-%% nodes to do the trace cases on. The reason we must have this function is that
-%% the tracecases must still be entered into the history with bindings and all.
-%% But we let them be marked as 'running' immediately (no need for the activator
-%% process).
-add_initial_tcs_to_history([{TC,Vars}|Rest],LD=#ld{tc_dict=TCdict,chl=CHL}) ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} ->
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} ->
- Id=make_ref(), % Trace case ID.
- FakeProcH=make_ref(), % Need something to enter as activator.
- NewCHL=set_activating_chl(TC,Id,CHL,Bindings,FakeProcH),
- NewCHL2=set_running_chl(FakeProcH,TC,Id,void,NewCHL), % Result=void.
- NewInitialTcs=add_initial_tcs(TC,Id,LD#ld.started_initial_tcs),
- add_initial_tcs_to_history(Rest,LD#ld{chl=NewCHL2,
- started_initial_tcs=NewInitialTcs});
- {error,_Reason} -> % Not much we can do about that.
- add_initial_tcs_to_history(Rest,LD)
- end;
- false ->
- add_initial_tcs_to_history(Rest,LD)
- end;
-add_initial_tcs_to_history([],LD) ->
- LD.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% reinitiate_session
-%% -----------------------------------------------------------------------------
-
-%% Function doing the reinitiation. That means first do init_tracing at the nodes
-%% in question. Then redo the command history to bring them up to speed.
-%% But first the runtime component is cleared of all trace patterns.
-h_reinitiate_session(Nodes,M,F,TDGargs,LD=#ld{c_node=CNode,nodes=NodesD}) ->
- case h_reinitiate_session_2(Nodes,NodesD,CNode) of
- {ok,{[],NodesErr}} -> % No nodes to reinitiate.
- {ok,{NodesErr,{ok,[]},LD}};
- {ok,{Nodes2,NodesErr}} -> % List of nodes or local_runtime.
- case call_tracer_data_generator(CNode,M,F,TDGargs,Nodes2) of
- {ok,TracerList} ->
- case h_start_session_2(CNode,TracerList,[]) of % Borrow from start_session.
- {ok,ReturnValue} -> % Ok, now we must redo cmd history.
- {NewNodesD,_Nodes}=
- set_tracing_running_nodes(CNode,ReturnValue,NodesD),
- NewLD=h_reinitiate_session_chl(Nodes2,LD#ld{nodes=NewNodesD}),
- {ok,{NodesErr,ReturnValue,NewLD}};
- {error,Reason} ->
- {error,Reason}
- end;
- {error,Reason} ->
- {error,{bad_tdg,Reason}}
- end;
- {error,Reason} ->
- {error,Reason}
- end.
-
-%% Help function finding out which nodes in Nodes actually can be reinitiated.
-%% A node must be up, inactive and not suspended in order for this to work. All the
-%% rest is just a matter of how detailed error return values we want to generate.
-h_reinitiate_session_2(local_runtime,NodesD,undefined) -> % Non distributed case.
- case get_state_nodes(local_runtime,NodesD) of
- {inactive,running} -> % Only ok case.
- case inviso:ctp_all() of
- ok ->
- {ok,{local_runtime,[]}};
- {error,Reason} -> % This is strange.
- {error,Reason}
- end;
- {_,suspended} ->
- {ok,{[],{error,suspended}}};
- down ->
- {ok,{[],{error,down}}};
- _ ->
- {ok,{[],{error,already_in_session}}}
- end;
-h_reinitiate_session_2(Nodes,NodesD,CNode) when is_list(Nodes) ->
- {ok,lists:foldl(fun(N,{Nodes2,NodesErr})->
- case get_state_nodes(N,NodesD) of
- {inactive,running} -> % Only ok case.
- case rpc:call(CNode,inviso,ctp_all,[[N]]) of
- {ok,[{N,ok}]} ->
- {[N|Nodes2],NodesErr};
- {ok,[{N,{error,Reason}}]} ->
- {Nodes2,[{N,{error,Reason}}|NodesErr]};
- {error,Reason} ->
- {Nodes2,[{N,{error,Reason}}|NodesErr]};
- {badrpc,Reason} ->
- {Nodes2,[{N,{error,{badrpc,Reason}}}|NodesErr]}
- end;
- {_,suspended} ->
- {Nodes2,[{N,{error,suspended}}|NodesErr]};
- down ->
- {Nodes2,[{N,{error,down}}|NodesErr]};
- false ->
- {Nodes2,[{N,{error,unknown_node}}|NodesErr]};
- _ ->
- {Nodes2,[{N,{error,already_in_session}}|NodesErr]}
- end
- end,
- {[],[]},
- Nodes)};
-h_reinitiate_session_2(Nodes,_NodesD,_CNode) ->
- {error,{badarg7,Nodes}}.
-
-%% Help function redoing the command history log at all nodes that actually
-%% started to trace. Note that we do not modify the return value which will be
-%% given to the caller just because we decide not to redo commands. The user
-%% must conclude him self from the inviso return value that commands were not
-%% redone at a particular node.
-h_reinitiate_session_chl(local_runtime,LD) ->
- h_reinitiate_session_chl([local_runtime],LD);
-h_reinitiate_session_chl([Node|Rest],LD=#ld{nodes=NodesD}) ->
- case get_state_nodes(Node,NodesD) of
- {tracing,running} -> % Only case when we shall redo!
- h_reinitiate_session_chl(Rest,redo_cmd_history(Node,LD));
- _ -> % No redo of chl in other cases.
- h_reinitiate_session_chl(Rest,LD)
- end;
-h_reinitiate_session_chl([],LD) ->
- LD.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% restore_session
-%% -----------------------------------------------------------------------------
-
-%% Help function starting a session (init tracing) and redoes the history
-%% found in CHL.
-h_restore_session(MoreTDGargs,LD) ->
- DateTime=calendar:universal_time(),
- {M,F,Args}=LD#ld.tdg,
- TDGargs=inviso_tool_lib:mk_tdg_args(DateTime,MoreTDGargs++Args),
- case h_start_session(M,F,TDGargs,LD) of
- {ok,{SessionNr,ReturnVal,NewLD}} -> % There were no available nodes.
- {ok,{SessionNr,ReturnVal,NewLD}};
- {ok,{SessionNr,ReturnVal,Nodes2,NewLD}} ->
- NewLD2=h_reinitiate_session_chl(Nodes2,NewLD),
- {ok,{SessionNr,ReturnVal,NewLD2}};
- {error,Reason} -> % Risk of out of control.
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% stop_session
-%% -----------------------------------------------------------------------------
-
-%% Help function stopping tracing at tracing nodes.
-h_stop_session(#ld{c_node=CNode,nodes=NodesD,tracer_data=TDs}) ->
- case h_stop_session_2(CNode,NodesD) of
- {ok,Result} ->
- {ok,{get_latest_session_nr_tracer_data(TDs),Result}};
- {error,Reason} ->
- {error,Reason}
- end.
-
-h_stop_session_2(undefined,NodesD) -> % The non distributed case.
- case get_tracing_nodes(NodesD) of
- {up,{inactive,_}} -> % Already not tracing!
- {ok,[]};
- {up,_} ->
- case inviso:stop_tracing() of
- {ok,_State} ->
- {ok,[ok]};
- {error,no_response} ->
- {ok,[]};
- {error,Reason} ->
- {error,Reason}
- end;
- down ->
- {ok,[]}
- end;
-h_stop_session_2(CNode,NodesD) ->
- Nodes=get_tracing_nodes(NodesD),
- case rpc:call(CNode,inviso,stop_tracing,[Nodes]) of
- {ok,NodeResults} ->
- {ok,lists:map(fun({N,{ok,_}})->{N,ok};
- (NodeError)->NodeError
- end,
- NodeResults)};
- {error,Reason} ->
- {error,Reason};
- {badrpc,Reason} ->
- {error,{inviso_control_node_error,Reason}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function removing any trace flags, trace patterns and meta trace patterns
-%% at Nodes. This will cause the nodes to become "fresh".
-h_reset_nodes(local_runtime,_CNode) ->
- inviso:clear([keep_log_files]);
-h_reset_nodes(Nodes,CNode) ->
- case inviso_tool_lib:inviso_cmd(CNode,clear,[Nodes,[keep_log_files]]) of
- {ok,NodeResults} ->
- {ok,NodeResults};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% atc
-%% -----------------------------------------------------------------------------
-
-%% Function handling ativating a trace case. Trace cases that do not have a
-%% particular on/off handling (but just on in some scense) are handled here too.
-%% The trace case is entered into the Command History Log.
-%% Note that the trace case can not be executed at this node but must be
-%% executed where the inviso control component is.
-%% Further it is possible to either activated the tracecase for all running and
-%% tracing nodes, or just for a specified list of nodes.
-%% TC=tracecase_name(),
-%% Id=term(), identifiying this usage so we can turn it off later.
-%% Vars=list(), list of variable-value bindnings.
-h_atc(TC,Id,Vars,LD) ->
- h_atc(TC,Id,Vars,LD,void). % For all running-tracing nodes.
-
-h_atc(TC,Id,Vars,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL},Nodes) ->
- case find_id_chl(TC,Id,CHL) of
- activating -> % Already started.
- {error,activating};
- stopping -> % Not yet stopped.
- {error,deactivating};
- false ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} -> % Such a trace case exists.
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} -> % Necessary vars exists in Vars.
- if
- is_list(Nodes) -> % Nodes predefined.
- h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes);
- true -> % Use all tracing and running nodes.
- Nodes1=get_nodenames_running_nodes(LD#ld.nodes),
- h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes1)
- end;
- {error,Reason} -> % Variable def missing.
- {error,Reason}
- end;
- false ->
- {error,unknown_tracecase}
- end;
- {ok,_Bindings} -> % Already activated and running.
- {error,already_started}
- end.
-
-h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes) ->
- {ok,ProcH} = exec_trace_case_on(CNode,TraceCase,Bindings,Nodes),
- %% Trace cases have no return values.
- NewCHL=set_activating_chl(TC,Id,CHL,Bindings,ProcH),
- {ok,LD#ld{chl=NewCHL}}.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% sync_atc
-%% -----------------------------------------------------------------------------
-
-h_sync_atc(TC,Id,Vars,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case find_id_chl(TC,Id,CHL) of
- activating -> % Already started.
- {error,activating};
- stopping -> % Not yet stopped.
- {error,deactivating};
- false ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} -> % Such a trace case exists.
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} -> % Necessary vars exists in Vars.
- {ok,TcFName}=get_tc_activate_fname(TraceCase),
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
- RpcNode=get_rpc_nodename(CNode),
- case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
- {ok,Value} ->
- FakeProcH=make_ref(),
- NewCHL1=set_activating_chl(TC,Id,CHL,Bindings,FakeProcH),
- NewCHL2=set_running_chl(FakeProcH,TC,Id,Value,NewCHL1),
- {ok,LD#ld{chl=NewCHL2},Value};
- {error,Reason} ->
- {error,{faulty_tracecase,{TcFName,Reason}}};
- {badrpc,Reason} ->
- {error,{badrpc,Reason}}
- end;
- {error,Reason} -> % Variable def missing.
- {error,Reason}
- end;
- false ->
- {error,unknown_tracecase}
- end;
- {ok,_Bindings} -> % Already activated and running.
- {error,already_started}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% rtc
-%% -----------------------------------------------------------------------------
-
-%% Function handling running a trace case without marking it as activated. It
-%% is in the history mearly indicated as activated
-h_sync_rtc(TC,Vars,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} -> % Such a trace case exists.
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} -> % Necessary vars exists in Vars.
- {ok,TcFName}=get_tc_activate_fname(TraceCase),
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
- RpcNode=get_rpc_nodename(CNode),
- case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
- {ok,Value} ->
- {ok,LD#ld{chl=add_rtc_chl(TC,Bindings2,CHL)},Value};
- {error,Reason} ->
- {error,{faulty_tracecase,{TcFName,Reason}}};
- {badrpc,Reason} ->
- {error,{badrpc,Reason}}
- end;
- {error,Reason} -> % Variable def missing.
- {error,Reason}
- end;
- false ->
- {error,unknown_tracecase}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% dtc
-%% -----------------------------------------------------------------------------
-
-%% Function handling turning a trace case off. The trace case must be registered
-%% as having an off mechanism. If it has an off mechanism and was previously entered
-%% into the Command History Log and is done with its activation phase, it will be
-%% executed and removed from the CHL.
-h_dtc(TC,Id,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case find_id_chl(TC,Id,CHL) of
- {ok,Bindings} -> % Yes, we have turned it on before.
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} ->
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- case exec_trace_case_off(CNode,TraceCase,Bindings,Nodes) of
- {ok,ProcH} ->
- NewCHL=set_stopping_chl(TC,Id,CHL,ProcH),
- {ok,LD#ld{chl=NewCHL}};
- {error,Reason} ->
- {error,Reason}
- end;
- false -> % Strange, Id ok but no such trace case.
- {error,unknown_tracecase}
- end;
- false -> % Not previously turned on.
- {error,unknown_id};
- activating ->
- {error,activating};
- stopping ->
- {error,already_deactivating}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% sync_dtc
-%% -----------------------------------------------------------------------------
-
-h_sync_dtc(TC,Id,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case find_id_chl(TC,Id,CHL) of
- {ok,Bindings} -> % Yes, we have turned it on before.
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} ->
- case get_tc_deactivate_fname(TraceCase) of
- {ok,TcFName} ->
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
- RpcNode=get_rpc_nodename(CNode),
- case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
- {ok,Value} ->
- FakeProcH=make_ref(),
- NewCHL1=set_stopping_chl(TC,Id,CHL,FakeProcH),
- NewCHL2=nullify_chl(FakeProcH,TC,Id,NewCHL1),
- {ok,LD#ld{chl=NewCHL2},Value};
- {error,Reason} -> % Script fault.
- {error,{faulty_tracecase,{TcFName,Reason}}};
- {badrpc,Reason} ->
- {error,{badrpc,Reason}}
- end;
- false ->
- {error,no_deactivation}
- end;
- false -> % Strange, Id ok but no such trace case.
- {error,unknown_tracecase}
- end;
- false -> % Not previously turned on.
- {error,unknown_id};
- activating ->
- {error,activating};
- stopping ->
- {error,already_deactivating}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% inviso
-%% -----------------------------------------------------------------------------
-
-%% Function executing one inviso command. The returnvalue from the inviso
-%% function call will be the return value to the client. The command is
-%% entered into the history command log.
-%% Note that the inviso call may have to be done at another node, dictated
-%% by the c_node field. Further, if the module name is not an atom it is
-%% most likely a regexp, which must be expanded at the regexp_node. Note
-%% this is only relevant for tp and tpl.
-h_inviso(Cmd,Args,LD=#ld{c_node=CNode,regexp_node=RegExpNode,chl=CHL}) ->
- Arity=length(Args),
- case check_proper_inviso_call(Cmd,Arity) of
- {true,RegExpFlag} -> % Yes it is an inviso call.
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- case h_inviso_2(Cmd,Args,CNode,RegExpNode,RegExpFlag,Nodes) of
- {ok,Result} ->
- case check_inviso_call_to_history(Cmd,Arity) of
- true -> % This function shall be added to chl.
- {ok,{Result,LD#ld{chl=add_inviso_call_chl(Cmd,Args,CHL)}}};
- false -> % Do not add it.
- {ok,{Result,LD}}
- end;
- {error,Reason} ->
- {error,Reason}
- end;
- false -> % Not an inviso function.
- {error,invalid_function_name}
- end.
-
-h_inviso_2(Cmd,Args,undefined,_,_,_) -> % A non distributed system.
- case catch apply(inviso,Cmd,Args) of % Regexp expansion only relevant when
- {'EXIT',Reason} -> % distributed, here let inviso_rt expand.
- {error,{'EXIT',Reason}};
- Result ->
- {ok,Result}
- end;
-h_inviso_2(Cmd,Args,CNode,RegExpNode,RegExpFlag,Nodes) ->
- case expand_module_regexps(Args,RegExpNode,Nodes,RegExpFlag) of
- {ok,NewArgs} ->
- case catch inviso_tool_lib:inviso_cmd(CNode,Cmd,[Nodes|NewArgs]) of
- {'EXIT',Reason} ->
- {error,{'EXIT',Reason}};
- {error,{badrpc,Reason}} -> % Includes runtime failure.
- {error,{badrpc,Reason}};
- Result ->
- {ok,Result}
- end;
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% reactivate
-%% -----------------------------------------------------------------------------
-
-h_reactivate(_Node,undefined) -> % The non-distributed case.
- case inviso:cancel_suspension() of
- ok ->
- ok;
- {error,Reason} ->
- {error,Reason}
- end;
-h_reactivate(Node,CNode) ->
- case inviso_tool_lib:inviso_cmd(CNode,cancel_suspension,[[Node]]) of
- {ok,[{Node,ok}]} ->
- ok;
- {ok,[{Node,{error,Reason}}]} ->
- {error,Reason};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% save_history
-%% -----------------------------------------------------------------------------
-
-h_save_history(HDir,Dir,FileName,SortedLog) ->
- Dir0=
- if
- is_list(HDir) -> % There is a history dir specified.
- HDir; % Use it then.
- true ->
- Dir % Else use the tool dir.
- end,
- case catch make_absolute_path(FileName,Dir0) of
- AbsFileName when is_list(AbsFileName) ->
- Log2=build_saved_history_data(SortedLog), % Remove stopped tracecases.
- case file:write_file(AbsFileName,term_to_binary(Log2)) of
- ok ->
- {ok,AbsFileName};
- {error,Reason} ->
- {error,{write_file,Reason}}
- end;
- {'EXIT',_Reason} ->
- {error,{bad_filename,FileName}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% get_autostart_data
-%% -----------------------------------------------------------------------------
-
-%% Help function building the structures used when exporting autostart information
-%% from the tool. Note that we remove the tool-dependency and insert the one
-%% specify in the get_autostart_data call.
-h_get_autostart_data(local_runtime,_,Dependency,ASD,M,F,TDGargs,OptsG) ->
- CompleteTDGargs=call_tracer_data_generator_mkargs(local_runtime,TDGargs),
- Opts0=start_runtime_components_mk_opts(local_runtime,OptsG),
- Opts=[Dependency|lists:keydelete(dependency,1,Opts0)],
- {ok,{ASD,{ok,{Opts,{tdg,{M,F,CompleteTDGargs}}}}}};
-
-h_get_autostart_data(Nodes,CNode,Dependency,ASD,M,F,TDGargs,OptsG) when is_list(Nodes) ->
- {ok,{ASD,h_get_autostart_data_2(Nodes,CNode,Dependency,M,F,TDGargs,OptsG)}};
-h_get_autostart_data(Nodes,_CNode,_Dependency,_ASD,_M,_F,_TDGargs,_OptsG) ->
- {error,{badarg,Nodes}}.
-
-h_get_autostart_data_2([Node|Rest],CNode,Dependency,M,F,TDGargs,OptsG) ->
- CompleteTDGargs=call_tracer_data_generator_mkargs(Node,TDGargs),
- Opts0=start_runtime_components_mk_opts(Node,OptsG),
- Opts=[Dependency|lists:keydelete(dependency,1,Opts0)],
- [{Node,{ok,{Opts,{tdg,{M,F,CompleteTDGargs}}}}}|
- h_get_autostart_data_2(Rest,CNode,Dependency,M,F,TDGargs,OptsG)];
-h_get_autostart_data_2([],_CNode,_Dependency,_M,_F,_TDGargs,_OptsG) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% flush
-%% -----------------------------------------------------------------------------
-
-h_flush(undefined,_Nodes) ->
- inviso:flush();
-h_flush(CNode,Nodes) ->
- inviso_tool_lib:inviso_cmd(CNode,flush,[Nodes]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% tc_executer_reply
-%% -----------------------------------------------------------------------------
-
-%% Function handling that a trace case has completed its activation phase and
-%% shall now be marked in the Command History Log as running.
-h_tc_activation_done(ProcH,Result,LD=#ld{chl=CHL}) ->
- case find_tc_executer_chl(ProcH,CHL) of
- {activating,{TC,Id}} ->
- case Result of
- {ok,Value} -> % The trace case is successful activated.
- {ok,LD#ld{chl=set_running_chl(ProcH,TC,Id,Value,CHL)}};
- {error,_} -> % Then pretend it never happend :-)
- {ok,LD#ld{chl=del_tc_chl(ProcH,TC,Id,CHL)}} % Remove it.
- end;
- _ -> % Where did this come from?
- {ok,LD} % Well just ignore it then.
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function handling that a trace case has completed its stopping phase and
-%% shall now be nulled in the Command History Log (meaning that it will not
-%% be repeated in the event of a reactivation).
-h_tc_stopping_done(ProcH,Result,LD=#ld{chl=CHL}) ->
- case find_tc_executer_chl(ProcH,CHL) of
- {stopping,{TC,Id}} ->
- case Result of
- {ok,_Result} -> % _Result is returned from the tracecase.
- {ok,LD#ld{chl=nullify_chl(ProcH,TC,Id,CHL)}};
- {error,_} -> % This is difficult, is it still active?
- {ok,LD#ld{chl=nullify_chl(ProcH,TC,Id,CHL)}}
- end;
- _ -> % Strange.
- {ok,LD}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Terminate.
-%% -----------------------------------------------------------------------------
-
-%% Help function stopping the inviso control component. Does not return
-%% anything significant.
-stop_inviso_at_c_node(undefined) -> % Non distributed case.
- inviso:stop();
-stop_inviso_at_c_node(CNode) ->
- rpc:call(CNode,inviso,stop,[]).
-%% -----------------------------------------------------------------------------
-
-%% Help function that removes all trace patterns from the nodes that are not
-%% marked as such were patterns shall be left after stopping of inviso.
-%% Returns {ok,NodeResult} or {error,Reason}. In the non-distributed case
-%% 'ok' is returned incase of success, ot 'patterns_untouched'.
-remove_all_trace_patterns(undefined,KeepNodes,_Nodes) ->
- case KeepNodes of
- undefined -> % No, remove patterns from localruntime.
- inviso:ctp_all();
- _ ->
- patterns_untouched
- end;
-remove_all_trace_patterns(CNode,KeepNodes,Nodes) ->
- Nodes2=lists:filter(fun(N)->not(lists:member(N,KeepNodes)) end,Nodes),
- case inviso_tool_lib:inviso_cmd(CNode,ctp_all,[Nodes2]) of
- {ok,NodeResults} ->
- F=fun(N) ->
- case lists:member(N,KeepNodes) of
- true ->
- {N,patterns_untouched};
- false ->
- case lists:keysearch(N,1,NodeResults) of
- {value,Result} ->
- Result; % {Node,ok}
- false -> % Extremely strange.
- {N,{error,general_error}}
- end
- end
- end,
- {ok,lists:map(F,Nodes)};
- {error,{badrpc,Reason}} ->
- {error,{inviso_control_node_error,Reason}};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% =============================================================================
-%% Second level help functions.
-%% =============================================================================
-
-%% Help function building a reply to a reconnection call based on which nodes
-%% where asked to be reconnected and which of those are actually now working.
-%% We actually make an effort to serve the return value in the same order as the
-%% nodes were mentioned in the original call (Nodes).
-build_reconnect_nodes_reply(local_runtime,local_runtime,_NodesErr,NodesD) ->
- case get_state_nodes(local_runtime,NodesD) of
- down ->
- {error,down};
- {State,Status} ->
- {ok,{State,Status}}
- end;
-build_reconnect_nodes_reply(local_runtime,_,NodesErr,_NodesD) ->
- NodesErr;
-build_reconnect_nodes_reply([Node|Rest],Nodes2,NodesErr,NodesD) ->
- case lists:member(Node,Nodes2) of
- true -> % Ok, look in the #ld.nodes.
- case get_state_nodes(Node,NodesD) of
- down -> % Somekind of failure, still down.
- [{Node,{error,down}}|
- build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)];
- {State,Status} -> % {State,Status}
- [{Node,{ok,{State,Status}}}|
- build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)]
- end;
- false -> % Error already from the beginning.
- {value,{_,Error}}=lists:keysearch(Node,1,NodesErr),
- [{Node,Error}|build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)]
- end;
-build_reconnect_nodes_reply([],_,_,_) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Help function building a return value to reinitiate_session. Nodes contains
-%% all involved nodes. If the node occurrs in NodesErr, we choose the error in
-%% NodesErr. Otherwise the returnvalue in ReturnVal is used.
-build_reinitiate_session_reply(Nodes,NodesErr,{ok,NodesResults}) ->
- {ok,build_reinitiate_session_reply_2(Nodes,NodesErr,NodesResults)};
-build_reinitiate_session_reply(local_runtime,[],NodeResult) ->
- NodeResult;
-build_reinitiate_session_reply(local_runtime,NodesErr,_NodeResult) ->
- NodesErr.
-build_reinitiate_session_reply_2([Node|Rest],NodesErr,NodeResults) ->
- case lists:keysearch(Node,1,NodesErr) of
- {value,{_,Error}} ->
- [{Node,Error}|build_reinitiate_session_reply_2(Rest,NodesErr,NodeResults)];
- false ->
- case lists:keysearch(Node,1,NodeResults) of
- {value,Value} ->
- [Value|build_reinitiate_session_reply_2(Rest,NodesErr,NodeResults)]
- end
- end;
-build_reinitiate_session_reply_2([],_NodesErr,_NodeResults) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Help function returning a history log where stop and stopping entries have
-%% been removed. Further all tracecase log entries must be set to running since
-%% there can not be such a thing as an activating tracecase stored away in a
-%% saved historyfile!
-%% We must also take away any #Ref.
-build_saved_history_data(SortedLog) ->
- CleanedLog=
- lists:filter(fun({_,_,Stop,_}) when Stop==stop;Stop==stopping -> false;
- (_) -> true
- end,
- SortedLog),
- lists:map(fun({{TC,Id},C,activating,B}) -> {{TC,Id},C,running,B};
- ({{TC,Id},C,S,B}) -> {{TC,Id},C,S,B};
- ({{M,F,Args,_Ref},C}) -> {{M,F,Args},C};
- ({{TC,_Ref},C,B}) -> {TC,C,B} % An rtc.
- end,
- CleanedLog).
-%% -----------------------------------------------------------------------------
-
-%% This help function builds the AutoStartData structure which is returned from
-%% get_austostart_data. An AutoStartData structure is a list of trace-files and
-%% inviso commands. The order is significant since it is the idea that doing
-%% the trace case files and inviso commands in that order will bring a node to
-%% a certain state in a trace perspective.
-%% Returns {ok,AutoStartData} or {error,Reason}
-build_autostart_data(SortedLog,TCdict) ->
- build_autostart_data_2(SortedLog,TCdict,[]).
-
-build_autostart_data_2([{_,_C,Stop,_B}|Rest],TCdict,Accum) when Stop==stop;Stop==stopping->
- build_autostart_data_2(Rest,TCdict,Accum); % Simply skip deactivated/deativating.
-build_autostart_data_2([{{TCname,_},_C,activating,Bindings}|Rest],TCdict,Accum) ->
- build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
-build_autostart_data_2([{{TCname,_},_C,running,Bindings}|Rest],TCdict,Accum) ->
- build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
-build_autostart_data_2([{{TCname,_Ref},_C,Bindings}|Rest],TCdict,Accum) ->
- build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
-build_autostart_data_2([{{M,F,Args,_Ref},_C}|Rest],TCdict,Accum) ->
- build_autostart_data_2(Rest,TCdict,[{mfa,{M,F,Args}}|Accum]);
-build_autostart_data_2([],_TCdict,Accum) ->
- {ok,lists:reverse(Accum)}.
-
-%% Help function placing the filename in the AutoStartData structure.
-build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum) ->
- {ok,TC}=get_tracecase_tc_dict(TCname,TCdict),
- {ok,FName}=get_tc_activate_fname(TC),
- build_autostart_data_2(Rest,TCdict,[{file,{FName,Bindings}}|Accum]).
-%% -----------------------------------------------------------------------------
-
-%% Help function generating tracerdata to init inviso tracing. The generation
-%% is done by the TracerDataGenerator, TDG, function.
-%% Individual tracerdata is generated for each node in Nodes.
-%% Returns {ok,TracerData} or {error,Reason}.
-call_tracer_data_generator(undefined,M,F,TDGargs,_Nodes) -> % Non distributed.
- case catch call_tracer_data_generator_3(M,F,TDGargs,local_runtime) of
- {'EXIT',Reason} ->
- {error,{'EXIT',Reason}};
- TracerData ->
- {ok,TracerData}
- end;
-call_tracer_data_generator(_CNode,M,F,TDGargs,Nodes) ->
- case catch call_tracer_data_generator_2(M,F,TDGargs,Nodes) of
- {'EXIT',Reason} ->
- {error,{'EXIT',Reason}};
- TracerList ->
- {ok,TracerList}
- end.
-
-call_tracer_data_generator_2(M,F,TDGargs,[Node|Rest]) ->
- [{Node,call_tracer_data_generator_3(M,F,TDGargs,Node)}|
- call_tracer_data_generator_2(M,F,TDGargs,Rest)];
-call_tracer_data_generator_2(_,_,_,[]) ->
- [].
-
-call_tracer_data_generator_3(M,F,TDGargs,Node) ->
- apply(M,F,call_tracer_data_generator_mkargs(Node,TDGargs)).
-
-%% This function creates the arguments that the tracer data generator function
-%% accepts (in an apply call). The reason for making it a sepparate function is
-%% that the arguments are constructed in more situations than just when actually
-%% doing the apply. By having a function it will become obvious where to change
-%% should the arguments change.
-call_tracer_data_generator_mkargs(Node,TDGargs) ->
- inviso_tool_lib:mk_complete_tdg_args(Node,TDGargs).
-%% -----------------------------------------------------------------------------
-
-%% This function acts as standard options generator function. That is returning
-%% the options argument to inviso:add_node/3. Note that this function must not
-%% return the dependency part of that option.
-std_options_generator(_Node) ->
- []. % No particular options(!)
-%% -----------------------------------------------------------------------------
-
-
-%% Help function checking that Vars contains a binding for every variable
-%% listed in the VarNames field in TraceCase. Note that the special variable 'Nodes'
-%% is disregarded, since it is always added by the inviso_tool.
-%% Returns {ok,Bindings} or {error,Reason}. Where Bindings is a bindngs structure
-%% according to file:eval functionality.
-check_bindings(Vars,TraceCase) ->
- case catch check_bindings_2(Vars,
- get_tc_varnames(TraceCase),
- erl_eval:new_bindings()) of
- {'EXIT',_Reason} ->
- {error,variable_error};
- {error,Reason} -> % Missing a bindning.
- {error,Reason};
- {ok,Bindings} ->
- {ok,Bindings}
- end.
-
-check_bindings_2(Vars,['Nodes'|Rest],Bindings) ->
- check_bindings_2(Vars,Rest,Bindings); % Disregard Nodes since it is automatic.
-check_bindings_2(Vars,[VarName|Rest],Bindings) ->
- case lists:keysearch(VarName,1,Vars) of
- {value,{_,Val}} ->
- check_bindings_2(Vars,Rest,erl_eval:add_binding(VarName,Val,Bindings));
- false -> % Mandatory variable missing.
- {error,{missing_variable,VarName}} % Quite here then.
- end;
-check_bindings_2(_,[],Bindings) ->
- {ok,Bindings}.
-%% -----------------------------------------------------------------------------
-
-%% This help function checks that the command the user tries to do is amongst
-%% the inviso API. It at the same time returns what kind of command it is.
-%% {true,RegExpFlag} or 'false' where RegExpFlag indicates if this command
-%% needs to have its argument modified by module regexp expansion or not.
-check_proper_inviso_call(Cmd,Arity) ->
- case lists:member({Cmd,Arity},?INVISO_CMDS) of
- true -> % It is part of inviso API.
- {true,check_proper_inviso_call_regexp(Cmd,Arity)};
- false ->
- false
- end.
-
-%% Returns {Type,Arity,PlaceOfModuleSpec} or 'false'.
-check_proper_inviso_call_regexp(tp,5) -> {tp,5,1};
-check_proper_inviso_call_regexp(tp,4) -> {tp,4,1};
-check_proper_inviso_call_regexp(tp,1) -> {tp,1,1};
-check_proper_inviso_call_regexp(tpl,5) -> {tp,5,1};
-check_proper_inviso_call_regexp(tpl,4) -> {tp,4,1};
-check_proper_inviso_call_regexp(tpl,1) -> {tp,1,1};
-check_proper_inviso_call_regexp(ctp,3) -> {ctp,3,1};
-check_proper_inviso_call_regexp(ctp,1) -> {ctp,1,1};
-check_proper_inviso_call_regexp(ctpl,3) -> {ctp,3,1};
-check_proper_inviso_call_regexp(ctpl,1) -> {ctp,1,1};
-check_proper_inviso_call_regexp(_,_) -> % No regexp expansion.
- false.
-%% -----------------------------------------------------------------------------
-
-%% Help function checking if this inviso command shall be added to the command
-%% history log. Returns true or false.
-check_inviso_call_to_history(Cmd,Arity) ->
- case lists:member({Cmd,Arity},?INVISO_CMD_HISTORY) of
- true ->
- true;
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function traversing the arguments and expanding module names stated
-%% as regular expressions. This means that the resulting arguments may be longer
-%% than the orginal ones.
-%% When we run this function it has been determined that we are a distributed
-%% system.
-%% Also note that if there are no regexps in Args, no regexpansion will be
-%% made and RegExpNode may be 'undefined' (as it is if not set at start-up).
-%% If RegExpNode is unavailable the nodes found in Nodes will be used until
-%% one that works is found.
-expand_module_regexps(Args,_RegExpNode,_Nodes,false) ->
- {ok,Args};
-expand_module_regexps([PatternList],RegExpNode,Nodes,{tp,1,1}) ->
- case catch expand_module_regexps_tp(PatternList,RegExpNode,Nodes) of
- NewPatternList when is_list(NewPatternList) ->
- {ok,[NewPatternList]};
- {error,Reason} ->
- {error,Reason}
- end;
-expand_module_regexps([PatternList],RegExpNode,Nodes,{ctp,1,1}) ->
- case catch expand_module_regexps_ctp(PatternList,RegExpNode,Nodes) of
- NewPatternList when is_list(NewPatternList) ->
- {ok,[NewPatternList]};
- {error,Reason} ->
- {error,Reason}
- end;
-expand_module_regexps([M,F,Arity,MS,Opts],RegExpNode,Nodes,{tp,5,1}) ->
- expand_module_regexps([[{M,F,Arity,MS,Opts}]],RegExpNode,Nodes,{tp,1,1});
-expand_module_regexps([M,F,Arity,MS],RegExpNode,Nodes,{tp,4,1}) ->
- expand_module_regexps([[{M,F,Arity,MS,[]}]],RegExpNode,Nodes,{tp,1,1});
-expand_module_regexps([M,F,Arity],RegExpNode,Nodes,{ctp,3,1}) ->
- expand_module_regexps([[{M,F,Arity}]],RegExpNode,Nodes,{ctp,1,1}).
-
-
-expand_module_regexps_tp([E={M,_,_,_,_}|Rest],RegExpNode,Nodes) when is_atom(M) ->
- [E|expand_module_regexps_tp(Rest,RegExpNode,Nodes)];
-expand_module_regexps_tp([{M,F,Arity,MS,Opts}|Rest],RegExpNode,Nodes) when is_list(M);is_tuple(M) ->
- case inviso_tool_lib:expand_module_names([RegExpNode],
- M,
- [{expand_only_at,RegExpNode}]) of
- {singlenode_expansion,Modules} ->
- expand_module_regexps_tp_2(Modules,F,Arity,MS,Opts,Rest,RegExpNode,Nodes);
- {error,{faulty_node,RegExpNode}} -> % RegExpNode probably down.
- case Nodes of
- [NewRegExpNode|RestNodes] -> % Ok, just choose a node.
- expand_module_regexps_tp([{M,F,Arity,MS,Opts}|Rest],NewRegExpNode,RestNodes);
- [] -> % No more nodes to choose from.
- throw({error,no_available_regexpnode})
- end;
- {error,_Reason} ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes)
- end;
-expand_module_regexps_tp([_|Rest],RegExpNode,Nodes) ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes); % Skip faulty module specification.
-expand_module_regexps_tp([],_RegExpNodes,_Nodes) ->
- [].
-
-expand_module_regexps_tp_2([M|MRest],F,Arity,MS,Opts,Rest,RegExpNode,Nodes) ->
- [{M,F,Arity,MS,Opts}|
- expand_module_regexps_tp_2(MRest,F,Arity,MS,Opts,Rest,RegExpNode,Nodes)];
-expand_module_regexps_tp_2([],_,_,_,_,Rest,RegExpNode,Nodes) ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes).
-
-expand_module_regexps_ctp([E={M,_,_}|Rest],RegExpNode,Nodes) when is_atom(M) ->
- [E|expand_module_regexps_ctp(Rest,RegExpNode,Nodes)];
-expand_module_regexps_ctp([{M,F,Arity}|Rest],RegExpNode,Nodes) when is_list(M);is_tuple(M) ->
- case inviso_tool_lib:expand_module_names([RegExpNode],
- M,
- [{expand_only_at,RegExpNode}]) of
- {singlenode_expansion,Modules} ->
- expand_module_regexps_ctp_2(Modules,F,Arity,Rest,RegExpNode,Nodes);
- {error,_Reason} ->
- expand_module_regexps_ctp(Rest,RegExpNode,Nodes)
- end;
-expand_module_regexps_ctp([_|Rest],RegExpNode,Nodes) ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes); % Skip faulty module specification.
-expand_module_regexps_ctp([],_RegExpNodes,_Nodes) ->
- [].
-
-expand_module_regexps_ctp_2([M|MRest],F,Arity,Rest,RegExpNode,Nodes) ->
- [{M,F,Arity}|expand_module_regexps_ctp_2(MRest,F,Arity,Rest,RegExpNode,Nodes)];
-expand_module_regexps_ctp_2([],_,_,Rest,RegExpNode,Nodes) ->
- expand_module_regexps_ctp(Rest,RegExpNode,Nodes).
-%% -----------------------------------------------------------------------------
-
-
-
-%% Help function running the activation of a trace case. Note that this must
-%% be done at the inviso control component's Erlang node *and* that it must be
-%% done in its own process since there is no telling for how long a trace case
-%% may run.
-%% Returns {ok,ActivationHandler}.
-exec_trace_case_on(CNode,TraceCase,Bindings,Nodes) ->
- {ok,TcFName}=get_tc_activate_fname(TraceCase),
- {ok,exec_trace_case_2(CNode,
- TcFName,
- erl_eval:add_binding('Nodes',Nodes,Bindings),
- activating)}.
-
-%% Help function running the deactivation of a trace case.
-exec_trace_case_off(CNode,TraceCase,Bindings,Nodes) ->
- case get_tc_deactivate_fname(TraceCase) of
- {ok,TcFName} -> % There is a deactivation.
- {ok,exec_trace_case_2(CNode,
- TcFName,
- erl_eval:add_binding('Nodes',Nodes,Bindings),
- stopping)};
- false ->
- {error,no_deactivation}
- end.
-
-exec_trace_case_2(CNode,TcFName,Bindings,Phase) ->
- if
- CNode==undefined -> % The non distributed case.
- spawn_link(?MODULE,tc_executer,[TcFName,Bindings,Phase,self()]);
- true ->
- spawn_link(CNode,?MODULE,tc_executer,[TcFName,Bindings,Phase,self()])
- end.
-
-%% This function is run in its own process and is responsible for executing
-%% the trace case.
-tc_executer(TcFName,Bindings,Phase,Parent) ->
- case catch file:script(TcFName,Bindings) of
- {ok,Value} ->
- tc_executer_reply(Parent,{Phase,self(),{ok,Value}});
- {'EXIT',Reason} ->
- tc_executer_reply(Parent,{Phase,self(),{error,{'EXIT',Reason}}});
- Error ->
- tc_executer_reply(Parent,{Phase,self(),Error})
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function which starts a reactivator process redoing command history at
-%% Node. It also updates the loopdata to indicate that Node is now in state
-%% reactivating. It is a good idea to only handle one node per reactivator process.
-%% This because if the node terminates and comes back up, the reactivator must be
-%% stopped.
-redo_cmd_history(Node,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL,nodes=NodesD}) ->
- P=start_reactivator(Node,CNode,TCdict,CHL),
- LD#ld{nodes=set_reactivating_nodes(Node,NodesD),
- reactivators=add_reactivators(Node,P,LD#ld.reactivators)}.
-
-%% Help function starting a reactivator process replaying the command history log.
-%% Returns a pid of the reactivator process.
-start_reactivator(Node,CNode,TCdict,CHL) ->
- UnsortedLog=get_loglist_chl(CHL), % Must fetch here, later on wrong node.
- if
- CNode==undefined -> % The non-distributed case.
- spawn_link(?MODULE,
- reactivator_executer,
- [Node,TCdict,UnsortedLog,self(),0,[]]);
- true ->
- spawn_link(CNode,
- ?MODULE,
- reactivator_executer,
- [Node,TCdict,UnsortedLog,self(),0,[]])
- end.
-
-%% The strategy is to traverse the CHL ETS table in Counter order, redoing the
-%% commands one by one. We wait until one command is finished until we do the
-%% next. Commands marked as nullified are not performed. In fact when a command
-%% is nullified only the stop will be found in the CHL. Its activation will be
-%% removed.
-reactivator_executer(Node,TCdict,UnsortedLog,TPid,StartCounter,DoneCases) ->
- SortedLog=lists:keysort(2,UnsortedLog), % Sort on Counter, oldest first.
- Log=reactivator_skip_log_entries(SortedLog,StartCounter),
- case reactivator_executer_2(Node,TCdict,TPid,StartCounter,DoneCases,Log) of
- done ->
- true; % Simply terminate the reactivator then.
- {more,{NewStartCounter,NewDoneCases,NewUnsortedLog}} ->
- reactivator_executer(Node,TCdict,NewUnsortedLog,TPid,NewStartCounter,NewDoneCases)
- end.
-
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,Id},NextC,running,Bindings}|Rest]) ->
- reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,_Ref},NextC,Bindings}|Rest]) ->
- reactivator_executer_rtc(Node,TCdict,TPid,DoneCases,Rest,TCname,NextC,Bindings,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,Id},NextC,activating,Bindings}|Rest]) ->
- reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{M,F,Args,_Ref},NextC}|Rest]) ->
- reactivator_executer_cmd(Node,M,F,Args),
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{_TCname,_Id},NextC,stopping,_Bindings}|Rest]) ->
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,Id,_Ref},NextC,stop,Bindings}|Rest]) ->
- case lists:member({TCname,Id},DoneCases) of
- true -> % We have activated it, must stop then.
- case get_tracecase_tc_dict(TCname,TCdict) of
- {ok,{_,_,_,_,FNameOff}} ->
- reactivator_executer_tc(Node,Bindings,FNameOff),
- NewDoneCases=lists:delete({TCname,Id},DoneCases),
- reactivator_executer_2(Node,TCdict,TPid,NextC,NewDoneCases,Rest);
- {ok,_} -> % No stop-filename, strange!
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
- false -> % Even stranger, does not exist!?
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end;
- false -> % Never activated in the first place.
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end;
-%% Done all log entries found this lap. See if there are more entries by now.
-reactivator_executer_2(_Node,_TCdict,TPid,Counter,DoneCases,[]) ->
- case reactivator_reply(TPid,Counter) of % Ask the tool process for more entries.
- done -> % No more entries in the CHL.
- done;
- {more,NewUnsortedLog} -> % Repeat the procedure
- {more,{Counter+1,DoneCases,NewUnsortedLog}} % with log entries from Counter+1.
- end.
-
-%% This help function activates a tracecase.
-reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest) ->
- case get_tracecase_tc_dict(TCname,TCdict) of
- {ok,{_,_,_,FNameOn}} -> % A case with just on functionality.
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,[{TCname,Id}|DoneCases],Rest);
- {ok,{_,_,_,FNameOn,_}} ->
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,[{TCname,Id}|DoneCases],Rest);
- false -> % Strange, does not exist anylonger!?
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end.
-
-%% Help function executing a trace case in the reactivators context. Does not
-%% return anything significant.
-reactivator_executer_tc(Node,Bindings,FileName) ->
- catch file:eval(FileName,erl_eval:add_binding('Nodes',[Node],Bindings)).
-
-%% Help function handling trace case that are simply executed - rtc.
-reactivator_executer_rtc(Node,TCdict,TPid,DoneCases,Rest,TCname,NextC,Bindings,Rest) ->
- case get_tracecase_tc_dict(TCname,TCdict) of
- {ok,{_,_,_,FNameOn}} -> % A case with just on functionality.
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
- {ok,{_,_,_,FNameOn,_}} ->
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
- false -> % Strange, does not exist anylonger!?
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end.
-
-reactivator_executer_cmd(nonode@nohost,M,F,Args) ->
- catch apply(M,F,Args); % Non-distributed.
-reactivator_executer_cmd(Node,M,F,Args) ->
- catch apply(M,F,[[Node]|Args]).
-
-%% Help function returning a list of log entries missing the first entries
-%% having a counter less or equal to C1.
-reactivator_skip_log_entries([{_,C,_,_}|Rest],C1) when C<C1 ->
- reactivator_skip_log_entries(Rest,C1);
-reactivator_skip_log_entries([{_,C}|Rest],C1) when C<C1 ->
- reactivator_skip_log_entries(Rest,C1);
-reactivator_skip_log_entries(Log,_) ->
- Log.
-%% -----------------------------------------------------------------------------
-
-%% Help function returning the node name to use in an rpc call.
-get_rpc_nodename(undefined) ->
- node();
-get_rpc_nodename(CNode) ->
- CNode.
-%% -----------------------------------------------------------------------------
-
-mk_rt_tag() ->
- inviso_tool.
-%% -----------------------------------------------------------------------------
-
-is_string([C|Rest]) when C>=32, C=<255 ->
- is_string(Rest);
-is_string([]) ->
- true;
-is_string(_) ->
- false.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Functions for handling the configuration file.
-%% -----------------------------------------------------------------------------
-
-%% The inviso tool is configured via start arguments and/or a configuration file.
-%% Start arguments will override any definitions in a configuration file.
-%% The configuration file is pointed out by either a start argument or the
-%% inviso application parameter 'inviso_tool_config_file'.
-
-%% Help function building the internal configuration structure. Configurations
-%% in the start argument will override parameters found in a configuration file.
-fetch_configuration(Config) ->
- case fetch_config_filename(Config) of
- {ok,FName} -> % We are supposed to use a conf-file.
- case read_config_file(FName) of
- {ok,LD} -> % Managed to open a file.
- NewLD=read_config_list(LD,Config),
- {ok,NewLD};
- Error = {error,_Reason} -> % Problem finding/opening file.
- Error
- end;
- false -> % No filename specified.
- LD=read_config_list(#ld{},Config),
- {ok,LD}
- end.
-
-%% Help function determining the name of the file which shall be consulted as
-%% the main configuration file.
-%% Returns {ok,FileName} or 'false'. The latter if no name could be determined.
-fetch_config_filename(Config) ->
- case catch lists:keysearch(config_file,1,Config) of
- {value,{_,FName}} when is_list(FName) ->
- {ok,FName};
- _ -> % No filename in the start argument.
- fetch_config_filename_2()
- end.
-
-fetch_config_filename_2() ->
- case application:get_env(inviso_tool_config_file) of
- {ok,FName} when is_list(FName) ->
- {ok,FName};
- _ -> % Application parameter not specified.
- false % Means no config file will be used.
- end.
-
-%% Help function reading the configuration file. Returns a #conf or {error,Reason}.
-read_config_file(FName) ->
- case catch file:consult(FName) of
- {ok,Terms} ->
- {ok,read_config_list(#ld{},Terms)};
- {error,Reason} ->
- {error,{file_consult,Reason}};
- {'EXIT',Reason} ->
- {error,{failure,Reason}}
- end.
-
-%% Help function traversing the Terms list entering known tag-values into #ld.
-read_config_list(LD,Terms) ->
- LD#ld{
- nodes = case mk_nodes(proplists:get_value(nodes,Terms,LD#ld.nodes)) of
- {ok,Nodes} -> Nodes;
- _ -> LD#ld.nodes
- end,
- c_node = proplists:get_value(c_node,Terms,LD#ld.c_node), % atom8)
- regexp_node = proplists:get_value(regexp_node,Terms,LD#ld.regexp_node), % atom()
- tc_def_file = proplists:get_value(tc_def_file,Terms,LD#ld.tc_def_file),
- tdg = proplists:get_value(tdg,Terms,LD#ld.tdg),
- debug = proplists:get_value(debug,Terms,LD#ld.debug),
- initial_tcs = proplists:get_value(initial_tcs,Terms,LD#ld.initial_tcs),
- dir = proplists:get_value(dir,Terms,LD#ld.dir),
- optg = proplists:get_value(optg,Terms,LD#ld.optg)
- }.
-
-%% -----------------------------------------------------------------------------
-
-
-%% Help function which, if it exists, consults the trace definition file. The
-%% idea behind the trace definition file is to point out which trace cases there
-%% are, where to find them and how to turn them on and off.
-%% Trace case definitions are:
-%% {TCname,Type,VariableNameList,ActivatioFileName} |
-%% {TCname,Type,VariableNameList,ActivationFileName,DeactivationFileName}
-%% TCname=atom()
-%% Type=on | on_off
-%% VariableNameList=[atom(),...]
-%% ActivationFileName=DeactivationFileName=string()
-read_trace_case_definitions(LD) ->
- case LD#ld.tc_def_file of
- TCfileName when is_list(TCfileName) ->
- case catch file:consult(TCfileName) of
- {ok,Terms} ->
- Dir=LD#ld.dir, % The working directory of the tool.
- TCdict=read_trace_case_definitions_2(Terms,Dir,mk_tc_dict()),
- LD#ld{tc_dict=TCdict};
- _ ->
- LD
- end;
- _ ->
- LD
- end.
-
-read_trace_case_definitions_2([{TCname,on,VarNames,FName}|Rest],Dir,TCdict) ->
- FileName=make_absolute_path(FName,Dir),
- read_trace_case_definitions_2(Rest,
- Dir,
- insert_tracecase_tc_dict(TCname,
- on,
- VarNames,
- FileName,
- TCdict));
-read_trace_case_definitions_2([{TCname,on_off,VarNames,FNameOn,FNameOff}|Rest],Dir,TCdict) ->
- FileNameOn=make_absolute_path(FNameOn,Dir),
- FileNameOff=make_absolute_path(FNameOff,Dir),
- read_trace_case_definitions_2(Rest,
- Dir,
- insert_tracecase_tc_dict(TCname,
- on_off,
- VarNames,
- FileNameOn,
- FileNameOff,
- TCdict));
-read_trace_case_definitions_2([_|Rest],Dir,TCdict) ->
- read_trace_case_definitions_2(Rest,Dir,TCdict);
-read_trace_case_definitions_2([],_Dir,TCdict) ->
- TCdict.
-
-%% Help function returning an absolute path to FName if FName is not already
-%% absolute. Dir is the working dir of the tool and supposed to be absolute.
-make_absolute_path(FName,Dir) ->
- case filename:pathtype(FName) of
- absolute -> % Then do nothing, allready absolute.
- FName;
- _ ->
- filename:join(Dir,FName)
- end.
-%% -----------------------------------------------------------------------------
-
-get_status(undefined,_Node) ->
- inviso:get_status();
-get_status(CNode,Nodes) ->
- inviso_tool_lib:inviso_cmd(CNode,get_status,[Nodes]).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Internal data structure functions.
-%% =============================================================================
-
-%% -----------------------------------------------------------------------------
-%% The nodes database structure.
-%% -----------------------------------------------------------------------------
-
-%% The purpose of the nodes database structure is to keep track of what runtime
-%% nodes we have, and their current status.
-%% Implementation:
-%% [{NodeName,AvailableStatus},...] or AvailableStatus in the
-%% non-distributed case.
-%% AvailableStatus={up,Status1} | down
-%% Status1={State,Status} | reactivating
-%% State=tracing | inactive | trace_failure
-%% Status=running | suspended
-%% reactivating=the node is now being brought up to date.
-%% inactive=not tracing, can be initiated and then reactivated.
-%% The following states can occure.
-%% {inactive,running}
-%% Mainly when we start the tool, before a session has been started.
-%% {tracing,running}
-%% When a trace session is on-going.
-%% {trace_failure,running}
-%% If init_tracing failed for some reason.
-%% {tracing,suspended}
-%% reactivating
-%% The node is tracing (has always been) but was suspended. It is now
-%% no longer suspended and the tool is redong commands.
-%% {inactive,suspended}
-%% We can end up here if a session is stopped with this node suspended.
-
-%% Returns a nodes database structure filled with the nodes Nodes.
-mk_nodes(Nodes) when is_list(Nodes) ->
- {ok,lists:map(fun(N) when is_atom(N)->{N,down} end,Nodes)};
-mk_nodes(local_runtime) -> % The non-distributed case.
- down;
-mk_nodes(_Nodes) ->
- error.
-%% -----------------------------------------------------------------------------
-
-%% Updates the nodes database structure for each node that has been added.
-%% This is the case when we start the tool or reactivate a node. Note that a node
-%% may have become adopted instead of started.
-%% Returns a new nodes database structure.
-update_added_nodes(CNode,[{Node,NodeResult}|Rest],NodesD) ->
- case update_added_nodes_3(NodeResult) of
- already_added -> % Already added to the control component.
- case get_status(CNode,[Node]) of % Examine if it is tracing or not.
- {ok,[{Node,NodeResult2}]} ->
- Result=mk_nodes_state_from_status(NodeResult2),
- update_added_nodes_2(CNode,Node,Result,NodesD,Rest);
- {error,_Reason} -> % Strange, mark it as down now.
- update_added_nodes_2(CNode,Node,down,NodesD,Rest)
- end;
- Result ->
- update_added_nodes_2(CNode,Node,Result,NodesD,Rest)
- end;
-update_added_nodes(_CNode,[],NodesD) ->
- NodesD;
-update_added_nodes(_CNode,NodeResult,_NodesD) -> % Non distributed case.
- case update_added_nodes_3(NodeResult) of
- already_added -> % Already added, most likely autostart.
- mk_nodes_state_from_status(inviso:get_status());
- Result ->
- Result % Simply replace NodesD.
- end.
-
-update_added_nodes_2(CNode,Node,Result,NodesD,Rest) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,_} -> % Node already exists, replace!
- update_added_nodes(CNode,Rest,lists:keyreplace(Node,1,NodesD,{Node,Result}));
- false -> % Strange, unknown node!
- update_added_nodes(CNode,Rest,NodesD)
- end.
-
-update_added_nodes_3({ok,{adopted,tracing,running,_Tag}}) ->
- {up,{tracing,running}};
-update_added_nodes_3({ok,{adopted,tracing,{suspended,_SReason},_Tag}}) ->
- {up,{tracing,suspended}};
-update_added_nodes_3({ok,{adopted,_,running,_Tag}}) ->
- {up,{inactive,running}};
-update_added_nodes_3({ok,{adopted,_,{suspended,_SReason},_Tag}}) ->
- {up,{inactive,suspended}};
-update_added_nodes_3({ok,new}) ->
- {up,{inactive,running}};
-update_added_nodes_3({ok,already_added}) ->
- already_added; % This is an error value!
-update_added_nodes_3({error,_Reason}) ->
- down.
-%% -----------------------------------------------------------------------------
-
-%% Function marking all nodes that, according to the returnvalue from init_tracing,
-%% now are successfully initiated as tracing and running. Note that nodes that
-%% does not fully respond 'ok' when init_tracing are marked as 'trace_failure'.
-%% Also note that we assume that the nodes must be running to have made it this far.
-%% A node can of course have become suspended in the process, but that node will
-%% be marked as suspended later when that inviso event message arrives to the tool.
-%% Returns {NewNodesD,Nodes} where Nodes are the nodes that actually got initiated
-%% as a result of the init_tracing call (judged from the LogResults).
-set_tracing_running_nodes(undefined,{ok,_LogResults},_AvailableStatus) -> % Non-distr. case.
- {{up,{tracing,running}},local_runtime};
-set_tracing_running_nodes(undefined,{error,already_initiated},_) -> % Non-distributed case.
- {mk_nodes_state_from_status(inviso:get_status()),[]}; % Ask it for its status.
-set_tracing_running_nodes(undefined,{error,_Reason},_) -> % Non-distributed case.
- {down,[]}; % This is questionable!
-set_tracing_running_nodes(CNode,{ok,NodeResults},NodesD) ->
- set_tracing_running_nodes_2(CNode,NodeResults,NodesD,[]).
-
-set_tracing_running_nodes_2(CNode,[{Node,{ok,_LogResults}}|Rest],NodesD,Nodes) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,_} ->
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,{up,{tracing,running}}}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,[Node|Nodes]);
- false -> % Strange.
- set_tracing_running_nodes_2(CNode,Rest,NodesD,Nodes)
- end;
-set_tracing_running_nodes_2(CNode,[{Node,{error,already_initiated}}|Rest],NodesD,Nodes) ->
- case get_status(CNode,[Node]) of % Then we must ask what it is doing now.
- {ok,[{Node,NodeResult}]} ->
- Result=mk_nodes_state_from_status(NodeResult),
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,Result}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes);
- {error,_Reason} -> % Strange, mark it as down.
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,down}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes)
- end;
-set_tracing_running_nodes_2(CNode,[{Node,{error,_Reason}}|Rest],NodesD,Nodes) ->
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,{up,{trace_failure,running}}}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes);
-set_tracing_running_nodes_2(_CNode,[],NodesD,Nodes) ->
- {NodesD,Nodes}. % New NodesD and nodes successfully initiated.
-
-%% -----------------------------------------------------------------------------
-
-%% Function updating Node in the NodesD structure and sets it to 'down'.
-%% Returns a new nodes structure.
-set_down_nodes(Node,[{Node,_}|Rest]) ->
- [{Node,down}|Rest];
-set_down_nodes(Node,[NodeStruct|Rest]) ->
- [NodeStruct|set_down_nodes(Node,Rest)];
-set_down_nodes(_,[]) ->
- [];
-set_down_nodes(_,_) -> % Non-distributed case.
- down. % One can argue if this can happend.
-%% -----------------------------------------------------------------------------
-
-%% Function updating Node in NodesD to now be suspended. Note that if the node is
-%% reactivating it must be moved to state tracing because that is what is doing.
-set_suspended_nodes(Node,[{Node,{up,reactivating}}|Rest]) ->
- [{Node,{up,{tracing,suspended}}}|Rest];
-set_suspended_nodes(Node,[{Node,{up,{State,_}}}|Rest]) ->
- [{Node,{up,{State,suspended}}}|Rest];
-set_suspended_nodes(Node,[NodesData|Rest]) ->
- [NodesData|set_suspended_nodes(Node,Rest)];
-set_suspended_nodes(_Node,[]) -> % Hmm, strange why did we end up here?
- [];
-set_suspended_nodes(_,{up,reactivating}) -> % Non-distributed case.
- {up,{tracing,suspended}};
-set_suspended_nodes(_,{up,{State,_}}) ->
- {up,{State,suspended}}.
-%% -----------------------------------------------------------------------------
-
-%% This function is called when reactivation is completed. Hence it moves the
-%% node to no longer suspended. Note this can mean that the node is either
-%% tracing or inactive. Reactivation is not allowed for a node have trace_failure.
-set_running_nodes(Node,NodesD) when is_list(NodesD) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,{_,AvailableStatus}} ->
- lists:keyreplace(Node,1,NodesD,{Node,set_running_nodes_2(AvailableStatus)});
- false -> % Very strange!
- NodesD
- end;
-set_running_nodes(_,NodesD) -> % The non-distributed case.
- set_running_nodes_2(NodesD).
-
-set_running_nodes_2({up,reactivating}) ->
- {up,{tracing,running}};
-set_running_nodes_2({up,{State,suspended}}) ->
- {up,{State,running}}.
-%% -----------------------------------------------------------------------------
-
-%% Function marking node as now reactivating. That means it is not suspended
-%% any longer (and tracing), but still not part of the set of nodes which shall
-%% get all commands. Returns a new NodesD.
-set_reactivating_nodes(Node,[{Node,_}|Rest]) ->
- [{Node,{up,reactivating}}|Rest];
-set_reactivating_nodes(Node,[NodesData|Rest]) ->
- [NodesData|set_reactivating_nodes(Node,Rest)];
-set_reactivating_nodes(_,[]) ->
- [];
-set_reactivating_nodes(_,{up,_}) -> % The non-distributed case.
- {up,reactivating}.
-%% -----------------------------------------------------------------------------
-
-%% Function called when stop-tracing is done. That is all nodes in Nodes shall
-%% be inactive now. Note that an inactive node can still be suspended.
-%% Returns a new NodesD.
-set_inactive_nodes(_,{up,reactivating}) -> % Non-distributed case.
- {up,{inactive,running}};
-set_inactive_nodes(_,{up,{_,Status}}) -> % Tracing or trace_failure.
- {up,{inactive,Status}};
-set_inactive_nodes(_,down) ->
- down;
-set_inactive_nodes([{Node,ok}|Rest],NodesD) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,{_,{up,reactivating}}} ->
- set_inactive_nodes(Rest,lists:keyreplace(Node,1,NodesD,{Node,{up,{inactive,running}}}));
- {value,{_,{up,{_,Status}}}} -> % Tracing or trace_failure.
- set_inactive_nodes(Rest,lists:keyreplace(Node,1,NodesD,{Node,{up,{inactive,Status}}}));
- _ -> % This should not happend.
- set_inactive_nodes(Rest,NodesD)
- end;
-set_inactive_nodes([{_Node,_Error}|Rest],NodesD) ->
- set_inactive_nodes(Rest,NodesD);
-set_inactive_nodes([],NodesD) ->
- NodesD.
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of all node names. Note that it can only be used in the
-%% distributed case.
-get_all_nodenames_nodes(NodesD) ->
- lists:map(fun({Node,_})->Node end,NodesD).
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of all nodes that are up, tracing and running (not suspended),
-%% or 'void' in the non-distributed case. This is the list of nodes that shall get
-%% inviso commands.
-get_nodenames_running_nodes([{Node,{up,{tracing,running}}}|Rest]) ->
- [Node|get_nodenames_running_nodes(Rest)];
-get_nodenames_running_nodes([{_Node,_}|Rest]) ->
- get_nodenames_running_nodes(Rest);
-get_nodenames_running_nodes([]) ->
- [];
-get_nodenames_running_nodes(_) ->
- void. % When non distributed, N/A.
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of nodes that can be made to initiate tracing.
-get_inactive_running_nodes({up,{inactive,running}}) ->
- local_runtime;
-get_inactive_running_nodes(NonDistributed) when not(is_list(NonDistributed)) ->
- [];
-get_inactive_running_nodes([{Node,{up,{inactive,running}}}|Rest]) ->
- [Node|get_inactive_running_nodes(Rest)];
-get_inactive_running_nodes([{_Node,_}|Rest]) ->
- get_inactive_running_nodes(Rest);
-get_inactive_running_nodes([]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of nodes that are currently tracing (not necessarily running).
-%% In the non-distributed case the status of the runtime component will be
-%% returned.
-%% Note that nodes showing trace_failure will be included since we like to stop
-%% tracing at those nodes too.
-get_tracing_nodes([{Node,{up,{tracing,_}}}|Rest]) ->
- [Node|get_tracing_nodes(Rest)];
-get_tracing_nodes([{Node,{up,{trace_failure,_}}}|Rest]) ->
- [Node|get_tracing_nodes(Rest)];
-get_tracing_nodes([{Node,{up,reactivating}}|Rest]) ->
- [Node|get_tracing_nodes(Rest)];
-get_tracing_nodes([_|Rest]) ->
- get_tracing_nodes(Rest);
-get_tracing_nodes([]) ->
- [];
-get_tracing_nodes(AvailableStatus) ->
- AvailableStatus.
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of all nodes that are currently up.
-get_available_nodes(down) ->
- undefined;
-get_available_nodes([{_Node,down}|Rest]) ->
- get_available_nodes(Rest);
-get_available_nodes([{Node,_}|Rest]) ->
- [Node|get_available_nodes(Rest)];
-get_available_nodes([]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Function returning the "state" of Node. Mainly used to check if the node is
-%% suspended or not.
-%% Returns {State,Status} | reactivating | down
-%% where
-get_state_nodes(Node,NodesD) when is_list(NodesD) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,{_,AvailableStatus}} ->
- get_state_nodes_2(AvailableStatus);
- false ->
- false
- end;
-get_state_nodes(_,NodesD) -> % Non distributed case.
- get_state_nodes_2(NodesD).
-
-get_state_nodes_2({up,{trace_failure,Status}}) ->
- {trace_failure,Status};
-get_state_nodes_2({up,{State,suspended}}) -> % {tracing|inactive,suspended}
- {State,suspended};
-get_state_nodes_2({up,reactivating}) ->
- reactivating;
-get_state_nodes_2({up,{State,running}}) ->
- {State,running};
-get_state_nodes_2(down) ->
- down.
-%% -----------------------------------------------------------------------------
-
-%% Help function in the case we need to consult the state/status of a runtime
-%% component. Returns a nodesD value that can be added to the nodes database.
-mk_nodes_state_from_status({ok,{tracing,running}}) ->
- {up,{tracing,running}};
-mk_nodes_state_from_status({ok,{tracing,{suspended,_SReason}}}) ->
- {up,{tracing,suspended}};
-mk_nodes_state_from_status({ok,{_,running}}) ->
- {up,{inactive,running}};
-mk_nodes_state_from_status({ok,{_,{suspended,_SReason}}}) ->
- {up,{inactive,suspended}};
-mk_nodes_state_from_status({error,_Reason}) ->
- down.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% The session_state.
-%% -----------------------------------------------------------------------------
-
-%% The session state reflects if the inviso_tool is tracing or not.
-%% This means that if the tool is tracing a reconnected node can be made to
-%% restart_session.
-
-%% Returns the correct value indicating that we are tracing now.
-tracing_sessionstate() ->
- tracing.
-%% -----------------------------------------------------------------------------
-
-%% Returns true or false depending on if we are tracing now or not.
-is_tracing(tracing) ->
- true;
-is_tracing(_) ->
- false.
-%% -----------------------------------------------------------------------------
-
-%% Returns the correct value indicating that the tool is not tracing.
-passive_sessionstate() ->
- idle.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% The tracer_data datastructure.
-%% -----------------------------------------------------------------------------
-
-%% The tracer_data structure collects the tracer data arguments used to init tracing
-%% by this inviso tool. The args are saved per session. Each session has
-%% a number.
-%% Implementation:
-%% Sessions=[{SessionNr,TDGargs},...]
-%% SessionNr=integer()
-%% TDGargs=list(), args given to the tracer data generator
-%% minus the first argument which is the Node name.
-
-%% Function taking tracerdata args structure inserting yet another session.
-%% Returns {SessionNr,NewTDs}.
-insert_td_tracer_data(TDGargs,TDs=[{SNr,_}|_]) ->
- {SNr+1,[{SNr+1,TDGargs}|TDs]};
-insert_td_tracer_data(TDGargs,undefined) ->
- {1,[{1,TDGargs}]}.
-%% -----------------------------------------------------------------------------
-
-%% Returns the latest session nr.
-get_latest_session_nr_tracer_data(undefined) ->
- undefined;
-get_latest_session_nr_tracer_data([{SessionNr,_}|_]) ->
- SessionNr.
-%% -----------------------------------------------------------------------------
-
-%% Returns the tracer data arguments used when creating the trace data for the
-%% latest session.
-get_latest_tdgargs_tracer_data(undefined) ->
- undefined;
-get_latest_tdgargs_tracer_data([{_,TDGargs}|_]) ->
- TDGargs.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% The tc_dict or trace case dictionary datastructure.
-%% -----------------------------------------------------------------------------
-
-%% The tc_dict stores information about all available trace cases.
-%% Implementation:
-%% [{TCname,Type,VarNames,FNameOn [,FNameOff]},...]
-%% TCname=atom()
-%% Type=on | on_off
-%% VarNames=[atom(),...]
-%% FNameOn=FNameOff=string()
-
-%% Returns the empty trace case dictionary.
-mk_tc_dict() ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Function inserting a new trace case into the trace case dictionary.
-insert_tracecase_tc_dict(TCname,on,VarNames,FNameOn,TCdict) ->
- [{TCname,on,VarNames,FNameOn}|TCdict].
-insert_tracecase_tc_dict(TCname,on_off,VarNames,FNameOn,FNameOff,TCdict) ->
- [{TCname,on_off,VarNames,FNameOn,FNameOff}|TCdict].
-%% -----------------------------------------------------------------------------
-
-%% Function finding a trace case definition in the tc_dict structure.
-%% Returns {ok,{TCname,Type,VarNAmes,FNameOn [,FNameOff]}} or 'false'.
-get_tracecase_tc_dict(TCname,[Tuple|_]) when element(1,Tuple)==TCname ->
- {ok,Tuple};
-get_tracecase_tc_dict(TCname,[_|Rest]) ->
- get_tracecase_tc_dict(TCname,Rest);
-get_tracecase_tc_dict(_,[]) ->
- false;
-get_tracecase_tc_dict(_,_) -> % There are no trace cases!
- false.
-%% -----------------------------------------------------------------------------
-
-%% Function working on the trace case definition returned by get_tracecase_tc_dict/2
-%% function.
-%% Returning {ok,ActivationFileName}.
-get_tc_activate_fname({_TCname,_Type,_VarNames,FNameOn}) ->
- {ok,FNameOn};
-get_tc_activate_fname({_TCname,_Type,_VarNames,FNameOn,_FNameOff}) ->
- {ok,FNameOn}.
-
-get_tc_deactivate_fname({_TCname,_Type,_VarNames,_FNameOn,FNameOff}) ->
- {ok,FNameOff};
-get_tc_deactivate_fname(_) -> % Not a case with off function.
- false.
-
-get_tc_varnames({_TCname,_Type,VarNames,_FNameOn}) ->
- VarNames;
-get_tc_varnames({_TCname,_Type,VarNames,_FNameOn,_FNameOff}) ->
- VarNames.
-
-%% -----------------------------------------------------------------------------
-
-
-%% The Command History Log (CHL) stores commands to make it possible to
-%% reactivate suspended nodes, reconnect restarted nodes, and to make
-%% autostart files.
-%% Each time tracing is initiated (that is started) the CHL is cleared since
-%% it would not make scense to repeat commands from an earlier tracing at
-%% reactivation for instance.
-
-%% Implementation: {NextCounter,OnGoingList,ETStable}
-%% NextCounter=integer(), next command number - to be able to sort them in order.
-%% OnGoingList=[{ProcH,{TCname,ID}},...]
-%% ID=term(), instance id for this execution of this trace case.
-%% ETStable=tid() -> {{TCname,Id},Counter,State1,Bindings}
-%% ETStable=tid() -> {{TCname,Id},Counter,running,Bindings,Result} |
-%% {{TCname,Id,#Ref},Counter,stop,Bindings} |
-%% {{TCname,#Ref},Counter,Bindings} % An rtc
-%% {{M,F,Args,#Ref},Counter}
-%% Counter=integer(), the order-counter for this logged entry.
-%% State1=activating | stopping
-%% Where:
-%% activating: the activation file for the tracecase is running.
-%% running : activation is completed.
-%% stopping : set on the previously running ETS entry when deactivation
-%% file is currently executing.
-%% stop : entered with own Counter into the ETS table when
-%% deactivation file is executing. Remains after too.
-%% Result=term(), the result returned from the tr-case or inviso call.
-
-
-%% Returning an initial empty CHL.
-mk_chl(undefined) ->
- {1,[],ets:new(inviso_tool_chl,[set,protected])};
-mk_chl({_,_,TId}) ->
- ets:delete(TId),
- mk_chl(undefined).
-
-%% Help function returning 'true' if there is a current history.
-history_exists_chl(undefined) ->
- false;
-history_exists_chl({_,_,_}) ->
- true.
-
-%% Function looking up the state of this trace case.
-find_id_chl(TCname,Id,{_NextCounter,_OnGoingList,TId}) ->
- case ets:lookup(TId,{TCname,Id}) of
- [{_,_,running,Bindings,_Result}] -> % The trace case is tracing.
- {ok,Bindings};
- [{_,_,State,_}] -> % activating or stopping.
- State;
- [] ->
- false
- end.
-
-%% Function finding the Trace case associated with a process handle
-%% doing this trace case's activation or stopping.
-find_tc_executer_chl(ProcH,{_,OnGoingList,TId}) ->
- case lists:keysearch(ProcH,1,OnGoingList) of
- {value,{_,{TCname,Id}}} ->
- [{_,_,State,_}]=ets:lookup(TId,{TCname,Id}),
- {State,{TCname,Id}}; % Should be activating or stopping.
- false ->
- false
- end.
-
-%% Adds a Trace case to the CHL. This is done when it is turned on. Or when it
-%% is called for trace cases that do not have on/off functionality.
-set_activating_chl(TCname,Id,{Counter,OnGoingList,TId},Bindings,ProcH) ->
- ets:insert(TId,{{TCname,Id},Counter,activating,Bindings}),
- {Counter+1,[{ProcH,{TCname,Id}}|OnGoingList],TId}.
-
-%% Function marking a trace case as now running. That is the activation
-%% phase is completed. It is normaly completed when the process executing
-%% the trace case signals that it is done.
-set_running_chl(ProcH,TCname,Id,Result,{NextCounter,OnGoingList,TId}) ->
- [{_,Counter,_,Bindings}]=ets:lookup(TId,{TCname,Id}),
- ets:insert(TId,{{TCname,Id},Counter,running,Bindings,Result}),
- NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
- {NextCounter,NewOnGoingList,TId}.
-
-%% Function marking trace case TCname with identifier Id as now in its stopping
-%% state. Where ProcH is the handler to the process running the stopping
-%% trace case.
-set_stopping_chl(TCname,Id,{NextCounter,OnGoingList,TId},ProcH)->
- [{_,Counter,_,Bindings,_}]=ets:lookup(TId,{TCname,Id}),
- ets:insert(TId,{{TCname,Id},Counter,stopping,Bindings}),
- ets:insert(TId,{{TCname,Id,make_ref()},NextCounter,stop,Bindings}),
- {NextCounter+1,[{ProcH,{TCname,Id}}|OnGoingList],TId}.
-
-%% Function removing a TCname-Id from the CHL. This is mostly used
-%% if activating the trace case failed for some reason. We do not then
-%% expect the user to stop the trace case. Hence it must be removed now.
-%% A reactivation process may have noticed the activating-entry and started
-%% to activate it. But since the general state reached after an unsuccessful
-%% activation can not easily be determined, we don't try to do much about it.
-del_tc_chl(ProcH,TCname,Id,{NextCounter,OnGoingList,TId}) ->
- ets:delete(TId,{TCname,Id}),
- NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
- {NextCounter,NewOnGoingList,TId}.
-
-%% Function removing the entry TCname+Id from the CHL. This makes it
-%% possible to activate a tracecase with this id again. The entry was
-%% previously marked as stopping.
-nullify_chl(ProcH,TCname,Id,{NextCounter,OnGoingList,TId}) ->
- ets:delete(TId,{TCname,Id}),
- NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
- {NextCounter+1,NewOnGoingList,TId}.
-
-%% Function stopping all processes saved as being now running tc executers.
-%% This is useful as cleanup during stop tracing for instance.
-%% Returns a new CHL which is not in all parts correct. Entries in the
-%% ETS table are for instance not properly state-changed. But the CHL will
-%% from now on only be used to create command files and similar.
-stop_all_tc_executer_chl({NextCounter,[{ProcH,_}|Rest],TId}) ->
- exit(ProcH,kill),
- stop_all_tc_executer_chl({NextCounter,Rest,TId});
-stop_all_tc_executer_chl({NextCounter,[],TId}) ->
- {NextCounter,[],TId}.
-
-%% Function adding a "plain" inviso call to the CHL.
-add_inviso_call_chl(Cmd,Args,{NextCounter,OnGoingList,TId}) ->
- ets:insert(TId,{{inviso,Cmd,Args,make_ref()},NextCounter}),
- {NextCounter+1,OnGoingList,TId}.
-
-%% Function adding a run trace case entry to the chl.
-add_rtc_chl(TCname,Bindings,{NextCounter,OnGoingList,TId}) ->
- ets:insert(TId,{{TCname,make_ref()},NextCounter,Bindings}),
- {NextCounter+1,OnGoingList,TId}.
-%% Returns the highest used counter number in the command history log.
-get_highest_used_counter_chl({NextCounter,_,_}) ->
- NextCounter-1.
-
-%% Help function returning a list of {{TCname,Id},Phase} for all ongoing
-%% assynchronous tracecases.
-get_ongoing_chl(undefined) ->
- [];
-get_ongoing_chl({_,OngoingList,TId}) ->
- get_ongoing_chl_2(OngoingList,TId).
-
-get_ongoing_chl_2([{_ProcH,{TCname,Id}}|Rest],TId) ->
- case ets:lookup(TId,{TCname,Id}) of
- [{_,_C,activating,_B}] ->
- [{{TCname,Id},activating}|get_ongoing_chl_2(Rest,TId)];
- [{_,_C,stopping,_B}] ->
- [{{TCname,Id},deactivating}|get_ongoing_chl_2(Rest,TId)]
- end;
-get_ongoing_chl_2([],_) ->
- [].
-
-%% Function returning a list of log entries. Note that the list is unsorted
-%% in respect to Counter.
-get_loglist_chl({_,_,TId}) ->
- L=ets:tab2list(TId),
- lists:map(fun({{TC,Id},C,S,B,_Result}) -> {{TC,Id},C,S,B}; % running
- (Tuple={{_TC,_Id},_C,_S,_B}) -> Tuple; % activating | stopping
- (Tuple={{_TC,_Id,_Ref},_C,_S,_B}) -> Tuple; % stop
- (Tuple={{_M,_F,_Args,_Ref},_C}) -> Tuple;
- (Tuple={{_TC,_Ref},_C,_B}) -> Tuple
- end,
- L);
-get_loglist_chl(_) -> % The history is not initiated, ever!
- [].
-
-%% Function returning a list of log entries, but only those which are not
-%% cancelled out by deactivations.
-% get_loglist_active_chl({_,_,TId}) ->
-% L=ets:tab2list(TId),
-% lists:zf(fun({{TC,Id},C,S,B,_Result}) -> {true,{{TC,Id},C,S,B}}; % running
-% (Tuple={{_TC,_Id},_C,_S,_B}) -> Tuple; % activating | stopping
-% (Tuple={{_TC,_Id,_Ref},_C,_S,_B}) -> Tuple; % stop
-% (Tuple={{_M,_F,_Args,_Ref},_C}) -> Tuple
-% end,
-% L);
-% get_loglist_chl(_) -> % The history is not initiated, ever!
-% [].
-
-
-%% This helpfunction recreates a history from a saved history list. This function
-%% is supposed to crash if the log is not well formatted. Note that we must restore
-%% the counter in order for the counter to work if new commands are added to the
-%% history.
-replace_history_chl(OldCHL,SortedLog) ->
- {_,Ongoing,TId}=mk_chl(OldCHL),
- {NewTId,Counter}=replace_history_chl_2(TId,SortedLog,0),
- {ok,{Counter+1,Ongoing,NewTId}}.
-
-replace_history_chl_2(TId,[{{TC,Id},C,running,B}|Rest],_Counter) ->
- ets:insert(TId,{{TC,Id},C,running,B,undefined}),
- replace_history_chl_2(TId,Rest,C);
-replace_history_chl_2(TId,[{{M,F,Args},C}|Rest],_Counter) ->
- ets:insert(TId,{{M,F,Args,make_ref()},C}),
- replace_history_chl_2(TId,Rest,C);
-replace_history_chl_2(TId,[{TC,C,B}|Rest],_Counter) ->
- ets:insert(TId,{{TC,make_ref()},C,B}),
- replace_history_chl_2(TId,Rest,C);
-replace_history_chl_2(TId,[],Counter) ->
- {TId,Counter}.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Reactivators data structure.
-%% -----------------------------------------------------------------------------
-
-%% Function adding a new node-reactivatorpid pair to the reactivators structure.
-%% In this way we know which reactivators to remove if Node terminates, or when
-%% a node is fully updated when a reactivator is done.
-add_reactivators(Node,Pid,Reactivators) ->
- [{Node,Pid}|Reactivators].
-
-%% Function removing a reactivator entry from the reactivators structure.
-del_reactivators(RPid,[{_Node,RPid}|Rest]) ->
- Rest;
-del_reactivators(RPid,[Element|Rest]) ->
- [Element|del_reactivators(RPid,Rest)];
-del_reactivators(_,[]) -> % This should not happend.
- [].
-
-get_node_reactivators(RPid,Reactivators) ->
- case lists:keysearch(RPid,2,Reactivators) of
- {value,{Node,_}} ->
- Node;
- false -> % This should not happend.
- false
- end.
-
-%% Returns a list of list all nodes that are currently reactivating.
-get_all_nodes_reactivators([{Nodes,_Pid}|Rest]) ->
- [Nodes|get_all_nodes_reactivators(Rest)];
-get_all_nodes_reactivators([]) ->
- [].
-
-%% Function stopping all running reactivator processes. Returns a new empty
-%% reactivators structure. Note that this function does not set the state of
-%% Nodes. It must most often be set to running.
-stop_all_reactivators([{_Nodes,Pid}|Rest]) ->
- exit(Pid,kill),
- stop_all_reactivators(Rest);
-stop_all_reactivators([]) ->
- []. % Returns an empty reactivators.
-
-%% Help function stopping the reactivator (if any) that reactivates Node.
-%% Returns a new list of reactivators structure.
-stop_node_reactivators(Node,[{Node,Pid}|Rest]) ->
- exit(Pid,kill),
- Rest;
-stop_node_reactivators(Node,[NodePid|Rest]) ->
- [NodePid|stop_node_reactivators(Node,Rest)];
-stop_node_reactivators(_,[]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Started initial trace cases data structure.
-%% -----------------------------------------------------------------------------
-
-%% This datastructure keeps information about ongoing trace cases started
-%% automatically at init_tracing. These must be automatically stopped when calling
-%% stop_tracing.
-
-add_initial_tcs(TCname,Id,StartedInitialTcs) ->
- [{TCname,Id}|StartedInitialTcs].
-%% -----------------------------------------------------------------------------
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/inviso/src/inviso_tool_lib.erl b/lib/inviso/src/inviso_tool_lib.erl
deleted file mode 100644
index f221c4b6de..0000000000
--- a/lib/inviso/src/inviso_tool_lib.erl
+++ /dev/null
@@ -1,379 +0,0 @@
-% ``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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights 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$
-%%
-%% Description:
-%% Support module to the inviso tool.
-%%
-%% Authors:
-%% Lennart Öhman, [email protected]
-%% -----------------------------------------------------------------------------
-
--module(inviso_tool_lib).
-
-%% -----------------------------------------------------------------------------
-%% Exported library APIs
-%% -----------------------------------------------------------------------------
--export([inviso_cmd/3,expand_module_names/3,make_patterns/7,std_tdg/2]).
--export([mk_tdg_args/2,mk_complete_tdg_args/2,get_datetime_from_tdg_args/1]).
--export([debug/3]).
-
-%% -----------------------------------------------------------------------------
-%% Constants.
-%% -----------------------------------------------------------------------------
--define(DBG_OFF,off). % No internal debug indicator.
-
-
-%% =============================================================================
-%% Functions for inviso_cmd
-%% =============================================================================
-
-%% Help function which executes a trace control call. The reason for having a special
-%% function is that we either want to do rpc if the trace control component is
-%% located on another Erlang node than this one. Or call trace_c directly if
-%% it actually is on this node.
-%% Returns whatever the inviso function returns. In case of badrpc it is wrapped
-%% in an error-tuple.
-inviso_cmd(NodeName,Func,Args) ->
- case node() of
- NodeName -> % Control component on this node.
- apply(inviso,Func,Args);
- _ -> % On other node, must do RPC.
- case rpc:call(NodeName,inviso,Func,Args) of
- {badrpc,Reason} ->
- {error,{badrpc,Reason}};
- Result ->
- Result
- end
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Functions for expand_module_names
-%% =============================================================================
-
-%% Help function which expands the module name depending on how it is expressed.
-%% Setting Nodes to 'void' makes it non-distributed, expanding only here.
-%% The following special cases are handled:
-%% '_' =All modules, no expansion into individual module names. Instead
-%% it is intended to use the '_' mechanism in the trace_pattern BIF.
-%% Can therefore not be combined with a directory regexp.
-%% "*" =Is translated into ".*".
-%% "abc"=Means ".*abc.*". Can only be used for module or directory names
-%% containing upper or lowercase, digits and a slash.
-%% Returns {multinode_expansion,NodeModules},
-%% {singlenode_expansion,Modules},
-%% 'module', 'wildcard' or {error,Reason},
-%% To limit the places where expansion is done, the option {expand_only_at,Node}
-%% can be provided in the Opts list.
-%% In the non-distributed case the singlenode_expansion will be returned.
-expand_module_names(_Nodes,Mod={_,'_'},_) ->
- {error,{faulty_regexp_combination,Mod}};
-expand_module_names(Nodes,{DirStr,ModStr},Opts) when is_list(DirStr), is_list(ModStr) ->
- case expand_module_names_special_regexp(DirStr) of
- {ok,NewDirStr} ->
- case expand_module_names_special_regexp(ModStr) of
- {ok,NewModStr} ->
- expand_module_names_2(Nodes,NewDirStr,NewModStr,Opts);
- {error,_Reason} ->
- {error,{faulty_regexp,ModStr}}
- end;
- {error,_Reason} ->
- {error,{faulty_regexp,DirStr}}
- end;
-expand_module_names(_,'_',_Opts) -> % If we want to trace all modules
- wildcard; % we shall not expand it.
-expand_module_names(_Nodes,Mod,_Opts) when is_atom(Mod) ->
- module; % If it is an atom, no expansion.
-expand_module_names(Nodes,"*",Opts) -> % Treat this as a reg.exp.
- expand_module_names(Nodes,".*",Opts);
-expand_module_names(Nodes,ModStr,Opts) when is_list(ModStr) ->
- case expand_module_names_special_regexp(ModStr) of
- {ok,NewModStr} ->
- expand_module_names_2(Nodes,NewModStr,Opts);
- {error,_Reason} ->
- {error,{faulty_regexp,ModStr}}
- end.
-
-expand_module_names_2(void,ModStr,Opts) -> % Non-distributed case.
- {singlenode_expansion,inviso_rt_lib:expand_regexp(ModStr,Opts)};
-expand_module_names_2(Nodes,ModStr,Opts) ->
- case get_expand_regexp_at_opts(Opts) of
- {ok,Node} -> % Expansion only at this node.
- case inviso_rt_lib:expand_regexp([Node],ModStr,Opts) of
- [{Node,Modules}] when is_list(Modules) ->
- {singlenode_expansion,Modules};
- [{Node,_}] -> % Most likely badrpc.
- {error,{faulty_node,Node}}
- end;
- false -> % Expand on all nodes.
- Result=inviso_rt_lib:expand_regexp(Nodes,ModStr,Opts),
- {multinode_expansion,Result}
- end.
-expand_module_names_2(void,DirStr,ModStr,Opts) -> % Non-distributed case.
- {singlenode_expansion,inviso_rt_lib:expand_regexp(DirStr,ModStr,Opts)};
-expand_module_names_2(Nodes,DirStr,ModStr,Opts) ->
- case get_expand_regexp_at_opts(Opts) of
- {ok,Node} -> % Expansion only at this node.
- case inviso_rt_lib:expand_regexp([Node],DirStr,ModStr,Opts) of
- [{Node,Modules}] when is_list(Modules) ->
- {singlenode_expansion,Modules};
- [{Node,_}] -> % Most likely badrpc.
- {error,{faulty_node,Node}}
- end;
- false -> % Expand on all nodes.
- Result=inviso_rt_lib:expand_regexp(Nodes,DirStr,ModStr,Opts),
- {multinode_expansion,Result}
- end.
-
-%% Help function which converts a special regexp into a proper one. With
-%% special regexps we mean e.g:"abc" which is supposed to mean ".*abc.*".
-%% Always returns a regexp or {error,Reason}.
-expand_module_names_special_regexp(Str) ->
- StrLen=length(Str),
- case re:run(Str,"[0-9a-zA-Z_/]*") of
- {match,[{0,StrLen}]} -> % Ok, it is the special case.
- {ok,".*"++Str++".*"}; % Convert it to a proper regexp.
- {match,_} ->
- {ok,Str}; % Keep it and hope it is a regexp.
- nomatch ->
- {ok,Str}; % Keep it and hope it is a regexp.
- {error,Reason} -> % Can't continue with this!
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Functions for make_pattern.
-%% =============================================================================
-
--define(DEPTH,3). % Max recursive safety catch depth.
-
-%% Help function that creates trace-patterns for each module in the list.
-%% It can handle both lists of modules or lists of nodes and modules.
-%% It will also in the process apply safety catches, if such are not disabled,
-%% in order to prevent certain patterns to form.
-%% The safety catch function is supposed to return either 'ok' or {new,NewTracePattern}.
-%% Where the NewTracePattern is a list of zero or more {M,F,Arity,MS}. The
-%% NewTracePatter is then the replacement for the tried trace-pattern.
-%% Note that the new trace-pattern(s) are also tried against all safety catches.
-%% This can possibly result in even replacements of the replacements. There is
-%% a depth meter to prevent the safety catch mechanism from circularly expanding
-%% trace patterns for ever.
-%% Returns a list of [{Node,PatternList},...] or [Pattern,...].
-%% The latter is used when the modules have been expanded on a single node.
-make_patterns(Catches,Opts,Dbg,NodeModsOrMods,F,A,MS) ->
- OwnArg=get_ownarg_opts(Opts),
- case get_disable_safety_opts(Opts) of
- true -> % Do not use the safety catches.
- make_patterns_2(void,OwnArg,Dbg,NodeModsOrMods,F,A,MS);
- false ->
- make_patterns_2(Catches,OwnArg,Dbg,NodeModsOrMods,F,A,MS)
- end.
-
-make_patterns_2(Catches,OwnArg,Dbg,[{Node,Mods}|Rest],F,A,MS) when is_list(Mods) ->
- TPs=make_patterns_3(Catches,OwnArg,Dbg,Mods,F,A,MS,[]),
- [{Node,join_patterns(TPs)}|make_patterns_2(Catches,OwnArg,Dbg,Rest,F,A,MS)];
-make_patterns_2(Catches,OwnArg,Dbg,[{_Node,_}|Rest],F,A,MS) -> % badrpc!?
- make_patterns_2(Catches,OwnArg,Dbg,Rest,F,A,MS);
-make_patterns_2(Catches,OwnArg,Dbg,Modules,F,A,MS) when is_list(Modules) ->
- TPs=make_patterns_3(Catches,OwnArg,Dbg,Modules,F,A,MS,[]),
- join_patterns(TPs);
-make_patterns_2(_,_,_,[],_,_,_) ->
- [].
-
-make_patterns_3(void,OwnArg,Dbg,[M|Rest],F,A,MS,Result) -> % S-catches not used!
- make_patterns_3(void,OwnArg,Dbg,Rest,F,A,MS,[{M,F,A,MS}|Result]);
-make_patterns_3(Catches,OwnArg,Dbg,[M|Rest],F,A,MS,Result) ->
- NewTPs=try_safety_catches(Catches,OwnArg,[{M,F,A,MS}],Dbg,[],?DEPTH),
- make_patterns_3(Catches,OwnArg,Dbg,Rest,F,A,MS,[NewTPs|Result]);
-make_patterns_3(_,_,_,[],_,_,_,Result) ->
- lists:flatten(Result).
-
-try_safety_catches(_Catches,_OwnArg,TPs,Dbg,_Accum,0) -> % Max depth here!
- debug(max_catch_depth,Dbg,[TPs]),
- TPs; % Just return them unchanged.
-try_safety_catches(Catches,OwnArg,[TP={M,F,A,MS}|Rest],Dbg,Accum,Depth) ->
- case try_safety_catch(Catches,OwnArg,M,F,A,MS,Dbg) of
- ok -> % This pattern is safe!
- try_safety_catches(Catches,OwnArg,Rest,Dbg,[TP|Accum],?DEPTH);
- {new,NewTPs} -> % Then we must try them too!
- NewTPs2=try_safety_catches(Catches,OwnArg,NewTPs,Dbg,[],Depth-1),
- try_safety_catches(Catches,OwnArg,Rest,Dbg,[NewTPs2|Accum],?DEPTH)
- end;
-try_safety_catches(_,_,[],_,Accum,_) ->
- Accum.
-
-try_safety_catch([{SafetyMod,SafetyFunc}|Rest],OwnArg,M,F,A,MS,Dbg) ->
- case (catch apply(SafetyMod,SafetyFunc,[M,F,A,MS,OwnArg])) of
- ok -> % This catch has no oppinion about it.
- try_safety_catch(Rest,OwnArg,M,F,A,MS,Dbg); % Continue with the next.
- {new,NewTPs} -> % Replace it with this or these new.
- debug(safety_catch,Dbg,[new,{SafetyMod,SafetyFunc},M,F,A,MS,NewTPs]),
- {new,NewTPs}; % and stop trying safety cathes.
- {'EXIT',Reason} -> % Something wrong with the safety catch.
- debug(safety_catch,Dbg,['EXIT',{SafetyMod,SafetyFunc},M,F,A,MS,Reason]),
- try_safety_catch(Rest,OwnArg,M,F,A,MS,Dbg) % Skip it and go on.
- end;
-try_safety_catch([],_,_,_,_,_,_) ->
- ok. % Since it passed all, it is safe!
-%% -----------------------------------------------------------------------------
-
-%% Help function that joins patterns together. This is necessary since you can
-%% only set the pattern once for a module-function-arity. This function can not
-%% remove conflicting match-spec "commands". Match-specs will simply be concatenated.
-%% Returns a list of patterns where each mod-func-arity is unique.
-join_patterns(Patterns) ->
- join_patterns_2(Patterns,[]).
-
-join_patterns_2([{M,F,Arity,MS}|Rest],Result) ->
- case join_patterns_is_already_done(M,F,Arity,Result) of
- false -> % No we have not collapsed this one.
- case join_patterns_3(M,F,Arity,Rest) of
- [] -> % No this combination is unique.
- join_patterns_2(Rest,[{M,F,Arity,MS}|Result]);
- MSs -> % We got a list of all other TPs.
- join_patterns_2(Rest,[{M,F,Arity,MS++MSs}|Result])
- end;
- true -> % We already joined this M-F-Arity.
- join_patterns_2(Rest,Result) % Simply skip it, already done.
- end;
-join_patterns_2([],Result) ->
- Result. % Reversed but does not matter!
-
-%% Help function checking if we have already built a trace-pattern for
-%% this M-F-Arity. If so, the found M-F-Arity is already handled.
-join_patterns_is_already_done(M,F,Arity,[{M,F,Arity,_}|_]) ->
- true;
-join_patterns_is_already_done(M,F,Arity,[_|Rest]) ->
- join_patterns_is_already_done(M,F,Arity,Rest);
-join_patterns_is_already_done(_,_,_,[]) ->
- false.
-
-%% Help function which simply concatenates all match-specs for this
-%% M-F-Arity.
-join_patterns_3(M,F,Arity,[{M,F,Arity,MS}|Rest]) ->
- [MS|join_patterns_3(M,F,Arity,Rest)];
-join_patterns_3(M,F,Arity,[_|Rest]) ->
- join_patterns_3(M,F,Arity,Rest);
-join_patterns_3(_,_,_,[]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Function for tracer data creation.
-%% =============================================================================
-
--define(I2L(Arg),integer_to_list(Arg)).
-
-%% The inviso_tool uses a tracer-data generator function to create the tracer_data
-%% specification for each node that shall participate in tracing controlled
-%% through the inviso-tool. If no own tracer data generator function is specified,
-%% this function is used.
-std_tdg(Node,{{Y,Mo,D},{H,Mi,S}}) ->
- NameStr=atom_to_list(Node)++"_"++?I2L(Y)++"-"++?I2L(Mo)++"-"++?I2L(D)++"_"++
- ?I2L(H)++"-"++?I2L(Mi)++"-"++?I2L(S),
- LogTD={file,NameStr++".log"},
- TiTD={file,NameStr++".ti"},
- [{trace,LogTD},{ti,TiTD}].
-%% ------------------------------------------------------------------------------
-
-%% mk_tdg_args(DateTime,Args)=TDGargs
-%% DateTime={Date,Time}
-%% Date=tuple(),
-%% Time=tuple(),
-%% Args=list()
-%% TDGargs=list(),
-%% Creates the TDGargs list used when calling functions making the CompleteTDGargs.
-mk_tdg_args(DateTime,Args) ->
- [DateTime|Args].
-%% ------------------------------------------------------------------------------
-
-%% mk_complete_tdg_args(Node,TDGargs)=CompleteTDGargs
-%% Returns the list of all arguments a tracer data generator function must accept.
-mk_complete_tdg_args(Node,TDGargs) ->
- [Node|TDGargs].
-%% ------------------------------------------------------------------------------
-
-%% get_datetime_from_tdg_args(TDGargs)=DateTime
-%% Function returning the DateTime tuple in a TDGargs list.
-get_datetime_from_tdg_args([DateTime|_]) ->
- DateTime.
-%% ------------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Various help functions.
-%% =============================================================================
-
-%% -----------------------------------------------------------------------------
-%% Functions handling set trace-pattern options.
-%% -----------------------------------------------------------------------------
-
-%% Gets additional arguments given to various configurable functions.
-%% Returns a list.
-get_ownarg_opts(Opts) ->
- case lists:keysearch(arg,1,Opts) of
- {value,{_,OwnArg}} when is_list(OwnArg) ->
- OwnArg;
- {value,{_,OwnArg}} ->
- [OwnArg];
- false ->
- []
- end.
-%% -----------------------------------------------------------------------------
-
-get_disable_safety_opts(Opts) ->
- case lists:member(disable_safety,Opts) of
- true ->
- true;
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-get_expand_regexp_at_opts(Opts) ->
- case lists:keysearch(expand_only_at,1,Opts) of
- {value,{_,Node}} when is_atom(Node) ->
- {ok,Node};
- _ ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Functions for the internal debugging system.
-%% =============================================================================
-
-%% The debug system is meant to provide tracing of inviso tool at different levels.
-%%
-%% debug(What,Level,Description) -> nothing significant.
-%% What : controls what kind of event. This can both be certain parts of the tool
-%% as well as certain levels (info to catastrophy).
-%% Level: Determines if What shall be printed or not.
-%% Description: this is what happend.
-debug(_What,?DBG_OFF,_Description) ->
- true; % Debug is off, no action.
-debug(What,On,Description) ->
- debug_2(What,On,Description).
-
-debug_2(What,_,Description) ->
- io:format("INVISO DEBUG:~w, ~p~n",[What,Description]).
-%% -----------------------------------------------------------------------------
diff --git a/lib/inviso/src/inviso_tool_sh.erl b/lib/inviso/src/inviso_tool_sh.erl
deleted file mode 100644
index b02f498c5b..0000000000
--- a/lib/inviso/src/inviso_tool_sh.erl
+++ /dev/null
@@ -1,1749 +0,0 @@
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2011. 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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%% Description:
-%% The runtime component of the trace tool Inviso.
-%%
-%% Authors:
-%% Lennart �hman, [email protected]
-%% -----------------------------------------------------------------------------
-
--module(inviso_tool_sh).
-
-%% Inviso Session Handler.
-%% This is the code for the session handler process. Its purpose is that we have
-%% one session handler process for each trace session started through the
-%% start_session inviso tool API. The session handler process is responsible for:
-%%
-%% -Knowing the state/status of all participating runtime components.
-%% -Keeping storage of all tracerdata all our participants have used. This means
-%% also to find out the tracerdata of runtime components connecting by them
-%% selves.
-%%
-%% STORAGE STRATEGY
-%% ----------------
-%% The local information storage can be changed by two things. Either by executing
-%% commands issued through our APIs. Or by receiving trace_event from the control
-%% component. When we execute commands, a corresponding event will also follow.
-%% Meaning that in those situations we are informed twice.
-%% A simple strategy could be to wait for the event even when doing the changes
-%% to the runtime components our self (through commands). But that may result in
-%% a small time frame where someone might do yet another command and failing
-%% because the local information storage is not uptodate as it would have been
-%% expected to be. Therefore we always update the local storage when making changes
-%% to a runtime component our selves. There will eventually be a double update
-%% through an incoming event. But the storage must coop with that, preventing
-%% inconsitancies to happend. An example of a strategy is that the tracerdata table
-%% is a bag, not allowing for double entries of the same kind. Therefore a double
-%% update is harmless there.
-
-%% ------------------------------------------------------------------------------
-%% Module wide constants.
-%% ------------------------------------------------------------------------------
--define(LOCAL_RUNTIME,local_runtime). % Used as node name when non-disitrbuted.
--define(TRACING,tracing). % A state defined by the control component.
--define(RUNNING,running). % A status according to control componet.
-
--define(COPY_LOG_FROM,copy_log_from). % Common fileystem option.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% API exports.
-%% ------------------------------------------------------------------------------
--export([start_link/5,start_link/8]).
--export([cancel_session/1,stop_session/3]).
--export([reactivate/1,reactivate/2]).
--export([ctpl/5,tpl/5,tpl/6,tpl/7,
- tf/2,tf/3,
- tpm_localnames/2,init_tpm/6,init_tpm/9,tpm/6,tpm/7,tpm/10,
- tpm_ms/7,ctpm_ms/6,ctpm/5
- ]).
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Internal exports.
-%% ------------------------------------------------------------------------------
--export([init/1,handle_call/3,handle_info/2,terminate/2]).
-
--export([get_loopdata/1]).
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Includes.
-%% ------------------------------------------------------------------------------
--include_lib("kernel/include/file.hrl"). % Necessary for file module.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Exported API functions.
-%% ==============================================================================
-
-%% start_link(From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,NodesIn,NodesNotIn) =
-%% {ok,Pid} | {error,Reason}
-%% From= pid(), the initial client expecting the reply.
-%% NodeParams=[{Node,TracerData},{Node,TracerData,Opts}...]
-%% CtrlNode=atom() | 'void', the node where the trace control component is.
-%% CtrlPid=pid(), the pid of the trace control component.
-%% SafetyCatches=
-%% Dir=string(), where to place fetched logs and the merged log.
-%% Dbg=debug structure.
-%% NodesIn=[Node,...], list of nodes already in another session.
-%% NodesNotIn=[Node,...], list of nodes not in another session.
-%%
-%% Starts a session-handler. It keeps track of the the state and status of all
-%% participating runtime components. Note that there is a non-distributed case too.
-%% In the non-distributed case there is no things such as CtrlNode.
-start_link(From,TracerData,CtrlPid,SafetyCatches,Dbg) ->
- gen_server:start_link(?MODULE,
- {self(),From,TracerData,CtrlPid,SafetyCatches,Dbg},
- []).
-
-start_link(From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,Dbg,NodesIn,NodesNotIn) ->
- gen_server:start_link(?MODULE,
- {self(),From,NodeParams,CtrlNode,CtrlPid,
- SafetyCatches,Dbg,NodesIn,NodesNotIn},
- []).
-%% ------------------------------------------------------------------------------
-
-%% Stops tracing where it is ongoing. Fetches all logfiles.
-stop_session(SID,Dir,Prefix) ->
- gen_server:call(SID,{stop_session,Dir,Prefix}).
-%% ------------------------------------------------------------------------------
-
-%% stop_session(SID) = ok
-%%
-%% Cancels the session brutaly. All runtime components are made to stop tracing,
-%% all local log files are removed using the tracerdata we know for them.
-cancel_session(SID) ->
- gen_server:call(SID,cancel_session).
-%% ------------------------------------------------------------------------------
-
-%% reactivate(SID) = {ok,
-%% reactivate(SID,Nodes) = {ok,NodeResults} | {error,Reason}.
-%% SID=session id, pid().
-%% Nodes=[Node,...]
-%% NodeResult=[{Node,Result},...]
-%% Result={Good,Bad}
-%% Good,Bad=integer(), the number of redone activities.
-%%
-%% Function which reactivates runtime components being suspended. This is done
-%% replaying all trace flags (in the correct order) to the corresponding nodes.
-%% Note that this may also mean turning flags off. Like first turning them on
-%% then off a split second later.
-reactivate(SID) ->
- gen_server:call(SID,reactivate). %% NOT IMPLEMENTED YET.
-reactivate(SID,Nodes) ->
- gen_server:call(SID,{reactivate,Nodes}).
-%% ------------------------------------------------------------------------------
-
-
-%% tpl(SessionID,Mod,Func,Arity,MS)=
-%% tpl(SessionID,Mod,Func,Arity,MS,Opts)={ok,N}|{error,Reason}.
-%% tpl(SessionID,Nodes,Mod,Func,Arity,MS)=
-%% tpl(SessionID,Nodes,Mod,Func,Arity,MS,Opts)={ok,Result}|{error,Reason}
-%% Mod='_' | ModuleName | ModRegExp | {DirRegExp,ModRegExp}
-%% ModRegExp=DirRegExp= string()
-%% Func='_' | FunctionName
-%% Arity='_' | integer()
-%% MS=[] | false | a match specification
-%% Opts=[Opts,...]
-%% Opt={arg,Arg}, disable_safety, {expand_regexp_at,NodeName}, only_loaded
-%% Nodes=[NodeName,...]
-tpl(SID,Mod,Func,Arity,MS) ->
- gen_server:call(SID,{tp,tpl,Mod,Func,Arity,MS,[]}).
-tpl(SID,Mod,Func,Arity,MS,Opts) when list(MS);MS==true;MS==false ->
- gen_server:call(SID,{tp,tpl,Mod,Func,Arity,MS,Opts});
-tpl(SID,Nodes,Mod,Func,Arity,MS) when integer(Arity);Arity=='_' ->
- gen_server:call(SID,{tp,tpl,Nodes,Mod,Func,Arity,MS,[]}).
-tpl(SID,Nodes,Mod,Func,Arity,MS,Opts) ->
- gen_server:call(SID,{tp,tpl,Nodes,Mod,Func,Arity,MS,Opts}).
-%% ------------------------------------------------------------------------------
-
-%% ctpl(SessionID,Nodes,Mod,Func,Arity)=
-%% See tpl/X for arguments.
-%%
-%% Removes local trace-patterns from functions.
-ctpl(SID,Nodes,Mod,Func,Arity) ->
- gen_server:call(SID,{ctp,ctpl,Nodes,Mod,Func,Arity}).
-%% ------------------------------------------------------------------------------
-
-
-tpm_localnames(SID,Nodes) ->
- gen_server:call(SID,{tpm_localnames,Nodes}).
-
-%% tpm_globalnames(SID,Nodes) ->
-%% gen_server:call(SID,{tpm_globalnames,Nodes}).
-
-init_tpm(SID,Nodes,Mod,Func,Arity,CallFunc) ->
- gen_server:call(SID,{init_tpm,Nodes,Mod,Func,Arity,CallFunc}).
-init_tpm(SID,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- gen_server:call(SID,
- {init_tpm,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc}).
-tpm(SID,Nodes,Mod,Func,Arity,MS) ->
- gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS}).
-tpm(SID,Nodes,Mod,Func,Arity,MS,CallFunc) ->
- gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS,CallFunc}).
-tpm(SID,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc}).
-
-tpm_ms(SID,Nodes,Mod,Func,Arity,MSname,MS) ->
- gen_server:call(SID,{tpm_ms,Nodes,Mod,Func,Arity,MSname,MS}).
-
-ctpm_ms(SID,Nodes,Mod,Func,Arity,MSname) ->
- gen_server:call(SID,{tpm_ms,Nodes,Mod,Func,Arity,MSname}).
-
-ctpm(SID,Nodes,Mod,Func,Arity) ->
- gen_server:call(SID,{ctpm,Nodes,Mod,Func,Arity}).
-%% ------------------------------------------------------------------------------
-
-
-%% tf(SessionID,Nodes,TraceConfList)=
-%% TraceConfList=[{PidSpec,Flags},...]
-%% PidSpec=pid()|atom()|all|new|existing
-%% Flags=[Flag,...]
-tf(SID,TraceConfList) ->
- gen_server:call(SID,{tf,TraceConfList}).
-tf(SID,Nodes,TraceConfList) ->
- gen_server:call(SID,{tf,Nodes,TraceConfList}).
-%% ------------------------------------------------------------------------------
-
-
-get_loopdata(SID) ->
- gen_server:call(SID,get_loopdata).
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Genserver call-backs.
-%% ==============================================================================
-
-%% Initial function for the session handler process. The nodes participating in
-%% the session must previously have been added to our control component by the tool.
-%% The session handler first finds out the state/status of the specified runtime
-%% components, then it tries to initiate tracing on those where it is applicable.
-%% Note that a reply to the initial (tool)client is done from here instead from
-%% the tool-server.
-init({Parent,From,TracerData,CtrlPid,SafetyCatches,Dbg}) -> % The non-distributed case.
- {ok,StateStatus}=init_rtcomponent_states([],void,CtrlPid,[?LOCAL_RUNTIME]),
- case is_tool_internal_tracerdata(TracerData) of
- false -> % We shall initiate local runtime.
- case inviso:init_tracing(TracerData) of
- ok ->
- gen_server:reply(From,{ok,{self(),ok}}),
- {ok,mk_ld(Parent,
- void,
- CtrlPid,
- to_rtstates([{?LOCAL_RUNTIME,{tracing,?RUNNING},[]}]),
- [{?LOCAL_RUNTIME,TracerData}],
- [],
- SafetyCatches,
- Dbg)};
- {error,Reason} -> % It might have become suspended?!
- gen_server:reply(From,{error,Reason}),
- {ok,mk_ld(Parent,
- void,
- CtrlPid,
- to_rtstates([{?LOCAL_RUNTIME,StateStatus,[]}]),
- [{?LOCAL_RUNTIME,TracerData}],
- [],
- SafetyCatches,
- Dbg)}
- end;
- true -> % We shall not pass this one on.
- gen_server:reply(From,{ok,{self(),ok}}), % Then it is ok.
- {ok,mk_ld(Parent,
- void,
- CtrlPid,
- to_rtstates([{?LOCAL_RUNTIME,StateStatus,[]}]),
- [],
- [?LOCAL_RUNTIME],
- SafetyCatches,
- Dbg)}
- end;
-init({Parent,From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,Dbg,NodesIn,NodesNotIn}) ->
- case init_rtcomponent_states(NodeParams,CtrlNode,CtrlPid,NodesNotIn) of
- {ok,States} -> % A list of {Node,{State,Status},Opts}.
- {NodeParams2,Nodes2}=remove_nodeparams(NodesIn,NodeParams),
- case inviso_tool_lib:inviso_cmd(CtrlNode,init_tracing,[NodeParams2]) of
- {ok,Result} -> % Resulted in state changes!
- RTStates=set_tracing_rtstates(to_rtstates(States),Result),
- ReplyValue=init_fix_resultnodes(NodesIn,Nodes2,Result),
- gen_server:reply(From,{ok,{self(),ReplyValue}}),
- {ok,mk_ld(Parent,CtrlNode,CtrlPid,RTStates,
- NodeParams2,Nodes2,SafetyCatches,Dbg)};
- {error,Reason} -> % Some general failure.
- inviso_tool_lib:inviso_cmd(CtrlNode,unsubscribe,[]),
- gen_server:reply(From,{error,{init_tracing,Reason}}),
- {stop,{init_tracing,Reason}};
- What ->
- io:format("GOT:~n~w~n",[What]),
- exit(foo)
- end;
- {error,Reason} -> % Unable to get the state/status.
- inviso_tool_lib:inviso_cmd(CtrlNode,unsubscribe,[]),
- gen_server:reply(From,{error,Reason}),
- {stop,{error,Reason}};
- What ->
- io:format("GOT:~n~w~n",[What]),
- exit(foo)
- end.
-%% ------------------------------------------------------------------------------
-
-%% To stop a session means stop the tracing and remove all local files on the
-%% runtime nodes. We do have a table with all tracer data and that is how we are
-%% going to recreate what files to remove.
-%% Since runtime components may actually change state when this procedure is
-%% on-going, we do not care! It is the state in the session handling process at
-%% the time of start of this procedure which is used.
-handle_call(cancel_session,_From,LD) ->
- CtrlNode=get_ctrlnode_ld(LD),
- RTStates=get_rtstates_ld(LD),
- Dbg=get_dbg_ld(LD),
- TracingNodes=get_all_tracing_nodes_rtstates(RTStates),
- case stop_all_tracing(CtrlNode,Dbg,TracingNodes) of
- ok-> % Hopefully all nodes are stopped now.
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- TRDstorage=get_trdstorage_ld(LD),
- remove_all_local_logs(CtrlNode,TRDstorage,AvailableNodes,Dbg),
- {stop,normal,ok,LD}; % LD actually not correct now!
- {error,Reason} -> % Some serious error when stop_tracing.
- {stop,normal,{error,Reason},LD}
- end;
-%% ------------------------------------------------------------------------------
-
-%% *Stop all tracing on runtime components still tracing.
-%% *Copy all local log files to the collection directory.
-handle_call({stop_session,Dir,Prefix},_From,LD) ->
- case check_directory_exists(Dir) of % Check that this directory exists here.
- true ->
- RTStates=get_rtstates_ld(LD),
- CtrlNode=get_ctrlnode_ld(LD),
- Dbg=get_dbg_ld(LD),
- TracingNodes=get_all_tracing_nodes_rtstates(RTStates),
- case stop_all_tracing(CtrlNode,Dbg,TracingNodes) of
- ok -> % Hopefully no node is still tracing now.
- TRDstorage=get_trdstorage_ld(LD),
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- {FailedNodes,FetchedFiles}=
- transfer_logfiles(RTStates,CtrlNode,Dir,Prefix,
- TRDstorage,Dbg,AvailableNodes),
- RemoveNodes= % We only delete local logs where fetch ok.
- lists:filter(fun(N)->
- case lists:keysearch(N,1,FailedNodes) of
- {value,_} ->
- false;
- false ->
- true
- end
- end,
- AvailableNodes),
- remove_all_local_logs(CtrlNode,TRDstorage,RemoveNodes,Dbg),
- {stop,normal,{ok,{FailedNodes,FetchedFiles}},LD};
- {error,Reason} -> % Some general failure, quit.
- {stop,normal,{error,Reason},LD}
- end;
- false -> % You specified a non-existing directory!
- {reply,{error,{faulty_dir,Dir}},LD}
- end;
-%% ------------------------------------------------------------------------------
-
-handle_call({reactivate,Nodes},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- {OurNodes,OtherNodes}=
- remove_nodes_not_ours(Nodes,get_all_session_nodes_rtstates(RTStates)),
- CtrlNode=get_ctrlnode_ld(LD),
- ACTstorage=get_actstorage_ld(LD),
- case h_reactivate(CtrlNode,OurNodes,ACTstorage) of
- {ok,Results} -> % A list of {Node,Result}.
- if
- OtherNodes==[] -> % Normal case, no non-session nodes.
- {reply,{ok,Results},LD};
- true -> % Add error values for non-session nodes.
- {reply,
- {ok,
- lists:map(fun(N)->{N,{error,not_in_session}} end,OtherNodes)++
- Results},
- LD}
- end;
- {error,Reason} -> % Then this error takes presidence.
- {reply,{error,Reason},LD}
- end;
-%% ------------------------------------------------------------------------------
-
-%% Call-back for set trace-pattern for both global and local functions.
-handle_call({tp,PatternFunc,Mod,F,A,MS,Opts},_From,LD) ->
- Reply=h_tp(all,PatternFunc,Mod,F,A,MS,Opts,LD), % For all active nodes in the session.
- {reply,Reply,LD};
-handle_call({tp,PatternFunc,Nodes,Mod,F,A,MS,Opts},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- SNodes=get_all_session_nodes_rtstates(RTStates), % Notes belongoing to the session.
- {Nodes2,FaultyNodes}=remove_nodes_not_ours(Nodes,SNodes),
- Reply=h_tp(Nodes2,PatternFunc,Mod,F,A,MS,Opts,LD),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,FaultyNodes),
- {reply,ErrorReply++Reply,LD};
-%% ------------------------------------------------------------------------------
-
-%% Call-back handling the removal of both local and global trace-patterns.
-%% NOT IMPLEMENTED YET.
-handle_call({ctp,PatternFunc,Nodes,Mod,F,A},_From,LD) ->
- Reply=h_ctp(Nodes,PatternFunc,Mod,F,A,LD),
- {reply,Reply,LD};
-%% ------------------------------------------------------------------------------
-
-handle_call({tpm_localnames,Nodes},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_tpm_localnames(get_ctrlnode_ld(LD),Nodes2,RTStates,ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({init_tpm,Nodes,Mod,Func,Arity,CallFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- init_tpm,
- [Mod,Func,Arity,CallFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({init_tpm,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- init_tpm,
- [Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm,Nodes,Mod,Func,Arity,MS},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),Nodes2,tpm,[Mod,Func,Arity,MS],RTStates,ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm,Nodes,Mod,Func,Arity,MS,CallFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- tpm,
- [Mod,Func,Arity,MS,CallFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- tpm,
- [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm_ms,Nodes,Mod,Func,Arity,MSname,MS},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- tpm_ms,
- [Mod,Func,Arity,MSname,MS],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({ctpm_ms,Nodes,Mod,Func,Arity,MSname},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- ctpm_ms,
- [Mod,Func,Arity,MSname],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({ctpm,Nodes,Mod,Func,Arity},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),Nodes2,ctpm,[Mod,Func,Arity],RTStates,ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-%% ------------------------------------------------------------------------------
-
-%% Call-back for setting process trace-flags. Handles both distributed and non-
-%% distributed case.
-handle_call({tf,TraceConfList},From,LD) ->
- handle_call({tf,all,TraceConfList},From,LD);
-handle_call({tf,Nodes,TraceConfList},_From,LD) ->
- {Reply,NewACTstorage}=h_tf(get_ctrlnode_ld(LD),
- Nodes,
- TraceConfList,
- get_actstorage_ld(LD),
- get_rtstates_ld(LD)),
- {reply,Reply,put_actstorage_ld(NewACTstorage,LD)};
-%% ------------------------------------------------------------------------------
-
-
-
-handle_call(get_loopdata,_From,LD) ->
- io:format("The loopdata:~n~p~n",[LD]),
- {reply,ok,LD}.
-%% ------------------------------------------------------------------------------
-
-
-%% Clause handling an incomming state-change event from the control component.
-%% Note that it does not have to be one of our nodes since it is not possible
-%% to subscribe to certain node-events.
-%% We may very well get state-change events for state-changes we are the source
-%% to our selves. Those state-changes are already incorporated into the RTStates.
-%% There is however no harm in doing them again since we know that this event
-%% message will reach us before a reply to a potentially following state-change
-%% request will reach us. Hence we will do all state-changes in the correct order,
-%% even if sometimes done twice.
-handle_info({trace_event,CtrlPid,_Time,{state_change,Node,{State,Status}}},LD) ->
- case get_ctrlpid_ld(LD) of
- CtrlPid -> % It is from our control component.
- case {State,Status} of
- {?TRACING,?RUNNING} -> % This is the only case when new tracerdata!
- NewTracerData=add_current_tracerdata_ld(get_ctrlnode_ld(LD),
- Node,
- get_rtstates_ld(LD),
- get_trdstorage_ld(LD)),
- NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
- {noreply,put_trdstorage_ld(NewTracerData,
- put_rtstates_ld(NewRTStates,LD))};
- _ -> % In all other cases, just fix rtstates.
- NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
- {noreply,put_rtstates_ld(NewRTStates,LD)}
- end;
- _ ->
- {noreply,LD}
- end;
-%% If a new runtime component connects to our trace control component, and it is
-%% in our list of runtime components belonging to this session, we may update its
-%% state to now being present. Otherwise it does not belong to this session.
-%% Note that we avoid updating an already connected runtime component. This
-%% can happend if it connected by itself after we started the session handler,
-%% but before we managed to initiate tracing. Doing so or not will not result in
-%% any error in the long run, but during a short period of time we might be
-%% prevented from doing things with the runtime though it actually is tracing.
-handle_info({trace_event,CtrlPid,_Time,{connected,Node,{_Tag,{State,Status}}}},LD) ->
- case get_ctrlpid_ld(LD) of
- CtrlPid -> % It is from our control component.
- case get_statestatus_rtstates(Node,get_rtstates_ld(LD)) of
- {ok,unavailable} -> % This is the situation when we update!
- NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
- {noreply,put_rtstates_ld(NewRTStates,LD)};
- _ -> % In all other cases, let it be.
- {noreply,LD}
- end;
- _ -> % Not from our control component.
- {noreply,LD}
- end;
-%% If a runtime component disconnects we mark it as unavailable. We must also
-%% remove all saved trace-flags in order for them to not be accidently reactivated
-%% should the runtime component reconnect and then suspend.
-handle_info({trace_event,CtrlPid,_Time,{disconnected,Node,_}},LD) ->
- case get_ctrlpid_ld(LD) of
- CtrlPid -> % It is from our control component.
- NewRTStates=set_unavailable_rtstates(Node,get_rtstates_ld(LD)),
- NewACTstorage=del_node_actstorage(Node,get_actstorage_ld(LD)),
- {noreply,put_actstorage_ld(NewACTstorage,put_rtstates_ld(NewRTStates,LD))};
- _ ->
- {noreply,LD}
- end;
-handle_info(_,LD) ->
- {noreply,LD}.
-%% ------------------------------------------------------------------------------
-
-%% In terminate we cancel our subscription to event from the trace control component.
-%% That should actually not be necessary, but lets do it the correct way!
-terminate(_,LD) ->
- case get_ctrlnode_ld(LD) of
- void -> % Non-distributed.
- inviso:unsubscribe();
- Node ->
- inviso_tool_lib:inviso_cmd(Node,unsubscribe,[])
- end.
-%% ------------------------------------------------------------------------------
-
-
-
-%% ==============================================================================
-%% First level help functions to call-backs.
-%% ==============================================================================
-
-%% ------------------------------------------------------------------------------
-%% Help functions to init.
-%% ------------------------------------------------------------------------------
-
-%% Help function which find out the state/status of the runtime components.
-%% Note that since we have just started subscribe to state changes we must
-%% check our inqueue to see that we have no waiting messages for the nodes
-%% we learned the state/status of. If there is a waiting message we don't
-%% know whether that was a state change received before or after the state
-%% check was done. We will then redo the state-check.
-%% Returns {ok,States} or {error,Reason}.
-%% Where States is [{Node,{State,Status},Opts},...].
-%% Note that {error,Reason} can not occur in the non-distributed case.
-init_rtcomponent_states(NodeParams,void,CtrlPid,Nodes) -> % The non-distributed case.
- ok=inviso:subscribe(),
- init_rtcomponent_states_2(NodeParams,void,CtrlPid,Nodes,[]);
-init_rtcomponent_states(NodeParams,CtrlNode,CtrlPid,Nodes) ->
- ok=inviso_tool_lib:inviso_cmd(CtrlNode,subscribe,[]),
- init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,Nodes,[]).
-
-init_rtcomponent_states_2(_,_,_,[],States) ->
- {ok,States};
-init_rtcomponent_states_2(NodeParams,void,CtrlPid,_Nodes,States) ->
- case inviso:get_status() of
- {ok,StateStatus} -> % Got its state/status, now...
- {ProblemNodes,NewStates}=
- init_rtcomponent_states_3(NodeParams,CtrlPid,[{?LOCAL_RUNTIME,{ok,StateStatus}}],
- [],States),
- init_rtcomponent_states_2(NodeParams,void,CtrlPid,ProblemNodes,NewStates);
- {error,_Reason} -> % The runtime is not available!?
- {ok,[{?LOCAL_RUNTIME,unavailable,[]}]} % Create the return value immediately.
- end;
-init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,Nodes,States) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,get_status,[Nodes]) of
- {ok,NodeResult} ->
- {ProblemNodes,NewStates}=
- init_rtcomponent_states_3(NodeParams,CtrlPid,NodeResult,[],States),
- init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,ProblemNodes,NewStates);
- {error,Reason} -> % Severe problem, abort the session.
- {error,{get_status,Reason}}
- end.
-
-%% Traverses the list of returnvalues and checks that we do not have an event
-%% waiting in the message queue. If we do have, it is a problem. That node will
-%% be asked about its state again.
-%% Note that it is here we construct the RTStatesList.
-init_rtcomponent_states_3(NodeParams,CtrlPid,[{Node,{ok,{State,Status}}}|Rest],Problems,States) ->
- receive
- {trace_event,CtrlPid,_Time,{state_change,Node,_}} ->
- init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,[Node|Problems],States)
- after
- 0 -> % Not in msg queue, then we're safe!
- RTState=case lists:keysearch(Node,1,NodeParams) of
- {value,{_Node,_TracerData,Opts}} ->
- {Node,{State,Status},Opts};
- _ -> % No option available, use [].
- {Node,{State,Status},[]}
- end,
- init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,Problems,[RTState|States])
- end;
-init_rtcomponent_states_3(NodeParams,CtrlPid,[{Node,{error,_Reason}}|Rest],Problems,States) ->
- RTState=case lists:keysearch(Node,1,NodeParams) of
- {value,{_Node,_TracerData,Opts}} ->
- {Node,unavailable,Opts};
- _ -> % No option available, use [].
- {Node,unavailable,[]}
- end,
- init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,Problems,[RTState|States]);
-init_rtcomponent_states_3(_,_,[],Problems,States) ->
- {Problems,States}.
-%% ------------------------------------------------------------------------------
-
-%% Help function removing nodes from NodeParams. The reason for this can either
-%% be that we are using a tool internal tracerdata that shall not be forwarded to
-%% the trace control component, or that the node is actually already part of
-%% another session.
-%% Returns {NewNodeParams,NodesWhichShallNotBeInitiated}.
-remove_nodeparams(Nodes,NodesParams) ->
- remove_nodeparams_2(Nodes,NodesParams,[],[]).
-
-remove_nodeparams_2(Nodes,[NodeParam|Rest],NPAcc,NAcc) when % NPAcc=NodeParamsAcc.
- (is_tuple(NodeParam) and ((size(NodeParam)==2) or (size(NodeParam)==3))) ->
- Node=element(1,NodeParam),
- Params=element(2,NodeParam), % This is tracerdata!
- case lists:member(Node,Nodes) of
- true -> % Remove this one, in another session.
- remove_nodeparams_2(Nodes,Rest,NPAcc,NAcc);
- false -> % Ok so far...
- case is_tool_internal_tracerdata(Params) of
- false -> % Then keep it and use it later!
- remove_nodeparams_2(Nodes,Rest,[{Node,Params}|NPAcc],NAcc);
- true -> % Since it is, remove it from the list.
- remove_nodeparams_2(Nodes,Rest,NPAcc,[Node|NAcc])
- end
- end;
-remove_nodeparams_2(Nodes,[_|Rest],NPAcc,NAcc) -> % Faulty NodeParam, skip it!
- remove_nodeparams_2(Nodes,Rest,NPAcc,NAcc);
-remove_nodeparams_2(_,[],NPAcc,NAcc) ->
- {lists:reverse(NPAcc),NAcc}.
-%% ------------------------------------------------------------------------------
-
-%% Help function which adds both the nodes which were already part of another
-%% session and the nodes that we actually did not issue any init_tracing for.
-%% Returns a new Result list of [{Node,NodeResult},...].
-init_fix_resultnodes(NodesOtherSes,NodesNotInit,Result) ->
- NewResult=init_fix_resultnodes_2(NodesOtherSes,{error,in_other_session},Result),
- init_fix_resultnodes_2(NodesNotInit,ok,NewResult).
-
-init_fix_resultnodes_2([Node|Rest],NodeResult,Result) ->
- [{Node,NodeResult}|init_fix_resultnodes_2(Rest,NodeResult,Result)];
-init_fix_resultnodes_2([],_,Result) ->
- Result. % Append Result to the end of the list.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions to reactivate.
-%% ------------------------------------------------------------------------------
-
-h_reactivate(CtrlNode,Nodes,ACTstorage) -> % Distributed case.
- case inviso_tool_lib:inviso_cmd(CtrlNode,cancel_suspension,[Nodes]) of
- {ok,CSuspResults} ->
- {GoodNodes,BadResults}= % Sort out nodes no longer suspended.
- lists:foldl(fun({Node,ok},{GoodNs,BadNs})->
- {[Node|GoodNs],BadNs};
- ({Node,{error,Reason}},{GoodNs,BadNs})->
- {GoodNs,[{Node,{error,{cancel_suspension,Reason}}}|BadNs]}
- end,
- {[],[]},
- CSuspResults),
- Results=h_reactivate_redo_activity(CtrlNode,GoodNodes,ACTstorage,[]),
- {ok,BadResults++Results};
- {error,Reason} -> % General failure cancelling suspend.
- {error,{cancel_suspension,Reason}}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function which traverses the list of nodes known to be ours and have
-%% cancelled their suspend. If we fail redoing one of the activities associated
-%% with a node, the node will be reported in the return value as failed. From
-%% that point on its state must be considered unknown since we do not know how
-%% many of the activities were successfully redone.
-h_reactivate_redo_activity(CtrlNode,[Node|Rest],ACTstorage,Acc) ->
- case get_activities_actstorage(Node,ACTstorage) of
- {ok,Activities} -> % The node existed in activity storage.
- {Good,Bad}=h_reactivate_redo_activity_2(CtrlNode,Node,Activities,0,0),
- h_reactivate_redo_activity(CtrlNode,Rest,ACTstorage,[{Node,{Good,Bad}}|Acc]);
- false -> % Node not present in activity storage.
- h_reactivate_redo_activity(CtrlNode,Rest,ACTstorage,[{Node,{0,0}}|Acc])
- end;
-h_reactivate_redo_activity(_CtrlNode,[],_,Acc) ->
- lists:reverse(Acc).
-
-%% Help function actually redoing the activity. Note that there must be one
-%% clause here for every type of activity.
-%% Returns {NrGoodCmds,NrBadCmds}.
-%% The number of good or bad commands refers to inviso commands done. If any
-%% of the subparts of such a command returned an error, the command is concidered
-%% no good.
-h_reactivate_redo_activity_2(CtrlNode,Node,[{tf,{Op,TraceConfList}}|Rest],Good,Bad) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,Op,[[Node],TraceConfList]) of
- {ok,[{_Node,{ok,Answers}}]} ->
- case h_reactivate_redo_activity_check_tf(Answers) of
- ok ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good+1,Bad);
- error -> % At least oneReports the first encountered error.
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
- end;
- {ok,[{_Node,{error,_Reason}}]} ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1);
- {error,_Reason} -> % General error when doing cmd.
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
- end;
-h_reactivate_redo_activity_2(CtrlNode,Node,[{tpm,{Op,InvisoCmdParams}}|Rest],Good,Bad) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,Op,[[Node]|InvisoCmdParams]) of
- {ok,[{_Node,ok}]} ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good+1,Bad);
- {ok,[{_Node,{error,_Reason}}]} ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1);
- {error,_Reason} -> % General error when doing cmd.
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
- end;
-h_reactivate_redo_activity_2(_CtrlNode,_Node,[],Good,Bad) ->
- {Good,Bad}.
-
-%% Help function traversing a list of results from inviso:tf/2 or inviso:ctf/2
-%% to see if there were any errors.
-h_reactivate_redo_activity_check_tf([N|Rest]) when integer(N) ->
- h_reactivate_redo_activity_check_tf(Rest);
-h_reactivate_redo_activity_check_tf([{error,_Reason}|_]) ->
- error;
-h_reactivate_redo_activity_check_tf([]) ->
- ok.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions to tp (setting trace patterns, both local and global).
-%% ------------------------------------------------------------------------------
-
-%% Help function which handles both tpl and tp. Note that the non-distributed case
-%% handled with Nodes='all'.
-%% Returns what shall be the reply to the client.
-h_tp(all,PatternFunc,Mod,F,A,MS,Opts,LD) -> % All available runtime nodes.
- Nodes=get_all_available_nodes_rtstates(get_rtstates_ld(LD)),
- h_tp(Nodes,PatternFunc,Mod,F,A,MS,Opts,LD);
-h_tp(Nodes,PatternFunc,Mod,F,A,MS,Opts,LD) -> % Only certain nodes in the session.
- CtrlNode=get_ctrlnode_ld(LD),
- Dbg=get_dbg_ld(LD),
- SafetyCatches=get_safetycatches_ld(LD),
- case inviso_tool_lib:expand_module_names(Nodes,Mod,Opts) of % Take care of any reg-exps.
- {multinode_expansion,NodeMods} ->
- NodeTPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,NodeMods,F,A,MS),
- h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,NodeTPs,[]);
- {singlenode_expansion,Modules} ->
- TPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,Modules,F,A,MS),
- h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg);
- module ->
- TPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,[Mod],F,A,MS),
- h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg);
- wildcard -> % Means do for all modules, no safety.
- h_tp_do_tps(CtrlNode,Nodes,[{Mod,F,A,MS}],PatternFunc,Dbg);
- {error,Reason} ->
- {error,Reason}
- end.
-
-%% Note that this function can never be called in the non-distributed case.
-h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,[{Node,TPs}|Rest],Accum) ->
- case h_tp_do_tps(CtrlNode,[Node],TPs,PatternFunc,Dbg) of
- {ok,[{Node,Result}]} ->
- h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,Rest,[{Node,Result}|Accum]);
- {error,Reason} -> % Failure, but don't stop.
- h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,Rest,[{Node,{error,Reason}}|Accum])
- end;
-h_tp_node_by_node(_,_,_,[],Accum) ->
- {ok,lists:reverse(Accum)}.
-
-%% Help function which does the actual call to the trace control component.
-%% Note that Nodes can be a list of nodes (including a single one) or
-%% ?LOCAL_RUNTIME if we are not distributed. The non-distributed case is otherwise
-%% detected by the 'void' CtrlNode.
-%% Returns {ok,[{Node,{ok,{NrOfFunctions,NrOfErrors}}},{Node,{error,Reason}},...]} or
-%% {error,Reason}. In the non-distributed case {ok,{NrOfFunctions,NrOfErros}} or
-%% {error,Reason}.
-h_tp_do_tps(void,_Nodes,TPs,PatternFunc,Dbg) -> % Non distributed case!
- inviso_tool_lib:debug(tp,Dbg,[TPs,PatternFunc]),
- case inviso:PatternFunc(TPs) of
- {ok,Result} -> % A list of [Nr1,Nr2,error,...].
- {ok,
- lists:foldl(fun(N,{AccNr,AccErr}) when integer(N) ->
- {AccNr+N,AccErr};
- (error,{AccNr,AccErr}) ->
- {AccNr,AccErr+1}
- end,
- {0,0},
- Result)};
- {error,Reason} ->
- {error,{PatternFunc,Reason}}
- end;
-h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg) ->
- inviso_tool_lib:debug(tp,Dbg,[Nodes,TPs,PatternFunc]),
- case inviso_tool_lib:inviso_cmd(CtrlNode,PatternFunc,[Nodes,TPs]) of
- {ok,Result} -> % Result is [{Node,Result},...].
- {ok,
- lists:map(fun({Node,{ok,Res}})->
- {Node,lists:foldl(fun(N,{ok,{AccNr,AccErr}}) when integer(N) ->
- {ok,{AccNr+N,AccErr}};
- (error,{AccNr,AccErr}) ->
- {ok,{AccNr,AccErr+1}}
- end,
- {ok,{0,0}},
- Res)};
- ({_Node,{error,Reason}})->
- {error,Reason}
- end,
- Result)};
- {error,Reason} ->
- {error,{PatternFunc,Reason}}
- end.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Help functions for removing trace-patterns.
-%% ------------------------------------------------------------------------------
-
-%% NOT IMPLEMENTED YET.
-h_ctp(Node,PatternFunc,Mod,F,A,LD) ->
- tbd.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions for calling the trace information facility.
-%% ------------------------------------------------------------------------------
-
-
-%% Function handling the meta trace pattern for capturing registration of local
-%% process names.
-h_tpm_localnames(CtrlNode,Nodes,RTStates,ACTstorage) ->
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- {Nodes3,FaultyNodes}=remove_nodes_not_ours(Nodes,AvailableNodes),
- case inviso_tool_lib:inviso_cmd(CtrlNode,tpm_localnames,[Nodes3]) of
- {ok,Result} -> % That good we want to modify tpmstorage!
- NewACTstorage=add_tpm_actstorage(Result,tpm_localnames,[],ACTstorage),
- ErrorResult=lists:map(fun(N)->{N,{error,not_available}} end,FaultyNodes),
- {{ok,ErrorResult++Result},NewACTstorage};
- {error,Reason} -> % If general failure, do not modify storage.
- {{error,Reason},ACTstorage}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Functions calling meta trace functions for specified nodes. This function is
-%% intended for use with all tmp function calls, init_tpm,tpm,tpm_ms,ctpm_ms and
-%% ctpm.
-%% Note that we must store called meta trace functions and their parameters in the
-%% activity storage in order to be able to redo them in case of a reactivate.
-h_all_tpm(CtrlNode,Nodes,TpmCmd,InvisoCmdParams,RTStates,ACTstorage) ->
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- {Nodes3,FaultyNodes}=remove_nodes_not_ours(Nodes,AvailableNodes),
- case inviso_tool_lib:inviso_cmd(CtrlNode,TpmCmd,[Nodes3|InvisoCmdParams]) of
- {ok,Result} -> % That good we want to modify tpmstorage!
- NewACTstorage=add_tpm_actstorage(Result,TpmCmd,InvisoCmdParams,ACTstorage),
- ErrorResult=lists:map(fun(N)->{N,{error,not_available}} end,FaultyNodes),
- {{ok,ErrorResult++Result},NewACTstorage};
- {error,Reason} -> % If general failure, do not modify storage.
- {{error,Reason},ACTstorage}
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions for set trace flags.
-%% ------------------------------------------------------------------------------
-
-%% Help function which sets the tracepatterns in TraceConfList for all nodes
-%% mentioned in Nodes. Note that non-distributed case is handled with Nodes='all'.
-%% Returns {Reply,NewACTstorage} where Reply is whatever shall be returned to caller
-%% and NewACTstorage is traceflag storage modified with the flags added to the
-%% corresponding nodes.
-h_tf(void,_Nodes,TraceConfList,ACTstorage,_RTStates) -> % The non-distributed case.
- Reply=inviso:tf(TraceConfList),
- NewACTstorage=add_tf_actstorage([{?LOCAL_RUNTIME,Reply}],tf,TraceConfList,ACTstorage),
- {Reply,NewACTstorage};
-h_tf(CtrlNode,all,TraceConfList,ACTstorage,RTStates) ->
- AllNodes=get_all_session_nodes_rtstates(RTStates),
- h_tf(CtrlNode,AllNodes,TraceConfList,ACTstorage,RTStates);
-h_tf(CtrlNode,Nodes,TraceConfList,ACTstorage,_RTStates) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,tf,[Nodes,TraceConfList]) of
- {ok,Result} -> % That good we want to modify actstorage!
- NewACTstorage=add_tf_actstorage(Result,tf,TraceConfList,ACTstorage),
- {{ok,Result},NewACTstorage};
- {error,Reason} -> % If general failure, do not modify actstorage.
- {{error,Reason},ACTstorage}
- end.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Help functions to stop_session.
-%% ------------------------------------------------------------------------------
-
-%% This function fetches all local log-files using our stored tracerdata. Note
-%% that there are two major ways of tranfering logfiles. Either via distributed
-%% Erlang or by common filesystem (like NFS). The default is distributed Erlang.
-%% But there may be info in the RTStates structure about a common file-system.
-%% Returns {FailedNodes,FetchedFileNames} where FailedNodes is a list of
-%% nodenames where problems occurred. Note that problems does not necessarily
-%% mean that no files were copied.
-%% FetchedFileNames contains one or two of the tuples {trace_log,Files} and/or
-%% {ti_log,Files}, listing all files successfully fetched. Note that the
-%% list of fetched files contains sublists of filenames. One for each node and
-%% tracerdata.
-%% In the non-distributed system we always use copy (since the files always
-%% resides locally).
-transfer_logfiles(RTStates,CtrlNode,Dir,Prefix,TRDstorage,Dbg,AvailableNodes) ->
- if
- CtrlNode==void -> % When non-distributed, always copy!
- fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,[?LOCAL_RUNTIME]);
- true -> % The distributed case.
- {FetchNodes,CopyNodes}=find_logfile_transfer_methods(AvailableNodes,RTStates),
- {FailedFetchNodes,FetchedFiles}=
- case fetch_logfiles_distributed(CtrlNode,Dir,Prefix,TRDstorage,Dbg,FetchNodes) of
- {ok,Failed,Files} -> % So far no disasters.
- {Failed,Files};
- {error,Reason} -> % Means all fetch-nodes failed!
- inviso_tool_lib:debug(transfer_logfiles,Dbg,[FetchNodes,Reason]),
- {lists:map(fun(N)->{N,error} end,FetchNodes),[]}
- end,
- {FailedCopyNodes,CopiedFiles}=
- fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,CopyNodes),
- {FailedFetchNodes++FailedCopyNodes,FetchedFiles++CopiedFiles}
- end.
-
-%% Help function which finds out which node we have a common file system with
-%% and from which we must make distributed erlang tranfere.
-%% Returns {DistributedNodes,CopyNodes} where CopyNode is [{Node,CopyFromDir},...].
-find_logfile_transfer_methods(Nodes,RTStates) ->
- find_logfile_transfer_methods_2(Nodes,RTStates,[],[]).
-
-find_logfile_transfer_methods_2([Node|Rest],RTStates,FetchAcc,CopyAcc) ->
- {ok,Opts}=get_opts_rtstates(Node,RTStates), % Node must be in RTStates!
- case lists:keysearch(?COPY_LOG_FROM,1,Opts) of
- {value,{_,FromDir}} when list(FromDir) -> % Node has common filesystem.
- find_logfile_transfer_methods_2(Rest,RTStates,FetchAcc,[{Node,FromDir}|CopyAcc]);
- {value,_} -> % Can't understand dir option.
- find_logfile_transfer_methods_2(Rest,RTStates,[Node|FetchAcc],CopyAcc);
- false -> % Then we want to use fetch instead.
- find_logfile_transfer_methods_2(Rest,RTStates,[Node|FetchAcc],CopyAcc)
- end;
-find_logfile_transfer_methods_2([],_,FetchAcc,CopyAcc) ->
- {FetchAcc,CopyAcc}.
-%% ------------------------------------------------------------------------------
-
-%% Help function which transferes all local logfiles according to the tracerdata
-%% stored for the nodes in Nodes.
-%% Returns {ok,FailedNodes,FileNodeSpecs} or {error,Reason}.
-%% FailedNodes is a list of nodes where fetching logs did not succeed, partially
-%% or not at all.
-%% FileNames is a list of list of actually fetched files (the name as it is here, including
-%% Dir). The sublists are files which belong together.
-fetch_logfiles_distributed(CtrlNode,Dir,Prefix,TRDstorage,Dbg,Nodes) ->
- LogSpecList=build_logspeclist(Nodes,TRDstorage),
- case inviso_fetch_log(inviso_tool_lib:inviso_cmd(CtrlNode,
- fetch_log,
- [LogSpecList,Dir,Prefix])) of
- {ok,Result} ->
- Files=get_all_filenames_fetchlog_result(Result,Dbg),
- FailedNodes=get_all_failednodes_fetchlog_result(Result),
- {ok,FailedNodes,Files};
- {error,Reason} -> % Some general failure!
- {error,{fetch_log,Reason}}
- end.
-
-%% Help function which constructs a list {Node,TracerData} for all nodes in Nodes.
-%% Note that there may be more than one tracerdata for a node, resulting in multiple
-%% tuples for that node.
-build_logspeclist(Nodes,TRDstorage) ->
- build_logspeclist_2(Nodes,TRDstorage,[]).
-
-build_logspeclist_2([Node|Rest],TRDstorage,Acc) ->
- TRDlist=find_tracerdata_for_node_trd(Node,TRDstorage), % A list of all tracerdata.
- build_logspeclist_2(Rest,
- TRDstorage,
- [lists:map(fun(TRD)->{Node,TRD} end,TRDlist)|Acc]);
-build_logspeclist_2([],_,Acc) ->
- lists:flatten(Acc).
-
-%% Help function which translates inviso:fetch_log return values to what I
-%% want!
-inviso_fetch_log({error,Reason}) ->
- {error,Reason};
-inviso_fetch_log({_Success,ResultList}) ->
- {ok,ResultList}.
-
-%% Help function which collects all filenames mentioned in a noderesult structure.
-%% The files may or may not be complete.
-%% Returns a list of list of filenames. Each sublist contains files which belong
-%% together, i.e because they are a wrap-set.
-get_all_filenames_fetchlog_result(NodeResult,Dbg) ->
- get_all_filenames_fetchlog_result_2(NodeResult,Dbg,[]).
-
-get_all_filenames_fetchlog_result_2([{Node,{Success,FileInfo}}|Rest],Dbg,Accum)
- when Success=/=error, list(FileInfo) ->
- SubAccum=get_all_filenames_fetchlog_result_3(FileInfo,[]),
- get_all_filenames_fetchlog_result_2(Rest,Dbg,[{Node,SubAccum}|Accum]);
-get_all_filenames_fetchlog_result_2([{Node,{error,FReason}}|Rest],Dbg,Accum) ->
- inviso_tool_lib:debug(fetch_files,Dbg,[Node,FReason]),
- get_all_filenames_fetchlog_result_2(Rest,Dbg,Accum);
-get_all_filenames_fetchlog_result_2([],_Dbg,Accum) ->
- Accum.
-
-get_all_filenames_fetchlog_result_3([{FType,Files}|Rest],SubAccum) ->
- FilesOnly=lists:foldl(fun({ok,FName},Acc)->[FName|Acc];(_,Acc)->Acc end,[],Files),
- get_all_filenames_fetchlog_result_3(Rest,[{FType,FilesOnly}|SubAccum]);
-get_all_filenames_fetchlog_result_3([],SubAccum) ->
- SubAccum.
-
-%% Help function which traverses a noderesult and builds a list as return
-%% value containing the nodenames of all nodes not being complete.
-%% Note that a node may occur multiple times since may have fetched logfiles
-%% for several tracerdata from the same node. Makes sure the list contains
-%% unique node names.
-%% Returns a list nodes.
-get_all_failednodes_fetchlog_result(NodeResult) ->
- get_all_failednodes_fetchlog_result_2(NodeResult,[]).
-
-get_all_failednodes_fetchlog_result_2([{_Node,{complete,_}}|Rest],Acc) ->
- get_all_failednodes_fetchlog_result_2(Rest,Acc);
-get_all_failednodes_fetchlog_result_2([{Node,{_Severity,_}}|Rest],Acc) ->
- case lists:member(Node,Acc) of
- true -> % Already in the list.
- get_all_failednodes_fetchlog_result_2(Rest,Acc);
- false -> % Not in Acc, add it!
- get_all_failednodes_fetchlog_result_2(Rest,[Node|Acc])
- end;
-get_all_failednodes_fetchlog_result_2([],Acc) ->
- Acc.
-%% ------------------------------------------------------------------------------
-
-%% Help function which copies files from one location to Dir and at the same time
-%% adds the Prefix to the filename. NodeSpecs contains full path to the files. The
-%% reason the node information is still part of NodeSpecs is that otherwise we can
-%% not report faulty nodes. Note that one node may occur multiple times since there
-%% may be more than one tracerdata for a node.
-%% Returns {FailedNodes,Files} where FailedNodes is a list of nodes where problems
-%% occurred. Files is a tuple list of [{Node,[{FType,FileNames},...]},...].
-fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,NodeSpecs) ->
- CopySpecList=build_copylist(CtrlNode,Dbg,NodeSpecs,TRDstorage),
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,CopySpecList,[],[]).
-
-fetch_logfiles_copy_2(Dir,Prefix,Dbg,[{Node,CopySpecs}|Rest],FailedNodes,Files) ->
- case fetch_logfiles_copy_3(Dir,Prefix,Dbg,CopySpecs,[],0) of
- {0,LocalFiles} -> % Copy went ok and zero errors.
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,FailedNodes,[{Node,LocalFiles}|Files]);
- {_N,LocalFiles} -> % Copied files, but some went wrong.
- case lists:member(Node,FailedNodes) of
- true -> % Node already in FailedNodes.
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,FailedNodes,
- [{Node,LocalFiles}|Files]);
- false -> % Node not marked as failed, yet.
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,[Node|FailedNodes],
- [{Node,LocalFiles}|Files])
- end
- end;
-fetch_logfiles_copy_2(_,_,_,[],FailedNodes,Files) ->
- {FailedNodes,Files}. % The return value from fetch_logfiles_copy.
-
-fetch_logfiles_copy_3(Dir,Prefix,Dbg,[{FType,RemoteFiles}|Rest],Results,Errors) ->
- {Err,LocalFiles}=fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,RemoteFiles,[],0),
- fetch_logfiles_copy_3(Dir,Prefix,Dbg,Rest,[{FType,LocalFiles}|Results],Errors+Err);
-fetch_logfiles_copy_3(_,_,_,[],Results,Errors) ->
- {Errors,Results}.
-
-%% For each file of one file-type (e.g. trace_log).
-fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,[File|Rest],LocalFiles,Errors) ->
- DestName=Prefix++filename:basename(File),
- Destination=filename:join(Dir,DestName),
- case do_copy_file(File,Destination) of
- ok ->
- fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,Rest,[DestName|LocalFiles],Errors);
- {error,Reason} ->
- inviso_tool_lib:debug(copy_files,Dbg,[File,Destination,Reason]),
- fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,Rest,LocalFiles,Errors+1)
- end;
-fetch_logfiles_copy_3_1(_,_,_,[],LocalFiles,Errors) ->
- {Errors,LocalFiles}.
-
-%% Help function which builds a [{Node,[{Type,[ListOfRemoteFiles]}},...}]
-%% where Type describes trace_log or ti_log and each entry in ListOfRemoteFiles
-%% is a complete path to a file to be copied.
-build_copylist(CtrlNode,Dbg,NodeSpecList,TRDstorage) ->
- build_copylist_2(CtrlNode,Dbg,NodeSpecList,TRDstorage,[]).
-
-%% For each node specified in the NodeSpecList.
-build_copylist_2(CtrlNode,Dbg,[{Node,SourceDir}|Rest],TRDstorage,Acc) ->
- TRDlist=find_tracerdata_for_node_trd(Node,TRDstorage),
- CopySpecList=build_copylist_3(CtrlNode,Dbg,SourceDir,Node,TRDlist),
- build_copylist_2(CtrlNode,Dbg,Rest,TRDstorage,[CopySpecList|Acc]);
-build_copylist_2(_,_,[],_,Acc) ->
- lists:flatten(Acc).
-
-%% For each tracerdata found for the node.
-build_copylist_3(void,Dbg,SourceDir,Node,[TRD|Rest]) -> % The non-distributed case.
- case inviso:list_logs(TRD) of
- {ok,FileSpec} when list(FileSpec) -> % [{trace_log,Dir,Files},...]
- NewFileSpec=build_copylist_4(SourceDir,FileSpec,[]),
- [{Node,NewFileSpec}|build_copylist_3(void,Dbg,SourceDir,Node,Rest)];
- {ok,no_log} -> % This tracedata not associated with any log.
- build_copylist_3(void,Dbg,SourceDir,Node,Rest);
- {error,Reason} ->
- inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
- build_copylist_3(void,Dbg,SourceDir,Node,Rest)
- end;
-build_copylist_3(CtrlNode,Dbg,SourceDir,Node,[TRD|Rest]) -> % The distributed case.
- case inviso_tool_lib:inviso_cmd(CtrlNode,list_logs,[[{Node,TRD}]]) of
- {ok,[{Node,{ok,FileSpec}}]} when list(FileSpec) ->
- NewFileSpec=build_copylist_4(SourceDir,FileSpec,[]),
- [{Node,NewFileSpec}|build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest)];
- {ok,[{Node,{ok,no_log}}]} -> % It relays to another node, no files!
- build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest);
- {ok,[{Node,{error,Reason}}]} ->
- inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
- build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest);
- {error,Reason} -> % Some general failure.
- inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
- build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest)
- end;
-build_copylist_3(_,_,_,_,[]) ->
- [].
-
-%% Help function which makes a [{Type,Files},...] list where each file in Files
-%% is with full path as found from our file-system.
-build_copylist_4(SourceDir,[{Type,_Dir,Files}|Rest],Accum) ->
- NewFiles=
- lists:foldl(fun(FName,LocalAcc)->[filename:join(SourceDir,FName)|LocalAcc] end,
- [],
- Files),
- build_copylist_4(SourceDir,Rest,[{Type,NewFiles}|Accum]);
-build_copylist_4(_,[],Accum) ->
- Accum.
-
-
-%% Help function which copies a file using os:cmd.
-%% Returns 'ok' or {error,Reason}.
-do_copy_file(Source,Destination) ->
- case os:type() of
- {win32,_} ->
- os:cmd("copy "++Source++" "++Destination), % Perhaps a test on success?
- ok;
- {unix,_} ->
- os:cmd("cp "++Source++" "++Destination), % Perhaps a test on success?
- ok
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Various help functions.
-%% ==============================================================================
-
-%% Help function going through the Nodes list and checking that only nodes
-%% mentioned in OurNodes gets returned. It also makes the nodes in the return
-%% value unique.
-remove_nodes_not_ours(Nodes,OurNodes) ->
- remove_nodes_not_ours_2(Nodes,OurNodes,[],[]).
-
-remove_nodes_not_ours_2([Node|Rest],OurNodes,OurAcc,OtherAcc) ->
- case lists:member(Node,OurNodes) of
- true -> % Ok it is one of our nodes.
- case lists:member(Node,OurAcc) of
- true -> % Already in the list, skip.
- remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,OtherAcc);
- false ->
- remove_nodes_not_ours_2(Rest,OurNodes,[Node|OurAcc],OtherAcc)
- end;
- false ->
- case lists:member(Node,OtherAcc) of
- true ->
- remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,OtherAcc);
- false ->
- remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,[Node|OtherAcc])
- end
- end;
-remove_nodes_not_ours_2([],_,OurAcc,OtherAcc) ->
- {lists:reverse(OurAcc),lists:reverse(OtherAcc)}.
-%% ------------------------------------------------------------------------------
-
-%% Help function which returns 'true' or 'false' depending on if TracerData is
-%% meant to be used by the session handler (true) or if it supposed to be passed
-%% on to the trace system.
-is_tool_internal_tracerdata(_) -> % CURRENTLY NO INTERNAL TRACER DATA!
- false.
-%% ------------------------------------------------------------------------------
-
-%% Help function which checks that all nodes in the first list of nodes exists
-%% in the second list of nodes. Returns 'true' or 'false'. The latter if as much
-%% as one incorrect node was found.
-check_our_nodes([Node|Rest],AllNodes) ->
- case lists:member(Node,AllNodes) of
- true ->
- check_our_nodes(Rest,AllNodes);
- false -> % Then we can stop right here.
- false
- end;
-check_our_nodes([],_) ->
- true.
-%% ------------------------------------------------------------------------------
-
-%% Help function which checks that a directory actually exists. Returns 'true' or
-%% 'false'.
-check_directory_exists(Dir) ->
- case file:read_file_info(Dir) of
- {ok,#file_info{type=directory}} ->
- true;
- _ -> % In all other cases it is not valid.
- false
- end.
-%% ------------------------------------------------------------------------------
-
-%% This function stops the tracing on all nodes in Nodes. Preferably Nodes is a list
-%% of only tracing runtime components. Not that there will actually be any difference
-%% since the return value does not reflect how stopping the nodes went.
-%% Returns 'ok' or {error,Reason}, the latter only in case of general failure.
-stop_all_tracing(void,Dbg,[?LOCAL_RUNTIME]) -> % The non-distributed case, and is tracing.
- case inviso:stop_tracing() of
- {ok,_State} ->
- ok;
- {error,Reason} -> % We actually don't care.
- inviso_tool_lib:debug(stop_tracing,Dbg,[?LOCAL_RUNTIME,Reason]),
- ok
- end;
-stop_all_tracing(void,_,_) -> % There is no local runtime started.
- ok;
-stop_all_tracing(CtrlNode,Dbg,Nodes) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,stop_tracing,[Nodes]) of
- {ok,Result} -> % The result is only used for debug.
- Failed=lists:foldl(fun({N,{error,Reason}},Acc)->[{N,{error,Reason}}|Acc];
- (_,Acc)->Acc
- end,
- [],
- Result),
- if
- Failed==[] ->
- ok;
- true ->
- inviso_tool_lib:debug(stop_tracing,Dbg,[Nodes,Failed]),
- ok
- end;
- {error,Reason} ->
- {error,{stop_tracing,Reason}}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function removing all local logs using the tracerdata to determine what
-%% logs to remove from where.
-%% There is no significant return value since it is not really clear what to do
-%% if removal went wrong. The function can make debug-reports thought.
-remove_all_local_logs(CtrlNode,TRDstorage,Nodes,Dbg) ->
- LogSpecList=build_logspeclist_remove_logs(Nodes,TRDstorage),
- case inviso_tool_lib:inviso_cmd(CtrlNode,delete_log,[LogSpecList]) of
- {ok,Results} ->
- case look_for_errors_resultlist(Results) of
- [] -> % No errors found in the result!
- true;
- Errors ->
- inviso_tool_lib:debug(remove_all_local_logs,Dbg,[Errors]),
- true
- end;
- {error,Reason} -> % Some general error.
- inviso_tool_lib:debug(remove_all_local_logs,Dbg,[{error,Reason}]),
- true
- end.
-
-%% Help function which puts together a list of {Node,Tracerdata} tuples. Note that
-%% we must build one tuple for each tracerdata for one node.
-build_logspeclist_remove_logs(Nodes,TRDstorage) ->
- [{Node,TracerData}||Node<-Nodes,TracerData<-find_tracerdata_for_node_trd(Node,TRDstorage)].
-%% ------------------------------------------------------------------------------
-
-%% Help function which traverses a resultlist from an inviso function. Such are
-%% built up as [{Node,SubResults},...] where SubResult is a list of tuples for each
-%% file-type (e.g trace_log) {FType,FileList} where a FileList is either {error,Reason}
-%% or {ok,FileName}.
-%% Returns a list of {Node,[{error,Reason},...]}.
-look_for_errors_resultlist([{Node,{error,Reason}}|Rest]) ->
- [{Node,{error,Reason}}|look_for_errors_resultlist(Rest)];
-look_for_errors_resultlist([{Node,{ok,NResults}}|Rest]) when list(NResults) ->
- case look_for_errors_resultlist_2(NResults,[]) of
- [] ->
- look_for_errors_resultlist(Rest);
- Errors -> % A list of lists.
- [{Node,lists:flatten(Errors)}|look_for_errors_resultlist(Rest)]
- end;
-look_for_errors_resultlist([_|Rest]) ->
- look_for_errors_resultlist(Rest);
-look_for_errors_resultlist([]) ->
- [].
-
-look_for_errors_resultlist_2([{_FType,NSubResult}|Rest],Accum) ->
- case lists:filter(fun({error,_Reason})->true;(_)->false end,NSubResult) of
- [] -> % No errors for this node.
- look_for_errors_resultlist_2(Rest,Accum);
- Errors -> % A list of at least one error.
- look_for_errors_resultlist_2(Rest,[Errors|Accum])
- end;
-look_for_errors_resultlist_2([],Accum) ->
- Accum.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the loopdata structure.
-%% Its main purpose is to store information about runtime components participating
-%% in the session and their current status.
-%% ------------------------------------------------------------------------------
-
--record(ld,{parent,
- ctrlnode,
- ctrlpid, % To where to send inviso cmd.
- rtstates,
- tracerdata,
- safetycatches,
- dbg,
- actstorage % Activity storage, for reactivate.
- }).
-
-%% Function creating the initial datastructure.
-%% The datastructure is [{Node,State},...].
-%%
-%% The tracerdata table is a bag simply for the reason that if we try to insert
-%% the same tracerdata for a node twice, we will end up with one tracerdata after
-%% all. This is useful when we insert tracerdata ourselves, the tracerdata will
-%% come as a state-change too.
-mk_ld(Parent,CtrlNode,CtrlPid,RTStates,NodeParams,OtherNodes,SafetyCatches,Dbg) ->
- TRDtableName=list_to_atom("inviso_tool_sh_trdstorage_"++pid_to_list(self())),
- TRDtid=ets:new(TRDtableName,[bag]),
- ACTtableName=list_to_atom("inviso_tool_sh_actstorage_"++pid_to_list(self())),
- ACTtid=ets:new(ACTtableName,[bag]),
- mk_ld_fill_tracerdata(CtrlNode,TRDtid,NodeParams,OtherNodes), % Fill the ETS table.
- #ld{parent=Parent, % The tool main process.
- ctrlnode=CtrlNode, % Node name where the control component is.
- ctrlpid=CtrlPid, % The process id of the control component.
- rtstates=RTStates, % All nodes and their state/status.
- tracerdata=TRDtid,
- safetycatches=SafetyCatches,
- dbg=Dbg,
- actstorage=ACTtid
- }.
-
-%% Help function which inserts tracer data for the nodes. Note that we can get
-%% tracer data either from the return value from init_tracing or by asking the
-%% node for it. The latter is necessary for the nodes which were marked not to
-%% be initiated by the session handler. This maybe because those nodes have
-%% autostarted.
-mk_ld_fill_tracerdata(CtrlNode,TId,NodeParams,OtherNodes) ->
- mk_ld_fill_tracerdata_nodeparams(TId,NodeParams),
- mk_ld_fill_tracerdata_othernodes(CtrlNode,TId,OtherNodes).
-
-mk_ld_fill_tracerdata_nodeparams(TId,[{Node,TracerData}|Rest]) ->
- ets:insert(TId,{Node,TracerData}),
- mk_ld_fill_tracerdata_nodeparams(TId,Rest);
-mk_ld_fill_tracerdata_nodeparams(_,[]) ->
- ok.
-
-mk_ld_fill_tracerdata_othernodes(_,_,[]) -> % Then not necessary to do anything.
- ok;
-mk_ld_fill_tracerdata_othernodes(void,TId,[Node]) -> % The non-distributed case.
- case inviso:get_tracerdata() of
- {error,_Reason} -> % Perhaps in state new or disconnected.
- ok; % Do nothing.
- {ok,TracerData} ->
- ets:insert(TId,{Node,TracerData})
- end;
-mk_ld_fill_tracerdata_othernodes(CtrlNode,TId,Nodes) ->
- case inviso_tool_lib:invisomd(CtrlNode,get_tracerdata,[Nodes]) of
- {ok,Results} ->
- mk_ld_fill_tracerdata_othernodes_2(TId,Results);
- {error,_Reason} -> % Strange, we will probably crash later.
- ok
- end.
-
-mk_ld_fill_tracerdata_othernodes_2(TId,[{_Node,{ok,no_tracerdata}}|Rest]) ->
- mk_ld_fill_tracerdata_othernodes_2(TId,Rest); % It was not initiated then!
-mk_ld_fill_tracerdata_othernodes_2(TId,[{Node,{ok,TracerData}}|Rest]) ->
- ets:insert(TId,{Node,TracerData}),
- mk_ld_fill_tracerdata_othernodes_2(TId,Rest);
-mk_ld_fill_tracerdata_othernodes_2(_,[]) ->
- ok.
-%% ------------------------------------------------------------------------------
-
-get_ctrlnode_ld(#ld{ctrlnode=CtrlNode}) ->
- CtrlNode.
-%% ------------------------------------------------------------------------------
-
-
-get_ctrlpid_ld(#ld{ctrlpid=CtrlPid}) ->
- CtrlPid.
-%% ------------------------------------------------------------------------------
-
-get_rtstates_ld(#ld{rtstates=RTStates}) ->
- RTStates.
-
-put_rtstates_ld(NewRTStates,LD) ->
- LD#ld{rtstates=NewRTStates}.
-%% ------------------------------------------------------------------------------
-
-get_trdstorage_ld(#ld{tracerdata=TId}) ->
- TId.
-
-put_trdstorage_ld(_NewTId,LD) ->
- LD.
-%% ------------------------------------------------------------------------------
-
-%% Help function which adds the current tracerdata of node Node to the tracerdata
-%% storage. We only want to add tracerdata we have not seen before. We therefore
-%% avoid adding it if the node already is in state ?TRACING.
-%% Returns a new tracerdata (what ever it is)!
-add_current_tracerdata_ld(CtrlNode,Node,RTStates,TId) ->
- case get_statestatus_rtstates(Node,RTStates) of
- {ok,{?TRACING,_}} -> % Then we have already added the tracerdata.
- TId; % Then do nothing.
- {ok,_} -> % Since we were not tracing before.
- case add_current_tracerdata_ld_fetchtracerdata(CtrlNode,Node) of
- {ok,TracerData} ->
- ets:insert(TId,{Node,TracerData});
- no_tracerdata -> % Strange, how could we become tracing
- ok;
- {error,_Reason} -> % The node perhaps disconnected!?
- ok
- end;
- false -> % Very strange, not our node!
- ok % Do nothing.
- end.
-
-add_current_tracerdata_ld_fetchtracerdata(void,_Node) ->
- case inviso:get_tracerdata() of
- {ok,TracerData} ->
- {ok,TracerData};
- {error,no_tracerdata} ->
- no_tracerdata;
- {error,Reason} ->
- {error,Reason}
- end;
-add_current_tracerdata_ld_fetchtracerdata(CtrlNode,Node) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,get_tracerdata,[[Node]]) of
- {ok,[{Node,{ok,TracerData}}]} ->
- {ok,TracerData};
- {ok,[{Node,{error,no_tracerdata}}]} ->
- no_tracerdata;
- {ok,[{Node,{error,Reason}}]} ->
- {error,Reason};
- {error,Reason} ->
- {error,Reason}
- end.
-%% ------------------------------------------------------------------------------
-
-
-get_safetycatches_ld(#ld{safetycatches=SCs}) ->
- SCs.
-%% ------------------------------------------------------------------------------
-
-get_dbg_ld(#ld{dbg=Dbg}) ->
- Dbg.
-%% ------------------------------------------------------------------------------
-
-get_actstorage_ld(#ld{actstorage=ACTstorage}) ->
- ACTstorage.
-
-put_actstorage_ld(_NewACTstorage,LD) ->
- LD.
-%% ------------------------------------------------------------------------------
-
-
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the rtstates structure (which is a substructure of loopdata).
-%% It is either:
-%% [{Node,StateStatus,Opts},...]
-%% Node is either the node name of the runtime component erlang node or
-%% ?LOCAL_RUNTIME as returned from the trace control component.
-%% StateStatus is {State,Status}, 'unavailable' or 'unknown'.
-%% Status is the returnvalue from trace control component.
-%% i.e: running | {suspended,Reason}
-%% ------------------------------------------------------------------------------
-
-%% Function contructing an rtstates structure from a list of [{Node,StateStatus,Opts},...].
-to_rtstates(ListOfStates) when list(ListOfStates) ->
- ListOfStates.
-%% ------------------------------------------------------------------------------
-
-%% Function which takes a rtstates structure and returns a list of [{Node,StateStatus},...].
-from_rtstates(RTStates) ->
- RTStates.
-%% ------------------------------------------------------------------------------
-
-%% Function which takes an rtstates structure and a result as returned from
-%% init_tracing. The RTStates is modified for the nodes that changed state as a
-%% result of successful init_tracing.
-%% Returns a new RTStates.
-set_tracing_rtstates([E={Node,_StateStatus,Opts}|Rest],Result) ->
- case lists:keysearch(Node,1,Result) of
- {value,{_,ok}} -> % Means state-change to tracing!
- [{Node,{tracing,running},Opts}|set_tracing_rtstates(Rest,Result)];
- _ -> % Otherwise, leave it as is.
- [E|set_tracing_rtstates(Rest,Result)]
- end;
-set_tracing_rtstates([],_Result) ->
- [].
-%% ------------------------------------------------------------------------------
-
-%% Function updating the state/status for a certain runtime component.
-%% Returns a new RTStates structure. Note that Node must not necessarily be one
-%% of the nodes in the session. Meaning that Node shall not be added to RTStates
-%% should it not already be in there.
-statechange_rtstates(Node,State,Status,RTStates) when list(RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,_,Opts}} ->
- lists:keyreplace(Node,1,RTStates,{Node,{State,Status},Opts});
- _ -> % Then Node does not exist.
- RTStates % Just keep it as is, as keyreplace would have done.
- end.
-%% ------------------------------------------------------------------------------
-
-%% Function updating the state/status for a certain runtime component. The
-%% state/status is set to 'unavailable'.
-%% Returns a new RTStates structure.
-set_unavailable_rtstates(Node,RTStates) when list(RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,_,Opts}} ->
- lists:keyreplace(Node,1,RTStates,{Node,unavailable,Opts});
- _ -> % Then Node does not exist.
- RTStates % Just keep it as is, as keyreplace would have done.
- end.
-%% ------------------------------------------------------------------------------
-
-%% Function finding the statestatus associated with Node in the RTStates structure.
-%% Returns {ok,StateStatus} or 'false'.
-get_statestatus_rtstates(Node,RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,StateStatus,_}} ->
- {ok,StateStatus};
- false ->
- false
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function which returns a list of all nodes that are currently marked
-%% as available to us in the runtime state structure.
-get_all_available_nodes_rtstates(RTStates) ->
- get_all_session_nodes_rtstates(lists:filter(fun({_N,unavailable,_})->false;
- (_)->true
- end,
- RTStates)).
-%% ------------------------------------------------------------------------------
-
-%% Help function returning a list of all nodes belonging to this session.
-get_all_session_nodes_rtstates(RTStates) ->
- lists:map(fun({Node,_,_})->Node end,RTStates).
-%% ------------------------------------------------------------------------------
-
-%% Function which returns a list of nodes that are indicated as tracing in the
-%% RTStates structure.
-get_all_tracing_nodes_rtstates(RTStates) ->
- lists:map(fun({N,_,_})->N end,
- lists:filter(fun({_,{tracing,_},_})->true;(_)->false end,RTStates)).
-%% ------------------------------------------------------------------------------
-
-%% Returns the options associated with Node in the RTStates structure.
-get_opts_rtstates(Node,RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,_,Opts}} ->
- {ok,Opts};
- false ->
- false
- end.
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the tracerdata structure, which is a part of the loopdata.
-%% The tracerdata structure is an ETS-table of type bag storing:
-%% {Node,TracerData}.
-%% Note that there can of course be multiple entries for a node.
-%% ------------------------------------------------------------------------------
-
-%% Help function which takes a tracerdata loopdata structure and returns a list
-%% of all stored tracerdata for a certain Node.
-find_tracerdata_for_node_trd(Node,TRD) ->
- case ets:lookup(TRD,Node) of
- Result when list(Result) ->
- lists:map(fun({_Node,TracerData})->TracerData end,Result);
- _ -> % Should probably never happend.
- []
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the activity storage structure, which is part of the
-%% loopdata. It stores entries about things that needs to be "redone" in case
-%% of a reactivation of the node. The time order is also important.
-%% Note that for every ActivityType there must be a "handler" in the reactivation
-%% functionality.
-%%
-%% The structure is a bag of {Node,ActivityType,What}.
-%% ActivityType/What=tf/{Op,TraceConfList}|tpm/{Op,[Mod,Func,Arity,MS,CallFunc]}
-%% /{Op,[Mod,Func,Arity,MS,CallFunc,ReturnFunc]}
-%% /{Op,[]}
-%% TraceConfList=[{Proc,Flags},...]
-%% How=true|false
-%% ------------------------------------------------------------------------------
-
-%% Function that adds meta-pattern activities to the activity storage. Note
-%% that one of the parameters to the function is a return value from an
-%% inviso call. In that way we do not enter activities that were unsuccessful.
-%% Op can be either the setting or clearing of a meta pattern.
-%% Returns a new ACTstorage.
-add_tpm_actstorage([{Node,ok}|Rest],Op,InvisoCmdParams,ACTstorage) ->
- true=ets:insert(ACTstorage,{Node,tpm,{Op,InvisoCmdParams}}),
- add_tpm_actstorage(Rest,Op,InvisoCmdParams,ACTstorage);
-add_tpm_actstorage([_|Rest],Op,InvisoCmdParams,ACTstorage) ->
- add_tpm_actstorage(Rest,Op,InvisoCmdParams,ACTstorage);
-add_tpm_actstorage([],_,_,ACTstorage) ->
- ACTstorage.
-
-%% Function that adds process trace-flags to the activity storage. Note that one
-%% of the parameters is the return value from an inviso function. Meaning that
-%% if the flags failed in their entirety, no activity will be saved. If only
-%% some of the flags failed, we will not go through the effort of trying to find
-%% out exactly which.
-%% Returns a new activity storage structure.
-add_tf_actstorage([{_Node,{error,_Reason}}|Rest],Op,TraceConfList,ACTstorage) ->
- add_tf_actstorage(Rest,Op,TraceConfList,ACTstorage);
-add_tf_actstorage([{Node,_Result}|Rest],Op,TraceConfList,ACTstorage) ->
- true=ets:insert(ACTstorage,{Node,tf,{Op,TraceConfList}}),
- add_tf_actstorage(Rest,Op,TraceConfList,ACTstorage);
-add_tf_actstorage([],_,_,ACTstorage) ->
- ACTstorage.
-%% ------------------------------------------------------------------------------
-
-%% Finds all activities associated with Node. Returns a list of them in the
-%% same order as they were inserted.
-get_activities_actstorage(Node,ACTstorage) ->
- case ets:lookup(ACTstorage,Node) of
- [] ->
- false;
- Result when list(Result) ->
- {ok,lists:map(fun({_N,Type,What})->{Type,What} end,Result)}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Function removing all activity entries associated with Node. This is useful
-%% if the Node disconnects for instance.
-del_node_actstorage(Node,ACTstorage) ->
- ets:delete(ACTstorage,Node),
- ACTstorage.
-%% ------------------------------------------------------------------------------
-
diff --git a/lib/inviso/test/Makefile b/lib/inviso/test/Makefile
deleted file mode 100644
index 2650faa392..0000000000
--- a/lib/inviso/test/Makefile
+++ /dev/null
@@ -1,61 +0,0 @@
-#
-include $(ERL_TOP)/make/target.mk
-include $(ERL_TOP)/make/$(TARGET)/otp.mk
-
-MODULES = \
- inviso_tool_SUITE
-
-ERL_FILES= $(MODULES:%=%.erl)
-
-TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
-INSTALL_PROGS= $(TARGET_FILES)
-
-EMAKEFILE=Emakefile
-
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/inviso_test
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-ERL_MAKE_FLAGS +=
-ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include
-
-EBIN = .
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-
-make_emakefile:
- $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES)\
- > $(EMAKEFILE)
-
-tests debug opt: make_emakefile
- erl $(ERL_MAKE_FLAGS) -make
-
-clean:
- rm -f $(EMAKEFILE)
- rm -f $(TARGET_FILES)
- rm -f core
-
-docs:
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_spec: opt
-
-release_tests_spec: make_emakefile
- $(INSTALL_DIR) "$(RELSYSDIR)"
- $(INSTALL_DATA) inviso.spec inviso.cover $(ERL_FILES) "$(RELSYSDIR)"
- chmod -R u+w "$(RELSYSDIR)"
- @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
-
-release_docs_spec:
-
-
diff --git a/lib/inviso/test/inviso.cover b/lib/inviso/test/inviso.cover
deleted file mode 100644
index e23b9fa59b..0000000000
--- a/lib/inviso/test/inviso.cover
+++ /dev/null
@@ -1,2 +0,0 @@
-{incl_app,inviso,details}.
-
diff --git a/lib/inviso/test/inviso.spec b/lib/inviso/test/inviso.spec
deleted file mode 100644
index 49f9b0b460..0000000000
--- a/lib/inviso/test/inviso.spec
+++ /dev/null
@@ -1 +0,0 @@
-{suites,"../inviso_test",all}.
diff --git a/lib/inviso/test/inviso_tool_SUITE.erl b/lib/inviso/test/inviso_tool_SUITE.erl
deleted file mode 100644
index e14f32de44..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE.erl
+++ /dev/null
@@ -1,1166 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2012. 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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-%% Description:
-%% Test suite for the inviso_tool. It is here assumed that inviso works
-%% properly.
-%%
-%% Authors:
-%% Lennart Öhman, [email protected]
-%% -----------------------------------------------------------------------------
-
--module(inviso_tool_SUITE).
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
--include_lib("kernel/include/file.hrl").
-
--define(l,?line).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
-
-all() ->
- [dist_basic_1, dist_rtc, dist_reconnect, dist_adopt,
- dist_history, dist_start_session_special].
-
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% -----------------------------------------------------------------------------
-
-init_per_suite(Config) ->
- case test_server:is_native(lists) of
- true ->
- {skip,"Native libs -- tracing doesn't work"};
- false ->
- Config
- end.
-%% -----------------------------------------------------------------------------
-
-end_per_suite(_Config) ->
- ok.
-%% -----------------------------------------------------------------------------
-
-%% For each distributed testcase, we need two other distributed nodes to run the
-%% runtime components on. Since they are freshly started every time there is no
-%% need to clean them up first.
-init_per_testcase(_Case,Config) ->
- ?l TH=test_server:timetrap(100000),
- ?l {ok,Node1}=test_server:start_node(inviso1,peer,[]),
- ?l {ok,Node2}=test_server:start_node(inviso2,peer,[]),
- ?l SuiteDir=filename:dirname(code:which(?MODULE)),
-
- %% Otherwise peer nodes will not find this module!
- ?l true=rpc:call(Node1,code,add_patha,[SuiteDir]),
- ?l true=rpc:call(Node2,code,add_patha,[SuiteDir]),
-
- %% SPECIAL FOR MY PRIVATE TEST ENVIROMENT
-% ?l rpc:call(Node1,code,add_patha,["/clearcase/otp/tools/runtime_tools/ebin"]),
-% ?l rpc:call(Node1,code,add_patha,["/clearcase/otp/tools/inviso/ebin"]),
-% ?l rpc:call(Node2,code,add_patha,["/clearcase/otp/tools/runtime_tools/ebin"]),
-% ?l rpc:call(Node2,code,add_patha,["/clearcase/otp/tools/inviso/ebin"]),
-
-% %% SPECIAL FOR MY PRIVATE TEST ENVIROMENT, windows.
-% ?l rpc:call(Node1,code,add_patha,["C:/DATA/PROJECTS/inviso_project/runtime_tools/ebin"]),
-% ?l rpc:call(Node1,code,add_patha,["C:/DATA/PROJECTS/inviso_project/inviso/ebin"]),
-% ?l rpc:call(Node2,code,add_patha,["C:/DATA/PROJECTS/inviso_project/runtime_tools/ebin"]),
-% ?l rpc:call(Node2,code,add_patha,["C:/DATA/PROJECTS/inviso_project/inviso/ebin"]),
-
- ?l ok=rpc:call(Node1,application,start,[runtime_tools]),
- ?l ok=rpc:call(Node2,application,start,[runtime_tools]),
- ?l timer:sleep(100), % Problem with autostarted runtime.
- %% The following is a test that the inviso_rt processes which are autostarted
- %% are now gone.
-
- ?l ok=poll(rpc,call,[Node1,erlang,whereis,[inviso_rt]],undefined,20),
- ?l ok=poll(rpc,call,[Node2,erlang,whereis,[inviso_rt]],undefined,20),
-
- NewConfig1=insert_remotenode_config(inviso1,Node1,Config),
- NewConfig2=insert_remotenode_config(inviso2,Node2,NewConfig1),
- insert_timetraphandle_config(TH,NewConfig2).
-%% -----------------------------------------------------------------------------
-
-end_per_testcase(_Case,Config) ->
- ?l test_server:stop_node(get_remotenode_config(inviso1,Config)),
- ?l test_server:stop_node(get_remotenode_config(inviso2,Config)),
- ?l test_server:timetrap_cancel(get_timetraphandle_config(Config)),
- ?l case whereis(inviso_tool) of % In case inviso_tool did not stop.
- Pid when is_pid(Pid) ->
- ?l io:format("Had to kill inviso_tool!~n",[]),
- ?l exit(Pid,kill);
- _ ->
- true
- end,
- ?l case whereis(inviso_rt) of % In case we ran a runtime here.
- Pid2 when is_pid(Pid2) ->
- ?l io:format("Had to kill inviso_rt!~n",[]),
- ?l exit(Pid2,kill);
- _ ->
- true
- end,
- ?l case whereis(inviso_c) of % In case we ran the controll component here.
- Pid3 when is_pid(Pid3) ->
- ?l io:format("Had to kill inviso_c!~n",[]),
- ?l exit(Pid3,kill);
- _ ->
- true
- end,
- NewConfig1=remove_remotenode_config(inviso1,Config),
- NewConfig2=remove_remotenode_config(inviso2,NewConfig1),
- remove_timetraphandle_config(NewConfig2).
-%% -----------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Testcases.
-%% ==============================================================================
-
-%% -----------------------------------------------------------------------------
-%% Functional tests:
-%% API:
-%% start/0 dist_basic_1
-%% stop/0 dist_basic_1
-%% reconnect_nodes/1 dist_reconnect
-%% start_session/0 dist_basic_1
-%% reinitiate_session/1 dist_reconnect
-%% stop_session/0 dist_basic_1
-%% atc/3 dist_basic_1
-%% sync_atc/3 dist_basic_1
-%% sync_rtc/2, dist_rtc
-%% dtc/2 dist_basic_1
-%% sync_dtc/2 dist_basic_1
-%% inviso/2 dist_basic_1
-%% reactivate/1 dist_basic_1
-%% get_autostart_data/2 dist_basic_1
-%% get_activities/0 dist_basic_1
-%% save_history/1 dist_history
-%% restore_session/1 dist_history
-%% get_node_status/1 dist_basic_1
-%% get_session_data/0 dist_basic_1
-%% flush/0 dist_basic_1
-%% -----------------------------------------------------------------------------
-
-%% Non functional tests:
-%% Run the control component on both the dist_history
-%% same node as the tool and on a
-%% different node.
-%% Let a trace case crash in its execution dist_basic_1
-%% and check that it does not become
-%% part of the history.
-%% Check that tracer data becomes what the NOT IMPLEMENTED
-%% tdg function generates.
-%% Check that all inviso runtime stop_inviso_tool/2
-%% components terminate if the tool is
-%% killed.
-%% Check that activation/deactivation dist_basic_1
-%% cancels each other out in the history.
-%% Check that regexp expansion is done on dist_reconnect
-%% another node if regexp_node is down.
-%% Test that tool-commands activating dist_basic_1
-%% something done during a reactivation
-%% are actually done a bit later at the
-%% reactivated node (this since the the
-%% command being reactivated at the momen
-%% at the reactivating node may not
-%% be finished at the time the new tool
-%% command is issued).
-%% Check that deactivating tracecases are dist_basic_1
-%% not redone at a reactivating node.
-%% (to prevent it from being redone and
-%% then just deactivated).
-%% Check that on-going reactivators and NOT IMPLEMENTED
-%% tracecases are killed when stop_session.
-%% Check that inviso_tool can and will adopt
-%% a running runtime component. dist_adopt
-%% -----------------------------------------------------------------------------
-
--define(TC_DEF_FILE,filename:join(DataDir,"tracecase_def.txt")).
-
-%% TEST CASE: Basic, distributed, start of inviso_tool with simple tracing.
-dist_basic_1(doc) -> ["Simple test"];
-dist_basic_1(suite) -> [];
-dist_basic_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- [RegExpNode|_]=RemoteNodes,
- CNode=node(),
- Nodes=RemoteNodes,
- DataDir=?config(data_dir,Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
- Opts=[{regexp_node,RegExpNode},
- {tdg,{?MODULE,tdg,[PrivDir]}},
- {tc_def_file,?TC_DEF_FILE},
- {initial_tcs,[{tracecase_init,[]}]},
- {dir,DataDir}], % This is where we find tracecases.
- ?l start_inviso_tool(Nodes,CNode,Opts),
- %% Now we know that all inviso runtimes are running and are not tracing.
- ?l {error,no_session}=inviso_tool:inviso(tpl,[lists,module_info,0,[]]),
- ?l {error,no_session}=inviso_tool:get_session_data(),
- ?l start_inviso_tool_session(CNode,[],1,Nodes),
- ?l {ok,{tracing,1,TDGargs}}=inviso_tool:get_session_data(),
- ?l true=is_list(TDGargs),
- %% Check that the initial tracecase has been executed at all tracing nodes.
- ?l lists:foreach(fun(N)->
- ok=poll(rpc,
- call,
- [N,
- erlang,
- trace_info,
- [{lists,module_info,1},traced]],
- {traced,local},
- 20)
- end,
- Nodes),
- %% Start a test process at every node with a runtime component.
- ?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes),
-
- %% Let the processes start
- timer:sleep(100),
-
- %% Find the pids of the test processes.
- ?l TestProcs=lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_tool_test_proc]) end,
- Nodes),
- ?l true=(1=<length(TestProcs)),
-
- %% Activate a trace case.
- ?l {error,unknown_tracecase}=inviso_tool:atc(nonexistant,id,[]),
- ?l {error,{missing_variable,'ProcessName'}}=
- inviso_tool:atc(tracecase1,id1,[]),
- ?l ok=inviso_tool:atc(tracecase1,id1,[{'ProcessName',inviso_tool_test_proc}]),
- ?l {error,activating}=inviso_tool:atc(tracecase1,id1,[{'ProcessName',inviso_tool_test_proc}]),
- ?l {ok,[{tracecases,[{{tracecase1,id1},activating}]}]}=inviso_tool:get_activities(),
- ?l timer:sleep(1700), % There is a 500 ms delay in the tracecase.
- ?l {error,already_started}=
- inviso_tool:atc(tracecase1,id1,[{'ProcessName',inviso_tool_test_proc}]),
-
- %% Now check that the trace case was executed at Nodes.
- ?l lists:foreach(fun(P)->
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[P,flags]],
- {flags,[call]},
- 10),
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[{math,module_info,1},traced]],
- {traced,local},
- 10)
- end,
- TestProcs),
- ?l lists:foreach(fun(P) ->
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[{math,module_info,0},traced]],
- {traced,false},
- 1)
- end,
- TestProcs),
-
- %% Test inviso_tool:inviso/2.
- ?l {ok,NodeResults1}=inviso_tool:inviso(tpl,[math,module_info,0,[]]),
- ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults1),
- ?l lists:foreach(fun(P) ->
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[{math,module_info,0},traced]],
- {traced,local},
- 10)
- end,
- TestProcs),
-
- %% Test inviso_tool:sync_atc/3.
- ?l a_return_value=inviso_tool:sync_atc(tracecase2,id2,[]), % This will take 3000 ms.
- ?l lists:foreach(fun(P) ->
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[{math,pi,0},traced]],
- {traced,local},
- 10)
- end,
- TestProcs),
-
- %% Test the reactivation mechanism.
- ?l [ANode|OtherNodes]=Nodes, % Get a node to suspend.
- ?l {ok,{tracing,running}}=inviso_tool:get_node_status(ANode),
- ?l {ok,[{ANode,ok}]}=rpc:call(CNode,inviso,suspend,[[ANode],test]),
- ?l [APid|_]=TestProcs, % The first process is at ANode.
- %% Now check that trace flags were removed at ANode. This is actually testing inviso.
- ?l ok=poll(rpc,call,[node(APid),erlang,trace_info,[APid,flags]],{flags,[]},10),
- ?l {ok,{tracing,suspended}}=inviso_tool:get_node_status(ANode),
- %% Now reactivate it and expect the history to be redone. But it will take
- %% 3000 ms since there is a delay in tracecase2. Use that delay to issue a new
- %% tool command.
- ?l ok=inviso_tool:reactivate(ANode),
- ?l {ok,reactivating}=inviso_tool:get_node_status(ANode),
- ?l {ok,NodeResults2}=inviso_tool:inviso(tpl,[math,sin,1,[]]),
- ?l true=check_noderesults(OtherNodes,{ok,[1]},NodeResults2),
- %% Verify that the inviso command was not done (yet) at ANode.
- ?l {traced,false}=rpc:call(ANode,erlang,trace_info,[{math,sin,1},traced]),
- ?l {ok,[{reactivating_nodes,[ANode]}]}=inviso_tool:get_activities(),
- ?l timer:sleep(3600),
- %% Now the history shall have been redone including the new inviso command.
- ?l ok=poll(rpc,call,[ANode,erlang,trace_info,[{math,sin,1},traced]],{traced,local},10),
- ?l {flags,[call]}=rpc:call(ANode,erlang,trace_info,[APid,flags]),
- ?l {ok,[]}=inviso_tool:get_activities(),
-
- %% Check the get_autostart function. We know that we use the standard options
- %% generator and the tracecases activated above.
- ?l {ok,{AutostartData1,NodeResults3}}=
- inviso_tool:get_autostart_data(Nodes,{dependency,infinity}),
- ?l true=check_noderesults(Nodes,
- fun({_N,{ok,{[{dependency,infinity}],{tdg,{_M,_F,TDlist}}}}})
- when is_list(TDlist)->
- true;
- (_) ->
- false
- end,
- NodeResults3),
- %% Check the tracecase that shall be activated and their order.
- ?l TraceCaseFileNameInit=filename:join(DataDir,"./tracecase_init.trc"),
- ?l TraceCaseFileName1=filename:join(DataDir,"./tracecase1_on.trc"),
- ?l TraceCaseFileName2=filename:join(DataDir,"./tracecase2_on.trc"),
- ?l [{file,{TraceCaseFileNameInit,[]}},
- {file,{TraceCaseFileName1,[{'ProcessName',inviso_tool_test_proc}]}},
- {mfa,{inviso,tpl,[math,module_info,0,[]]}},
- {file,{TraceCaseFileName2,[]}},
- {mfa,{inviso,tpl,[math,sin,1,[]]}}]=AutostartData1,
-
- %% Try to activate a faulty tracecase. We shall get the same history as before.
- ?l ok=inviso_tool:atc(tracecase3,id3,[]),
- ?l ok=poll(inviso_tool,get_activities,[],{ok,[]},10),
- ?l {ok,{AutostartData1,NodeResults3}}=
- inviso_tool:get_autostart_data(Nodes,{dependency,infinity}),
-
- %% Now deactivate a trace case.
- ?l inviso_tool:dtc(tracecase1,id1),
- %% Check that the now deactivated trace case is not part of autostart data
- %% if requested from the tool.
- ?l {ok,{AutostartData2,_NodeResults}}=
- inviso_tool:get_autostart_data(Nodes,{dependency,infinity}),
- ?l [{file,{TraceCaseFileNameInit,[]}},
- {mfa,{inviso,tpl,[math,module_info,0,[]]}},
- {file,{TraceCaseFileName2,[]}},
- {mfa,{inviso,tpl,[math,sin,1,[]]}}]=AutostartData2,
- %% Now tracing shall be removed since we deactivated tracecase1.
- ?l lists:foreach(fun(P)->
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[P,flags]],
- {flags,[]},
- 10),
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[{math,module_info,1},traced]],
- {traced,false},
- 10)
- end,
- TestProcs),
-
- %% Suspend the ANode again and check that when it is reactivated that
- %% tracecase1 is not redone at all. We use an ets table with a counter that is
- %% incremented every time the tracecase1 is executed.
- ?l {ok,[{ANode,ok}]}=rpc:call(CNode,inviso,suspend,[[ANode],testagain]),
- ?l [{counter,SideEffectCounter1}]=rpc:call(ANode,ets,lookup,[test_proc_tab,counter]),
- ?l true=(SideEffectCounter1>0),
- ?l ok=inviso_tool:reactivate(ANode),
- ?l timer:sleep(3000), % The delay in tracecase2.
- ?l ok=poll(inviso_tool,get_activities,[],{ok,[]},10),
- %% Now the reactivation is done, check that tracecase1 was not redone at ANode.
- ?l [{counter,SideEffectCounter1}]=rpc:call(ANode,ets,lookup,[test_proc_tab,counter]),
-
- %% Deactivate tracecase2.
- ?l another_return_value=inviso_tool:sync_dtc(tracecase2,id2),
- %% Immediately check the autostart data (again!). This time we want to see
- %% that the two inviso commands have been joined since there is no tracecase
- %% in between.
- ?l {ok,{AutostartData3,NodeResults}}=
- inviso_tool:get_autostart_data(Nodes,{dependency,infinity}),
- ?l [{file,{TraceCaseFileNameInit,[]}},
- {mfa,{inviso,tpl,[math,module_info,0,[]]}},
- {mfa,{inviso,tpl,[math,sin,1,[]]}}]=AutostartData3,
-
- %% Check that a deactivating tracecase is not redone at a reactivating node.
- ?l inviso_tool:sync_atc(tracecase5,id5,[]), % Updates the counter.
- %% Yet again suspend the node when we know that tracecase5 has been executed.
- ?l {ok,[{ANode,ok}]}=rpc:call(CNode,inviso,suspend,[[ANode],testagain2]),
- ?l timer:sleep(100), % Subscription reaches the tool.
- ?l [{counter,SideEffectCounter2A}]=rpc:call(ANode,ets,lookup,[test_proc_tab,counter]),
- ?l [BNode|_]=OtherNodes,
- ?l [{counter,SideEffectCounter2B}]=rpc:call(BNode,ets,lookup,[test_proc_tab,counter]),
- ?l ok=inviso_tool:dtc(tracecase5,id5), % In here there is a 2000 ms delay!
- ?l ok=inviso_tool:reactivate(ANode),
- %% Check that the reactivator is done, but that the tracecase remains. The
- %% reactivator should be done pretty quickly since there are no delays in the
- %% still active tracecases.
- ?l ok=poll(inviso_tool,
- get_activities,
- [],
- {ok,[{tracecases,[{{tracecase5,id5},deactivating}]}]},
- 10),
- ?l ok=poll(inviso_tool,get_activities,[],{ok,[]},30),
- ?l [{counter,SideEffectCounter2A}]=rpc:call(ANode,ets,lookup,[test_proc_tab,counter]),
- ?l SideEffectCounter2B1=SideEffectCounter2B+1,
- ?l [{counter,SideEffectCounter2B1}]=rpc:call(BNode,ets,lookup,[test_proc_tab,counter]),
-
- %% Check the flush function. It is difficult to find out if it really flushed.
- ?l {ok,NodeResults4}=inviso_tool:flush(),
- ?l true=check_noderesults(Nodes,ok,NodeResults4),
-
- %% Check that this function still has a trace pattern. We are going to stop session
- %% and check that it is still there.
- ?l lists:foreach(fun(N)->
- ok=poll(rpc,
- call,
- [N,
- erlang,
- trace_info,
- [{math,sin,1},traced]],
- {traced,local},
- 20)
- end,
- Nodes),
-
- ?l stop_inviso_tool_session(CNode,1,Nodes),
- ?l {ok,{not_tracing,1,TDGargs}}=inviso_tool:get_session_data(),
-
- ?l {ok,NodeResults5}=inviso_tool:flush(Nodes),
- ?l true=check_noderesults(Nodes,fun({_,{error,_}})->true;(_)->false end,NodeResults5),
- ?l {ok,[]}=inviso_tool:flush(),
-
- %% Check that you can not start trace cases when the session is stopped.
- ?l {error,no_session}=inviso_tool:atc(tracecase2,id3,[]),
- %% But the history shall be there to retrieve.
- ?l {ok,{AutostartData3,NodeResults}}=
- inviso_tool:get_autostart_data(Nodes,{dependency,infinity}),
- ?l {ok,{inactive,running}}=inviso_tool:get_node_status(ANode),
-
- %% Check that the trace pattern is still there.
- ?l lists:foreach(fun(N)->
- ok=poll(rpc,
- call,
- [N,
- erlang,
- trace_info,
- [{math,sin,1},traced]],
- {traced,local},
- 20)
- end,
- Nodes),
-
- %% Now start a session and check that the trace patterns is gone.
- ?l start_inviso_tool_session(CNode,[],2,Nodes),
- ?l lists:foreach(fun(N)->
- ok=poll(rpc,
- call,
- [N,
- erlang,
- trace_info,
- [{math,sin,1},traced]],
- {traced,false},
- 20)
- end,
- Nodes),
- ?l stop_inviso_tool_session(CNode,2,Nodes),
-
- ?l stop_inviso_tool(CNode,Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% This test case tests the rtc trace case mechanism.
-dist_rtc(doc) -> [""];
-dist_rtc(suite) -> [];
-dist_rtc(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- [RegExpNode|_]=RemoteNodes,
- CNode=node(),
- Nodes=RemoteNodes,
- DataDir=?config(data_dir,Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
- Opts=[{regexp_node,RegExpNode},
- {tdg,{?MODULE,tdg,[PrivDir]}},
- {tc_def_file,?TC_DEF_FILE},
- {initial_tcs,[{tracecase_init,[]}]},
- {dir,DataDir}], % This is where we find tracecases.
- ?l start_inviso_tool(Nodes,CNode,Opts),
- ?l start_inviso_tool_session(CNode,[],1,Nodes),
- %% Check that the initial tracecase has been executed at all tracing nodes.
- ?l lists:foreach(fun(N)->
- ok=poll(rpc,
- call,
- [N,
- erlang,
- trace_info,
- [{lists,module_info,1},traced]],
- {traced,local},
- 20)
- end,
- Nodes),
- %% Start a test process at every node with a runtime component.
- ?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes),
-
- %% Let the processes start
- timer:sleep(100),
-
- %% Find the pids of the test processes.
- ?l TestProcs=lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_tool_test_proc]) end,
- Nodes),
- ?l true=(1=<length(TestProcs)),
- ?l [ANode|_]=Nodes,
- ?l [{counter,Val}]=rpc:call(ANode,ets,lookup,[test_proc_tab,counter]),
- %% Call the tracecase as an rtc.
- ?l inviso_tool:sync_rtc(tracecase5,[]), % Updates the counter.
- ?l [{counter,Val2}]=rpc:call(ANode,ets,lookup,[test_proc_tab,counter]),
- ?l true=(Val2==Val+1),
- ?l inviso_tool:sync_rtc(tracecase5,[]), % Updates the counter.
- ?l [{counter,Val3}]=rpc:call(ANode,ets,lookup,[test_proc_tab,counter]),
- ?l true=(Val3==Val2+1),
-
- %% Now we stop the session and restore it again.
- ?l stop_inviso_tool_session(CNode,1,Nodes),
- ?l {ok,{2,_InvisoReturn}}=inviso_tool:restore_session(),
- %% The tracecase shall be done twice then.
- ?l ok=poll(rpc,call,[ANode,ets,lookup,[test_proc_tab,counter]],
- fun([{counter,V}]) when V==Val3+2 -> true;
- (_) -> false
- end,
- 20),
- ?l stop_inviso_tool_session(CNode,2,Nodes),
-
- ?l {ok,{AutostartData,_NodeResults}}=
- inviso_tool:get_autostart_data(Nodes,{dependency,infinity}),
- ?l [{file,{_FileNameInit,_}},{file,{FileName,Bindings}},{file,{FileName,Bindings}}]=
- AutostartData,
- ?l stop_inviso_tool(CNode,Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-
-%% This test case tests mainly that reconnect and reinitiations of a node works.
-dist_reconnect(doc) -> [""];
-dist_reconnect(suite) -> [];
-dist_reconnect(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- [RegExpNode|OtherNodes]=RemoteNodes,
- CNode=node(),
- Nodes=RemoteNodes,
- DataDir=?config(data_dir,Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
- Opts=[{regexp_node,RegExpNode},
- {tdg,{?MODULE,tdg,[PrivDir]}},
- {tc_def_file,?TC_DEF_FILE},
- {initial_tcs,[{tracecase_init,[]}]},
- {dir,DataDir}], % This is where we find tracecases.
- ?l start_inviso_tool(Nodes,CNode,Opts),
- ?l start_inviso_tool_session(CNode,[],1,Nodes),
- %% Start a test process at every node with a runtime component.
- ?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes),
-
- %% Let the processes start
- timer:sleep(100),
-
- %% Find the pids of the test processes.
- ?l TestProcs=lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_tool_test_proc]) end,
- Nodes),
- ?l true=(1=<length(TestProcs)),
- ?l ok=inviso_tool:atc(tracecase1,id1,[{'ProcessName',inviso_tool_test_proc}]),
- ?l ok=poll(inviso_tool,get_activities,[],{ok,[]},20),
-
- %% Now we want to crash a node. Lets choose the RegExp node, then we can test
- %% that regexp expansion is done elsewhere too.
- ?l test_server:stop_node(RegExpNode),
- ?l ok=poll(net_adm,ping,[RegExpNode],pang,10),
- %% Make time for the monitoring signal to reach inviso_c and for inviso_c to
- %% inform its subscribers e.g inviso_tool.
- ?l timer:sleep(100),
- ?l {_,[{_Node,Modules}]}=
- inviso_tool_lib:expand_module_names([node()],"application.*",[]),
- ?l NrOfModules=length(Modules),
- %% This also checks that regexp expansion can be made at another node
- %% than RexExpNode.
- ?l {ok,NodeResults1}=inviso_tool:inviso(tp,["application.*",module_info,0,[]]),
- ?l true=check_noderesults(OtherNodes,
- fun({_N,{ok,Ints}}) when is_list(Ints) ->
- NrOfModules=lists:sum(Ints),
- true;
- (_) ->
- false
- end,
- NodeResults1),
-
- %% Do some faulty tests on the dead node.
- ?l {ok,{ok,[{RegExpNode,{error,down}}]}}=
- inviso_tool:reinitiate_session([RegExpNode]),
- ?l {ok,down}=inviso_tool:get_node_status(RegExpNode),
-
- %% Now it is time to restart the crashed node and reconnect it and then
- %% finally reinitiate it.
- ?l RegExpNodeString=atom_to_list(RegExpNode),
- ?l [NodeNameString,_HostNameString] = string:tokens(RegExpNodeString,[$@]),
- ?l RegExpNodeName=list_to_atom(NodeNameString),
- ?l test_server:start_node(RegExpNodeName,peer,[]),
- ?l ok=poll(net_adm,ping,[RegExpNode],pong,20),
- ?l SuiteDir=filename:dirname(code:which(?MODULE)),
- ?l true=rpc:call(RegExpNode,code,add_patha,[SuiteDir]),
- ?l ok=rpc:call(RegExpNode,application,start,[runtime_tools]),
- ?l ok=poll(rpc,call,[RegExpNode,erlang,whereis,[inviso_rt]],undefined,20),
- ?l {ok,down}=inviso_tool:get_node_status(RegExpNode),
-
- %% Restart the test process.
- ?l spawn(RegExpNode,?MODULE,test_proc_init,[]),
- ?l ok=poll(rpc,
- call,
- [RegExpNode,erlang,whereis,[inviso_tool_test_proc]],
- fun(P) when is_pid(P) -> true;
- (undefined) -> false
- end,
- 10),
- ?l TPid=rpc:call(RegExpNode,erlang,whereis,[inviso_tool_test_proc]),
- ?l {ok,[{RegExpNode,{ok,{inactive,running}}}]}=inviso_tool:reconnect_nodes([RegExpNode]),
- %% Try to reconnect the node again and an unknown node.
- ?l UnknownNode='unknown@nonexistant',
- ?l {ok,[{RegExpNode,{error,already_connected}},{UnknownNode,{error,unknown_node}}]}=
- inviso_tool:reconnect_nodes([RegExpNode,UnknownNode]),
- ?l {ok,{ok,[{RegExpNode,{ok,_}}]}}=inviso_tool:reinitiate_session([RegExpNode]),
- ?l ok=poll(rpc,
- call,
- [RegExpNode,erlang,trace_info,[TPid,flags]],
- {flags,[call]},
- 10),
- ?l {ok,{ok,[{RegExpNode,{error,already_in_session}},{UnknownNode,{error,unknown_node}}]}}=
- inviso_tool:reinitiate_session([RegExpNode,UnknownNode]),
-
- %% Suspend RegExpNode and test that it can not be reinitiated.
- ?l {ok,[{RegExpNode,ok}]}=rpc:call(CNode,inviso,suspend,[[RegExpNode],yetatest]),
- ?l {ok,[{RegExpNode,{error,already_connected}}]}=inviso_tool:reconnect_nodes([RegExpNode]),
- ?l {ok,{ok,[{RegExpNode,{error,suspended}}]}}=inviso_tool:reinitiate_session([RegExpNode]),
-
- %% Now we start a tracecase that will never terminate. We then reactivate the
- %% suspended node. Then there will be a running reactivator and a running
- %% tracecase to kill.
- ?l ok=inviso_tool:atc(tracecase4,id4,[]), % This one will not terminate.
- ?l ok=inviso_tool:reactivate(RegExpNode),
- ?l ok=poll(inviso_tool,
- get_activities,
- [],
- fun({ok,L}) when length(L)==2 -> true;
- (_) -> false
- end,
- 20),
- %% Now the reactivator and the tracecase shall be stuck(!)
- ?l {links,ToolLinks}=process_info(whereis(inviso_tool),links),
- ?l [P1,P2]=lists:foldl(fun(P,Acc)->case process_info(P,initial_call) of
- {initial_call,{inviso_tool,_,_}} ->
- [P|Acc];
- _ ->
- Acc
- end
- end,
- [],
- ToolLinks),
- ?l stop_inviso_tool_session(CNode,1,Nodes),
- %% Check that the processes are killed.
- ?l ok=poll(erlang,process_info,[P1],undefined,10),
- ?l ok=poll(erlang,process_info,[P2],undefined,10),
- ?l stop_inviso_tool(CNode,Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% This test tests that we can adopt a running inviso runtime component and
-%% mark it as tracing-running.
-dist_adopt(doc) -> [""];
-dist_adopt(suite) -> [];
-dist_adopt(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- [RegExpNode|_]=RemoteNodes,
- CNode=node(),
- Nodes=RemoteNodes,
- DataDir=?config(data_dir,Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
-
- %% Then first start runtime components at different nodes for us to
- %% later adopt.
- ?l {ok,_IPid}=inviso:start(),
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_tag,[{dependency,infinity}]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l [ANode|OtherNodes]=Nodes,
- ?l {ok,[{ANode,_LogResult}]}=
- inviso:init_tracing([ANode],
- [{trace,{file,filename:join(PrivDir,"dist_adopt_adoptednode.log")}},
- {ti,{file,filename:join(PrivDir,"dist_adopt_adoptednode.ti")}}]),
- ?l inviso:stop(),
- ?l ok=poll(erlang,whereis,[inviso_c],undefined,10),
- ?l lists:foreach(fun(N)->true=(is_pid(rpc:call(N,erlang,whereis,[inviso_rt]))) end,
- Nodes),
-
- %% Now start the tool and watch it adopt the runtimes.
- Opts=[{regexp_node,RegExpNode},
- {tdg,{?MODULE,tdg,[PrivDir]}},
- {tc_def_file,?TC_DEF_FILE},
- {initial_tcs,[{tracecase_init,[]}]},
- {dir,DataDir}], % This is where we find tracecases.
- ?l Options=[{nodes,Nodes},{c_node,CNode}|Opts],
- ?l {ok,_Pid}=inviso_tool:start(Options),
- ?l ok=poll(erlang,whereis,[inviso_tool],fun(X)->true=is_pid(X) end,10),
- ?l io:format("LoopData:~p~n",[inviso_tool:get_loopdata()]),
- ?l {ok,{1,InvisoReturn}}=inviso_tool:start_session([]),
- ?l io:format("Invisoreturn:~p~n",[InvisoReturn]),
- %% Now check that all nodes are tracing.
- ?l lists:foreach(fun(N)->ok=poll(rpc,
- call,
- [CNode,inviso,get_status,[[N]]],
- fun({ok,[{_N,{ok,{tracing,running}}}]})->true;
- (_) ->false
- end,
- 10)
- end,
- Nodes),
-
- %% At this point all nodes shall be tracing. However the initial tracecase
- %% shall not have been executed at ANode since it was adopted by the tool.
- ?l {traced,false}=rpc:call(ANode,erlang,trace_info,[{lists,module_info,1},traced]),
- ?l lists:foreach(fun(N)->
- {traced,local}=
- rpc:call(N,erlang,trace_info,[{lists,module_info,1},traced])
- end,
- OtherNodes),
- ?l stop_inviso_tool_session(CNode,1,Nodes),
- ?l [BNode|_]=OtherNodes,
- %% Since nodes are not cleared the pattern still be there.
- ?l {traced,local}=rpc:call(BNode,erlang,trace_info,[{lists,module_info,1},traced]),
- ?l start_inviso_tool_session(CNode,[],2,Nodes),
- ?l stop_inviso_tool_session(CNode,2,Nodes),
- ?l {ok,_NodeResults}=inviso_tool:stop(),
- ?l ok=poll(erlang,whereis,[inviso_tool],undefined,10),
- ?l ok=poll(rpc,call,[CNode,erlang,whereis,[inviso_c]],undefined,10),
-
- ok.
-%% -----------------------------------------------------------------------------
-
-%% This test tests that saving and restoring a history works.
-dist_history(doc) -> [""];
-dist_history(suite) -> [];
-dist_history(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- [RegExpNode|_]=RemoteNodes,
- CNode=RegExpNode, % We use a remote control component.
- Nodes=RemoteNodes,
- DataDir=?config(data_dir,Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
-
- %% Start up the tool and a couple of inviso runtimes.
- Opts=[{regexp_node,RegExpNode},
- {tdg,{?MODULE,tdg,[PrivDir]}},
- {tc_def_file,?TC_DEF_FILE},
- {initial_tcs,[{tracecase_init,[]}]},
- {dir,DataDir}], % This is where we find tracecases.
- ?l start_inviso_tool(Nodes,CNode,Opts),
- ?l start_inviso_tool_session(CNode,[],1,Nodes),
- %% Start a test process at every node with a runtime component.
- ?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes),
-
- %% Activate tracing of the test process.
- ?l ok=inviso_tool:atc(tracecase1,id1,[{'ProcessName',inviso_tool_test_proc}]),
- ?l ok=poll(inviso_tool,get_activities,[],{ok,[]},10),
- ?l TestProcs=lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_tool_test_proc]) end,
- Nodes),
- ?l lists:foreach(fun(P)->
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[P,flags]],
- {flags,[call]},
- 10),
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[{math,module_info,1},traced]],
- {traced,local},
- 10)
- end,
- TestProcs),
-
- %% Create a history file.
- ?l AbsFileName=filename:join(PrivDir,"dist_history.his"),
- ?l {ok,AbsFileName}=inviso_tool:save_history(AbsFileName),
- ?l {ok,_FileInfo}=file:read_file_info(AbsFileName),
-
- %% Stop the tracing of the test process.
- ?l inviso_tool:sync_dtc(tracecase1,id1),
- ?l lists:foreach(fun(P)->
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[P,flags]],
- {flags,[]},
- 10),
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[{math,module_info,1},traced]],
- {traced,false},
- 10)
- end,
- TestProcs),
- %% Now stop the session.
- ?l stop_inviso_tool_session(CNode,1,Nodes),
- %% Restart the session using the previously saved history.
- ?l {ok,{2,_InvisoReturn}}=inviso_tool:restore_session(AbsFileName),
- ?l ok=poll(inviso_tool,get_activities,[],{ok,[]},10),
- %% Check that the history has been redone.
- ?l lists:foreach(fun(P)->
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[P,flags]],
- {flags,[call]},
- 10),
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[{math,module_info,1},traced]],
- {traced,local},
- 10)
- end,
- TestProcs),
- ?l {ok,_NodeResults1}=inviso_tool:inviso(tpl,[math,module_info,0,[]]),
- %% Also check that the restored history now is our history.
- ?l {ok,{AutostartData,_NodeResults2}}=
- inviso_tool:get_autostart_data(Nodes,{dependency,infinity}),
- ?l FNameInit=filename:join(DataDir,"tracecase_init.trc"),
- ?l FName1=filename:join(DataDir,"tracecase1_on.trc"),
- ?l [{file,{FNameInit,[]}},
- {file,{FName1,[{'ProcessName',inviso_tool_test_proc}]}},
- {mfa,{inviso,tpl,[math,module_info,0,[]]}}]=AutostartData,
- ?l stop_inviso_tool_session(CNode,2,Nodes),
- ?l NodeCounters=lists:foldl(fun(N,Acc)->[{_,X}]=rpc:call(N,ets,lookup,[test_proc_tab,counter]),
- [{N,X}|Acc]
- end,
- [],
- Nodes),
- %% Remove the patterns set by the initial tracecase.
- ?l lists:foreach(fun(N)->rpc:call(N,
- erlang,
- trace_pattern,
- [{lists,module_info,1},false,[local]])
- end,
- Nodes),
- %% Now we want to test that we can do restore on the current session.
- ?l {ok,{3,_InvisoReturn2}}=inviso_tool:restore_session(),
- ?l ok=poll(inviso_tool,get_activities,[],{ok,[]},10),
- %% Check that the history has been redone yet again.
- ?l lists:foreach(fun({N,X})->
- [{counter,Y}]=
- rpc:call(N,ets,lookup,[test_proc_tab,counter]),
- Y=X+1
- end,
- NodeCounters),
- ?l lists:foreach(fun(N)->
- {traced,local}=
- rpc:call(N,erlang,trace_info,[{lists,module_info,1},traced])
- end,
- Nodes),
-
- ?l {error,session_active}=inviso_tool:reset_nodes(Nodes),
- %% Now stop the session and check that we can clear the nodes.
- ?l stop_inviso_tool_session(CNode,3,Nodes),
- ?l lists:foreach(fun(N)->{traced,local}=
- rpc:call(N,erlang,trace_info,[{lists,module_info,1},traced])
- end,
- Nodes),
- ?l {ok,NodeResults3}=inviso_tool:reset_nodes(Nodes),
- ?l true=check_noderesults(Nodes,{ok,{new,running}},NodeResults3),
- ?l lists:foreach(fun(N)->{traced,false}=
- rpc:call(N,erlang,trace_info,[{lists,module_info,1},traced])
- end,
- Nodes),
- ?l stop_inviso_tool(CNode,Nodes),
-
- %% Now we want to test that restoring a session at no active nodes will
- %% not result in a crash. (Previous error).
- ?l FaultyNodes=[gurka@nonexistant,tomat@nonexistant],
- ?l Options=[{nodes,FaultyNodes},{c_node,CNode}|Opts],
- ?l {ok,_Pid}=inviso_tool:start(Options),
- ?l ok=poll(erlang,whereis,[inviso_tool],fun(X)->true=is_pid(X) end,10),
- %% Now try to restore a session.
- ?l {ok,{_,{ok,[]}}}=inviso_tool:restore_session(AbsFileName),
- ?l {ok,down}=inviso_tool:get_node_status(gurka@nonexistant),
- %% Now stop the (useless) session.
- ?l {ok,{_,[]}}=inviso_tool:stop_session(),
- ?l stop_inviso_tool(CNode,[]),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% This test tests a few strange situations when activating a session and there
-%% are no nodes that can be initiated or reinitiated.
-dist_start_session_special(doc) -> [""];
-dist_start_session_special(suite) -> [];
-dist_start_session_special(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- [RegExpNode|_]=RemoteNodes,
- CNode=RegExpNode, % We use a remote control component.
-% Nodes=RemoteNodes,
- DataDir=?config(data_dir,Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
-
- %% Start up the tool but with no exiting nodes.
- FaultyNodes=[gurka@nonexistant,tomat@nonexistant],
- Opts=[{regexp_node,RegExpNode},
- {tdg,{?MODULE,tdg,[PrivDir]}},
- {tc_def_file,?TC_DEF_FILE},
- {initial_tcs,[{tracecase_init,[]}]},
- {dir,DataDir}], % This is where we find tracecases.
- ?l Options=[{nodes,FaultyNodes},{c_node,CNode}|Opts],
- ?l {ok,_Pid}=inviso_tool:start(Options),
- ?l ok=poll(erlang,whereis,[inviso_tool],fun(X)->true=is_pid(X) end,10),
- %% Now try to initate a session.
- ?l {ok,{SessionNr,{ok,[]}}}=inviso_tool:start_session(),
- ?l {ok,down}=inviso_tool:get_node_status(gurka@nonexistant),
- %% Now stop the (useless) session.
- ?l {ok,{SessionNr,[]}}=inviso_tool:stop_session(),
-
- %% Now start again, still no useful nodes.
- ?l {ok,{SessionNr2,{ok,[]}}}=inviso_tool:start_session(),
- ?l {ok,{SessionNr2,[]}}=inviso_tool:stop_session(),
- ?l stop_inviso_tool(CNode,[]), % No nodes are connected.
-
- ok.
-%% -----------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Help functions.
-%% ==============================================================================
-
-%% Help function starting the inviso_tool with runtime components at Nodes
-%% and the inviso control component at CNode. OtherOpts shall contain all other
-%% necessary options except nodes and c_node. Returns nothing significant.
-start_inviso_tool(Nodes,CNode,OtherOpts) ->
- ?l Options=[{nodes,Nodes},{c_node,CNode}|OtherOpts],
- ?l {ok,_Pid}=inviso_tool:start(Options),
- ?l ok=poll(erlang,whereis,[inviso_tool],fun(X)->true=is_pid(X) end,10),
- %% Now the runtime components shall be started but no tracing started.
- ?l lists:foreach(fun(N)->ok=poll(rpc,
- call,
- [CNode,inviso,get_status,[[N]]],
- fun({ok,[{_N,{ok,{new,running}}}]})->true;
- (_) ->false
- end,
- 10)
- end,
- Nodes),
- true.
-%% -----------------------------------------------------------------------------
-
-%% Stops the inviso_tool.
-stop_inviso_tool(CNode,Nodes) ->
- ?l {ok,NodeResults}=inviso_tool:stop(),
- ?l true=check_noderesults(Nodes,ok,NodeResults),
- ?l ok=poll(erlang,whereis,[inviso_tool],undefined,10),
- %% Check that all inviso components are gone.
- ?l ok=poll(rpc,call,[CNode,erlang,whereis,[inviso_c]],undefined,10),
- ?l lists:foreach(fun(N)->ok=poll(rpc,
- call,
- [N,erlang,whereis,[inviso_rt]],
- undefined,
- 10)
- end,
- Nodes),
- true.
-%% -----------------------------------------------------------------------------
-
-%% Starts a trace session. Returns the InvisoReturn part of the return value.
-start_inviso_tool_session(CNode,MoreTDGargs,SessionNr,Nodes) ->
- ?l {ok,{SessionNr,InvisoReturn}}=inviso_tool:start_session(MoreTDGargs),
- %% Now check that all nodes are tracing.
- ?l lists:foreach(fun(N)->ok=poll(rpc,
- call,
- [CNode,inviso,get_status,[[N]]],
- fun({ok,[{_N,{ok,{tracing,running}}}]})->true;
- (_) ->false
- end,
- 10),
- %% Check that the initial trace case is executed.
- ?l ok=poll(rpc,
- call,
- [N,erlang,trace_info,[{lists,module_info,1},traced]],
- {traced,local},
- 10)
- end,
- Nodes),
- InvisoReturn.
-%% -----------------------------------------------------------------------------
-
-%% Stops a trace session.
-stop_inviso_tool_session(CNode,SessionNr,Nodes) ->
- ?l {ok,{SessionNr,NodeResults}}=inviso_tool:stop_session(),
- ?l true=check_noderesults(Nodes,ok,NodeResults),
- %% Now the runtimes shall not be tracing any longer.
- ?l lists:foreach(fun(N)->ok=poll(rpc,
- call,
- [CNode,inviso,get_status,[[N]]],
- fun({ok,[{_N,{ok,{idle,running}}}]})->true;
- (_) ->false
- end,
- 10)
- end,
- Nodes),
- true.
-%% -----------------------------------------------------------------------------
-
-%% Help function checking that there is a Result for each node in Nodes.
-%% Returns 'true' if successful.
-check_noderesults(Nodes,Fun,[{Node,Result}|Rest]) when is_function(Fun) ->
- case Fun({Node,Result}) of
- true ->
- case lists:member(Node,Nodes) of
- true ->
- check_noderesults(lists:delete(Node,Nodes),Fun,Rest);
- false -> % Not good.
- unknown_node_in_returnvalue
- end;
- _ ->
- illegal_result
- end;
-check_noderesults(Nodes,Result,[{Node,Result}|Rest]) ->
- case lists:member(Node,Nodes) of
- true ->
- check_noderesults(lists:delete(Node,Nodes),Result,Rest);
- false -> % Not good.
- unknown_node_in_returnvalue
- end;
-check_noderesults([],_,[]) ->
- true;
-check_noderesults(X,Y,Z) ->
- io:format("Bad arguments to check noderesults:~w~n~w~n~w~n",[X,Y,Z]),
- false.
-%% ------------------------------------------------------------------------------
-
-%% Help function which waits for a function call to become Result. This is useful
-%% if what we are waiting for can happend independantly of indications we have
-%% access to.
-poll(_,_,_,_,0) ->
- error;
-poll(M,F,Args,Result,Times) ->
- try apply(M,F,Args) of
- What when is_function(Result) ->
- case Result(What) of
- true ->
- ok;
- X ->
- io:format("Poll: ~w:~w ~w ~w ~w~n",[M,F,Args,Result,X]),
- timer:sleep(100),
- poll(M,F,Args,Result,Times-1)
- end;
- Result ->
- ok;
- X ->
- io:format("Poll: ~w:~w ~w ~w ~w~n",[M,F,Args,Result,X]),
- timer:sleep(100),
- poll(M,F,Args,Result,Times-1)
- catch
- error:Reason ->
- io:format("Apply in suite-function poll/5 failed, ~w~n",[Reason]),
- timer:sleep(100),
- poll(M,F,Args,Result,Times-1)
- end.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% The Tracer Data Generator function.
-%% ------------------------------------------------------------------------------
-
--define(I2L(Arg),integer_to_list(Arg)).
-
-tdg(Node,{{Y,Mo,D},{H,Mi,S}},PrivDir) ->
- NameStr=atom_to_list(Node)++"_"++?I2L(Y)++"-"++?I2L(Mo)++"-"++?I2L(D)++"_"++
- ?I2L(H)++"-"++?I2L(Mi)++"-"++?I2L(S),
- LogTD={file,filename:join(PrivDir,NameStr++".log")},
- TiTD={file,filename:join(PrivDir,NameStr++".ti")},
- [{trace,LogTD},{ti,TiTD}].
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Handling the test server Config.
-%% ------------------------------------------------------------------------------
-
-insert_remotenode_config(Name,Node,Config) ->
- [{remotenode,{Name,Node}}|Config].
-%% ------------------------------------------------------------------------------
-
-insert_timetraphandle_config(Handle,Config) ->
- [{timetraphandle,Handle}|Config].
-%% ------------------------------------------------------------------------------
-
-get_remotenode_config(Name, [{remotenode, {Name, Node}}| _Cs]) ->
- Node;
-get_remotenode_config(Name, [_C | Cs]) ->
- get_remotenode_config(Name, Cs);
-get_remotenode_config(Name, []) ->
- exit({no_remotenode, Name}).
-
-%% ------------------------------------------------------------------------------
-
-get_timetraphandle_config(Config) ->
- {value,{_,Handle}}=lists:keysearch(timetraphandle,1,Config),
- Handle.
-%% ------------------------------------------------------------------------------
-
-get_remotenodes_config([{remotenode,{_Name,Node}}|Config]) ->
- [Node|get_remotenodes_config(Config)];
-get_remotenodes_config([_|Config]) ->
- get_remotenodes_config(Config);
-get_remotenodes_config([]) ->
- [].
-%% ------------------------------------------------------------------------------
-
-remove_remotenode_config(Name, [{remotenode, {Name, _}} | Cs]) ->
- Cs;
-remove_remotenode_config(Name, [C | Cs]) ->
- [C | remove_remotenode_config(Name, Cs)];
-remove_remotenode_config(_Name, []) ->
- [].
-%% ------------------------------------------------------------------------------
-
-remove_timetraphandle_config(Config) ->
- lists:keydelete(timetraphandle,1,Config).
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Code for a test process which can be started.
-%% ==============================================================================
-
-%% The test proc is also responsible for owning a side effect table. The table
-%% can be updated by tracecases.
-test_proc_init() ->
- register(inviso_tool_test_proc,self()),
- ets:new(test_proc_tab,[named_table,public]),
- ets:insert(test_proc_tab,{counter,0}),
- test_proc_loop().
-
-test_proc_loop() ->
- receive
- {apply,M,F,Args} ->
- apply(M,F,Args),
- test_proc_loop();
- X ->
- io:format("Got ~w~n",[X]),
- test_proc_loop()
- end.
-%% ------------------------------------------------------------------------------
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase1_off.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase1_off.trc
deleted file mode 100644
index 426c1ed9f9..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase1_off.trc
+++ /dev/null
@@ -1,12 +0,0 @@
-%% TRACECASE1_OFF.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% Trace case deactivating the trace started by the activation case tracecase1_on.
-%%
-%% ProcessName=atom(), variable set in the test environment.
-%% Nodes=list(), inviso_tool variable - all traced nodes.
-%% -----------------------------------------------------------------------------
-
-inviso:ctf(Nodes,ProcessName,[call]).
-inviso:ctpl(Nodes,math,module_info,1).
-%% END-OF-TRACE-CASE
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase1_on.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase1_on.trc
deleted file mode 100644
index a9106dbc78..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase1_on.trc
+++ /dev/null
@@ -1,17 +0,0 @@
-%% TRACECASE1_ON.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% Trace case setting a local pattern on math:module_info/1 and process flags
-%% on a test process which is supposed to be started by the test environment.
-%%
-%% ProcessName=atom(), variable set in the test environment.
-%% Nodes=list(), inviso_tool variable - all traced nodes.
-%% -----------------------------------------------------------------------------
-
-inviso:tpl(Nodes,math,module_info,1,[]).
-inviso:tf(Nodes,ProcessName,[call]).
-lists:foreach(fun(N)->rpc:call(N,ets,update_counter,[test_proc_tab,counter,1]) end,
- Nodes).
-timer:sleep(500).
-%% END-OF-TRACE-CASE
-
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase2_off.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase2_off.trc
deleted file mode 100644
index cc89c3aa03..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase2_off.trc
+++ /dev/null
@@ -1,12 +0,0 @@
-%% TRACECASE2_OFF.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% The tracecase is mainly used for testing that synchronous tracecases return
-%% values.
-%%
-%% Nodes=list(), inviso_tool variable - all traced nodes.
-%% -----------------------------------------------------------------------------
-
-inviso:ctpl(Nodes,math,pi,0).
-another_return_value.
-%% END-OF-TRACE-CASE
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase2_on.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase2_on.trc
deleted file mode 100644
index a3ab5fcfc7..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase2_on.trc
+++ /dev/null
@@ -1,16 +0,0 @@
-%% TRACECASE2_ON.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% The tracecase is mainly used for testing that synchronous tracecases return
-%% values.
-%% We also use this tracecase to check that reactivation works when it comes to
-%% handling simulataneously issued tool commands (issued during reactivation).
-%%
-%% Nodes=list(), inviso_tool variable - all traced nodes.
-%% -----------------------------------------------------------------------------
-
-inviso:tpl(Nodes,math,pi,0,[]).
-timer:sleep(3000).
-a_return_value.
-%% END-OF-TRACE-CASE
-
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase3_on.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase3_on.trc
deleted file mode 100644
index e6c5ff78b1..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase3_on.trc
+++ /dev/null
@@ -1,9 +0,0 @@
-%% TRACECASE3_ON.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% It is faulty and meant to cause a crash!
-%% -----------------------------------------------------------------------------
-
-1=2.
-%% END-OF-TRACE-CASE
-
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase4_on.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase4_on.trc
deleted file mode 100644
index d14c11f78c..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase4_on.trc
+++ /dev/null
@@ -1,9 +0,0 @@
-%% TRACECASE4_ON.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% It contains an infinity timer in order for the tracecase executer to hang.
-%% -----------------------------------------------------------------------------
-
-timer:sleep(infinity).
-%% END-OF-TRACE-CASE
-
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase5_off.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase5_off.trc
deleted file mode 100644
index feb67acb11..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase5_off.trc
+++ /dev/null
@@ -1,11 +0,0 @@
-%% TRACECASE5_OFF.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% Contains a 2 second sleep.
-%% -----------------------------------------------------------------------------
-
-lists:foreach(fun(N)->rpc:call(N,ets,update_counter,[test_proc_tab,counter,1]) end,
- Nodes).
-timer:sleep(2000).
-%% END-OF-TRACE-CASE
-
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase5_on.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase5_on.trc
deleted file mode 100644
index 724c617c5a..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase5_on.trc
+++ /dev/null
@@ -1,11 +0,0 @@
-%% TRACECASE5_ON.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% This tracecase updates an ETS table. Can be used to verify that it has been
-%% done (or not done!).
-%% -----------------------------------------------------------------------------
-
-lists:foreach(fun(N)->rpc:call(N,ets,update_counter,[test_proc_tab,counter,1]) end,
- Nodes).
-%% END-OF-TRACE-CASE
-
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase_def.txt b/lib/inviso/test/inviso_tool_SUITE_data/tracecase_def.txt
deleted file mode 100644
index 5b08fa32a5..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase_def.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-{tracecase_init,on,[],"./tracecase_init.trc"}.
-{tracecase1,on_off,['ProcessName'],"./tracecase1_on.trc","./tracecase1_off.trc"}.
-{tracecase2,on_off,[],"./tracecase2_on.trc","./tracecase2_off.trc"}.
-{tracecase3,on,[],"./tracecase3_on.trc"}.
-{tracecase4,on,[],"./tracecase4_on.trc"}.
-{tracecase5,on_off,[],"./tracecase5_on.trc","./tracecase5_off.trc"}.
-
-
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase_init.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase_init.trc
deleted file mode 100644
index 49a79cd3a5..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase_init.trc
+++ /dev/null
@@ -1,10 +0,0 @@
-%% TRACECASE_INIT.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% Initial trace case executed at session start.
-%%
-%% Nodes=list(), inviso_tool variable - all traced nodes.
-%% -----------------------------------------------------------------------------
-
-inviso:tpl(Nodes,lists,module_info,1,[]).
-%% END-OF-TRACE-CASE
diff --git a/lib/inviso/vsn.mk b/lib/inviso/vsn.mk
deleted file mode 100644
index c6b0398bde..0000000000
--- a/lib/inviso/vsn.mk
+++ /dev/null
@@ -1 +0,0 @@
-INVISO_VSN = 0.6.3
diff --git a/lib/jinterface/doc/src/Makefile b/lib/jinterface/doc/src/Makefile
index 858c97f187..4d5713566c 100644
--- a/lib/jinterface/doc/src/Makefile
+++ b/lib/jinterface/doc/src/Makefile
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2010. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/jinterface/doc/src/notes.xml b/lib/jinterface/doc/src/notes.xml
index 420e121389..19f25e3681 100644
--- a/lib/jinterface/doc/src/notes.xml
+++ b/lib/jinterface/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2011</year>
+ <year>2000</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
index ec817aa39b..8ae63a1561 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/jinterface/test/Makefile b/lib/jinterface/test/Makefile
index b70521c08d..d9ff406994 100644
--- a/lib/jinterface/test/Makefile
+++ b/lib/jinterface/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2011. All Rights Reserved.
+# Copyright Ericsson AB 2004-2012. 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/lib/jinterface/test/jitu.erl b/lib/jinterface/test/jitu.erl
index c57fb9bfad..fb262cf9d7 100644
--- a/lib/jinterface/test/jitu.erl
+++ b/lib/jinterface/test/jitu.erl
@@ -89,13 +89,19 @@ classpath(Dir) ->
{win32, _} -> ";";
_ -> ":"
end,
- Dir++PS++
+ es(Dir++PS++
filename:join([code:lib_dir(jinterface),"priv","OtpErlang.jar"])++PS++
case os:getenv("CLASSPATH") of
false -> "";
Classpath -> Classpath
- end.
-
+ end).
+
+es(L) ->
+ lists:flatmap(fun($ ) ->
+ "\\ ";
+ (C) ->
+ [C]
+ end,lists:flatten(L)).
cmd(Cmd) ->
PortOpts = [{line,80},eof,exit_status,stderr_to_stdout],
diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile
index 78e5f7bc26..5e04bff0c1 100644
--- a/lib/kernel/doc/src/Makefile
+++ b/lib/kernel/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml
index ee687511a3..279c7558bc 100644
--- a/lib/kernel/doc/src/code.xml
+++ b/lib/kernel/doc/src/code.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -91,7 +91,7 @@
of the additional library directories will override modules with
the same name in OTP, except for modules in Kernel and
STDLIB.</p>
- <p>The environment variable <c>ERL_LIBS</c> (if defined) shold contain
+ <p>The environment variable <c>ERL_LIBS</c> (if defined) should contain
a colon-separated (for Unix-like systems) or semicolon-separated
(for Windows) list of additional libraries.</p>
<p>Example: On an Unix-like system, <c>ERL_LIBS</c> could be set to
@@ -728,16 +728,13 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]),
</desc>
</func>
<func>
- <name>is_module_native(Module) -> boolean() | undefined</name>
+ <name name="is_module_native" arity="1"/>
<fsummary>Test whether a module has native code</fsummary>
- <type>
- <v>Module = module()</v>
- </type>
<desc>
- <p>This function returns <c>true</c> if <c>Module</c> is
+ <p>This function returns <c>true</c> if <c><anno>Module</anno></c> is
name of a loaded module that has native code loaded, and
- <c>false</c> if <c>Module</c> is loaded but does not have
- native. If <c>Module</c> is not loaded, this function returns
+ <c>false</c> if <c><anno>Module</anno></c> is loaded but does not have
+ native. If <c><anno>Module</anno></c> is not loaded, this function returns
<c>undefined</c>.</p>
</desc>
</func>
diff --git a/lib/kernel/doc/src/erl_ddll.xml b/lib/kernel/doc/src/erl_ddll.xml
index 1911fb628e..26db11cfcd 100644
--- a/lib/kernel/doc/src/erl_ddll.xml
+++ b/lib/kernel/doc/src/erl_ddll.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2011</year>
+ <year>1997</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -182,11 +182,8 @@
</datatypes>
<funcs>
<func>
- <name>demonitor(MonitorRef) -> ok</name>
+ <name name="demonitor" arity="1"/>
<fsummary>Remove a monitor for a driver</fsummary>
- <type>
- <v>MonitorRef = reference()</v>
- </type>
<desc>
<p>Removes a driver monitor in much the same way as
<seealso marker="erts:erlang#erlang:demonitor/1">erlang:demonitor/1</seealso> does with process
@@ -232,24 +229,19 @@
</desc>
</func>
<func>
- <name>info(Name, Tag) -> Value</name>
+ <name name="info" arity="2"/>
<fsummary>Retrieve specific information about one driver</fsummary>
- <type>
- <v>Name = string() | atom()</v>
- <v>Tag = processes | driver_options | port_count | linked_in_driver | permanent | awaiting_load | awaiting_unload</v>
- <v>Value = term()</v>
- </type>
<desc>
<p>This function returns specific information about one aspect
- of a driver. The <c>Tag</c> parameter specifies which aspect
- to get information about. The <c>Value</c> return differs
+ of a driver. The <c><anno>Tag</anno></c> parameter specifies which aspect
+ to get information about. The <c><anno>Value</anno></c> return differs
between different tags:</p>
<taglist>
<tag><em>processes</em></tag>
<item>
<p>Return all processes containing <seealso marker="#users">users</seealso> of the specific drivers
- as a list of tuples <c>{pid(),int()}</c>, where the
- <c>int()</c> denotes the number of users in the process
+ as a list of tuples <c>{pid(),integer() >= 0}</c>, where the
+ <c>integer()</c> denotes the number of users in the process
<c>pid()</c>.</p>
</item>
<tag><em>driver_options</em></tag>
@@ -261,16 +253,16 @@
</item>
<tag><em>port_count</em></tag>
<item>
- <p>Return the number of ports (an <c>int()</c>) using the driver.</p>
+ <p>Return the number of ports (an <c>integer >= 0()</c>) using the driver.</p>
</item>
<tag><em>linked_in_driver</em></tag>
<item>
- <p>Return a <c>bool()</c>, being <c>true</c> if the driver is a
+ <p>Return a <c>boolean()</c>, being <c>true</c> if the driver is a
statically linked in one and <c>false</c> otherwise.</p>
</item>
<tag><em>permanent</em></tag>
<item>
- <p>Return a <c>bool()</c>, being <c>true</c> if the driver has made
+ <p>Return a <c>boolean()</c>, being <c>true</c> if the driver has made
itself permanent (and is <em>not</em> a statically
linked in driver). <c>false</c> otherwise.</p>
</item>
@@ -278,14 +270,14 @@
<item>
<p>Return a list of all processes having monitors for
<c>loading</c> active, each process returned as
- <c>{pid(),int()}</c>, where the <c>int()</c> is the
+ <c>{pid(),integer() >= 0}</c>, where the <c>integer()</c> is the
number of monitors held by the process <c>pid()</c>.</p>
</item>
<tag><em>awaiting_unload</em></tag>
<item>
<p>Return a list of all processes having monitors for
<c>unloading</c> active, each process returned as
- <c>{pid(),int()}</c>, where the <c>int()</c> is the
+ <c>{pid(),integer() >= 0}</c>, where the <c>integer()</c> is the
number of monitors held by the process <c>pid()</c>.</p>
</item>
</taglist>
@@ -377,41 +369,34 @@
</desc>
</func>
<func>
- <name>monitor(Tag, Item) -> MonitorRef</name>
+ <name name="monitor" arity="2"/>
<fsummary>Create a monitor for a driver</fsummary>
- <type>
- <v>Tag = driver </v>
- <v>Item = {Name, When}</v>
- <v>Name = atom() | string()</v>
- <v>When = loaded | unloaded | unloaded_only</v>
- <v>MonitorRef = reference()</v>
- </type>
<desc>
<p>This function creates a driver monitor and works in many
ways as the function <seealso marker="erts:erlang#erlang:monitor/2">erlang:monitor/2</seealso>,
does for processes. When a driver changes state, the monitor
results in a monitor-message being sent to the calling
- process. The <c>MonitorRef</c> returned by this function is
+ process. The <c><anno>MonitorRef</anno></c> returned by this function is
included in the message sent.</p>
<p>As with process monitors, each driver monitor set will only
generate <em>one single message</em>. The monitor is
"destroyed" after the message is sent and there is then no
need to call <seealso marker="#demonitor/1">demonitor/1</seealso>.</p>
- <p>The <c>MonitorRef</c> can also be used in subsequent calls
+ <p>The <c><anno>MonitorRef</anno></c> can also be used in subsequent calls
to <seealso marker="#demonitor/1">demonitor/1</seealso> to
remove a monitor.</p>
<p>The function accepts the following parameters:</p>
<taglist>
- <tag><em>Tag</em></tag>
+ <tag><em><anno>Tag</anno></em></tag>
<item>
<p>The monitor tag is always <c>driver</c> as this function
can only be used to create driver monitors. In the future,
driver monitors will be integrated with process monitors,
why this parameter has to be given for consistence.</p>
</item>
- <tag><em>Item</em></tag>
+ <tag><em><anno>Item</anno></em></tag>
<item>
- <p>The <c>Item</c> parameter specifies which driver one
+ <p>The <c><anno>Item</anno></c> parameter specifies which driver one
wants to monitor (the name of the driver) as well as
which state change one wants to monitor. The parameter
is a tuple of arity two whose first element is the
@@ -588,22 +573,8 @@
</desc>
</func>
<func>
- <name>try_load(Path, Name, OptionList) -> {ok,Status} | {ok, PendingStatus, Ref} | {error, ErrorDesc}</name>
+ <name name="try_load" arity="3"/>
<fsummary>Load a driver</fsummary>
- <type>
- <v>Path = Name = string() | atom()</v>
- <v>OptionList = [ Option ]</v>
- <v>Option = {driver_options, DriverOptionList} | {monitor, MonitorOption} | {reload, ReloadOption}</v>
- <v>DriverOptionList = [ DriverOption ]</v>
- <v>DriverOption = kill_ports</v>
- <v>MonitorOption = pending_driver | pending</v>
- <v>ReloadOption = pending_driver | pending</v>
- <v>Status = loaded | already_loaded | PendingStatus </v>
- <v>PendingStatus = pending_driver | pending_process</v>
- <v>Ref = reference()</v>
- <v>ErrorDesc = ErrorAtom | OpaqueError</v>
- <v>ErrorAtom = linked_in_driver | inconsistent | permanent | not_loaded_by_this_process | not_loaded | pending_reload | pending_process</v>
- </type>
<desc>
<p>This function provides more control than the
<c>load/2</c>/<c>reload/2</c> and
@@ -655,65 +626,65 @@
<p>When the function returns <c>{ok, pending_driver}</c> or
<c>{ok, pending_process}</c>, one might want to get information
about when the driver is <em>actually</em> loaded. This can
- be achieved by using the <c>{monitor, PendingOption}</c> option.</p>
+ be achieved by using the <c>{monitor, <anno>MonitorOption</anno>}</c> option.</p>
<p>When monitoring is requested, and a corresponding <c>{ok, pending_driver}</c> or <c>{ok, pending_process}</c> would be
- returned, the function will instead return a tuple <c>{ok, PendingStatus, reference()}</c> and the process will, at a later
+ returned, the function will instead return a tuple <c>{ok, <anno>PendingStatus</anno>, reference()}</c> and the process will, at a later
time when the driver actually gets loaded, get a monitor
message. The monitor message one can expect is described in
the <seealso marker="#monitor/2">monitor/2</seealso>
function description. </p>
<note>
<p>Note that in case of loading, monitoring can
- <em>not</em> only get triggered by using the <c>{reload, ReloadOption}</c> option, but also in special cases where
+ <em>not</em> only get triggered by using the <c>{reload, <anno>ReloadOption</anno>}</c> option, but also in special cases where
the load-error is transient, why <c>{monitor, pending_driver}</c> should be used under basically
<em>all</em> real world circumstances!</p>
</note>
<p>The function accepts the following parameters:</p>
<taglist>
- <tag><em>Path</em></tag>
+ <tag><em><anno>Path</anno></em></tag>
<item>
<p>The filesystem path to the directory where the driver
object file is situated. The filename of the object file
(minus extension) must correspond to the driver name
(used in the name parameter) and the driver must
identify itself with the very same name. The
- <c>Path</c> might be provided as an <em>io_list</em>,
- meaning it can be a list of other io_lists, characters
+ <c><anno>Path</anno></c> might be provided as an <em>iolist()</em>,
+ meaning it can be a list of other <c>iolist()</c>s, characters
(eight bit integers) or binaries, all to be flattened
into a sequence of characters.</p>
- <p>The (possibly flattened) <c>Path</c> parameter must be
+ <p>The (possibly flattened) <c><anno>Path</anno></c> parameter must be
consistent throughout the system, a driver should, by
all <seealso marker="#users">users</seealso>, be loaded
- using the same <em>literal</em><c>Path</c>. The
+ using the same <em>literal</em><c><anno>Path</anno></c>. The
exception is when <em>reloading</em> is requested, in
- which case the <c>Path</c> may be specified
+ which case the <c><anno>Path</anno></c> may be specified
differently. Note that all <seealso marker="#users">users</seealso> trying to load the
- driver at a later time will need to use the <em>new</em><c>Path</c> if the <c>Path</c> is changed using a
+ driver at a later time will need to use the <em>new</em><c><anno>Path</anno></c> if the <c><anno>Path</anno></c> is changed using a
<c>reload</c> option. This is yet another reason
to have <em>only one loader</em> of a driver one wants to
upgrade in a running system! </p>
</item>
- <tag><em>Name</em></tag>
+ <tag><em><anno>Name</anno></em></tag>
<item>
<p>The name parameter is the name of the driver to be used
in subsequent calls to <seealso marker="erts:erlang#open_port/2">open_port</seealso>. The
- name can be specified either as an <c>io_list()</c> or
+ name can be specified either as an <c>iolist()</c> or
as an <c>atom()</c>. The name given when loading is used
to find the actual object file (with the
- help of the <c>Path</c> and the system implied
+ help of the <c><anno>Path</anno></c> and the system implied
extension suffix, i.e. <c>.so</c>). The name by which
the driver identifies itself must also be consistent
- with this <c>Name</c> parameter, much as a beam-file's
+ with this <c><anno>Name</anno></c> parameter, much as a beam-file's
module name much correspond to its filename.</p>
</item>
- <tag><em>OptionList</em></tag>
+ <tag><em><anno>OptionList</anno></em></tag>
<item>
<p>A number of options can be specified to control the
loading operation. The options are given as a list of
two-tuples, the tuples having the following values and
meanings:</p>
<taglist>
- <tag><em>{driver_options, DriverOptionsList}</em></tag>
+ <tag><em>{driver_options, <anno>DriverOptionList</anno>}</em></tag>
<item>
<p>This option is to provide options that will change
its general behavior and will "stick" to the driver
@@ -729,14 +700,14 @@
when the last <seealso marker="#users">user</seealso> calls <seealso marker="#try_unload/2">try_unload/2</seealso>, or
the last process having loaded the driver exits.</p>
</item>
- <tag><em>{monitor, MonitorOption}</em></tag>
+ <tag><em>{monitor, <anno>MonitorOption</anno>}</em></tag>
<item>
- <p>A <c>MonitorOption</c> tells <c>try_load/3</c> to
+ <p>A <c><anno>MonitorOption</anno></c> tells <c>try_load/3</c> to
trigger a driver monitor under certain
conditions. When the monitor is triggered, the
- function will return a three-tuple <c>{ok, PendingStatus, reference()}</c>, where the <c>reference()</c> is
+ function will return a three-tuple <c>{ok, <anno>PendingStatus</anno>, reference()}</c>, where the <c>reference()</c> is
the monitor ref for the driver monitor.</p>
- <p>Only one <c>MonitorOption</c> can be specified and
+ <p>Only one <c><anno>MonitorOption</anno></c> can be specified and
it is either the atom <c>pending</c>, which means
that a monitor should be created whenever a load
operation is delayed, and the atom
@@ -747,7 +718,7 @@
is present for completeness, it is very well defined
which reload-options might give rise to which
delays. It might, however, be a good idea to use the
- same <c>MonitorOption</c> as the <c>ReloadOption</c>
+ same <c><anno>MonitorOption</anno></c> as the <c><anno>ReloadOption</anno></c>
if present.</p>
<p>If reloading is not requested, it might still be
useful to specify the <c>monitor</c> option, as
@@ -760,12 +731,12 @@
<c>{monitor, pending_driver}</c> in production
code (see the monitor discussion above). </p>
</item>
- <tag><em>{reload,RealoadOption}</em></tag>
+ <tag><em>{reload,<anno>ReloadOption</anno>}</em></tag>
<item>
<p>This option is used when one wants to
<em>reload</em> a driver from disk, most often in a
code upgrade scenario. Having a <c>reload</c> option
- also implies that the <c>Path</c> parameter need
+ also implies that the <c><anno>Path</anno></c> parameter need
<em>not</em> be consistent with earlier loads of
the driver.</p>
<p>To reload a driver, the process needs to have previously
@@ -814,9 +785,9 @@
<tag><em>{error,inconsistent}</em></tag>
<item>
<p>The driver has already been loaded with either other
- <c>DriverOptions</c> or a different <em>literal</em><c>Path</c> argument.</p>
+ <c><anno>DriverOptionList</anno></c> or a different <em>literal</em><c>Path</c> argument.</p>
<p>This can happen even if a <c>reload</c> option is given,
- if the <c>DriverOptions</c> differ from the current.</p>
+ if the <c>DriverOptionList</c> differ from the current.</p>
</item>
<tag><em>{error, permanent}</em></tag>
<item>
@@ -830,19 +801,19 @@
</item>
<tag><em>{error, pending_reload}</em></tag>
<item>
- <p>Driver reload is already requested by another <seealso marker="#users">user</seealso> when the <c>{reload, ReloadOption}</c> option was given.</p>
+ <p>Driver reload is already requested by another <seealso marker="#users">user</seealso> when the <c>{reload, <anno>ReloadOption</anno>}</c> option was given.</p>
</item>
<tag><em>{error, not_loaded_by_this_process}</em></tag>
<item>
<p>Appears when the <c>reload</c> option is given. The
- driver <c>Name</c> is present in the system, but there is no
+ driver <c><anno>Name</anno></c> is present in the system, but there is no
<seealso marker="#users">user</seealso> of it in this
process.</p>
</item>
<tag><em>{error, not_loaded}</em></tag>
<item>
<p>Appears when the <c>reload</c> option is given. The
- driver <c>Name</c> is not in the system. Only drivers
+ driver <c><anno>Name</anno></c> is not in the system. Only drivers
loaded by this process can be reloaded.</p>
</item>
</taglist>
@@ -856,18 +827,8 @@
</desc>
</func>
<func>
- <name>try_unload(Name, OptionList) -> {ok,Status} | {ok, PendingStatus, Ref} | {error, ErrorAtom}</name>
+ <name name="try_unload" arity="2"/>
<fsummary>Unload a driver</fsummary>
- <type>
- <v>Name = string() | atom()</v>
- <v>OptionList = [ Option ]</v>
- <v>Option = {monitor, MonitorOption} | kill_ports</v>
- <v>MonitorOption = pending_driver | pending</v>
- <v>Status = unloaded | PendingStatus </v>
- <v>PendingStatus = pending_driver | pending_process</v>
- <v>Ref = reference()</v>
- <v>ErrorAtom = linked_in_driver | not_loaded | not_loaded_by_this_process | permanent</v>
- </type>
<desc>
<p>This is the low level function to unload (or decrement
reference counts of) a driver. It can be used to force port
@@ -948,15 +909,15 @@
</taglist>
<p>The function accepts the following parameters:</p>
<taglist>
- <tag><em>Name</em></tag>
+ <tag><em><anno>Name</anno></em></tag>
<item>
<p>The name parameter is the name of the driver to be
unloaded. The name can be specified either as an
- <c>io_list()</c> or as an <c>atom()</c>. </p>
+ <c>iolist()</c> or as an <c>atom()</c>. </p>
</item>
- <tag><em>OptionList</em></tag>
+ <tag><em><anno>OptionList</anno></em></tag>
<item>
- <p>The <c>OptionList</c> argument can be used to specify
+ <p>The <c><anno>OptionList</anno></c> argument can be used to specify
certain behavior regarding ports as well as triggering
monitors under certain conditions:</p>
<taglist>
@@ -972,10 +933,10 @@
unloads, one should use the driver option
<c>kill_ports</c> when loading the driver instead.</p>
</item>
- <tag><em>{monitor, MonitorOption}</em></tag>
+ <tag><em>{monitor, <anno>MonitorOption</anno>}</em></tag>
<item>
<p>This option creates a driver monitor if the condition
- given in <c>MonitorOptions</c> is true. The valid
+ given in <c><anno>MonitorOption</anno></c> is true. The valid
options are:</p>
<taglist>
<tag><em>pending_driver</em></tag>
@@ -989,7 +950,7 @@
<c>{ok, pending_driver}</c> or <c>{ok, pending_process}</c>.</p>
</item>
</taglist>
- <p>The <c>pending_driver</c> <c>MonitorOption</c> is by far
+ <p>The <c>pending_driver</c> <c><anno>MonitorOption</anno></c> is by far
the most useful and it has to be used to ensure that the
driver has really been unloaded and the ports closed
whenever the <c>kill_ports</c> option is used or the
@@ -1016,11 +977,11 @@
</item>
<tag><em>{error, not_loaded}</em></tag>
<item>
- <p>The driver <c>Name</c> is not present in the system.</p>
+ <p>The driver <c><anno>Name</anno></c> is not present in the system.</p>
</item>
<tag><em>{error, not_loaded_by_this_process}</em></tag>
<item>
- <p>The driver <c>Name</c> is present in the system, but
+ <p>The driver <c><anno>Name</anno></c> is present in the system, but
there is no <seealso marker="#users">user</seealso> of
it in this process. </p>
<p>As a special case, drivers can be unloaded from
@@ -1088,12 +1049,8 @@
</desc>
</func>
<func>
- <name>loaded_drivers() -> {ok, Drivers}</name>
+ <name name="loaded_drivers" arity="0"/>
<fsummary>List loaded drivers</fsummary>
- <type>
- <v>Drivers = [Driver]</v>
- <v>Driver = string()</v>
- </type>
<desc>
<p>Returns a list of all the available drivers, both
(statically) linked-in and dynamically loaded ones.</p>
diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml
index ec3274965a..cd86b364f6 100644
--- a/lib/kernel/doc/src/error_logger.xml
+++ b/lib/kernel/doc/src/error_logger.xml
@@ -127,11 +127,8 @@ ok</pre>
</desc>
</func>
<func>
- <name>warning_map() -> Tag</name>
+ <name name="warning_map" arity="0"/>
<fsummary>Return the current mapping for warning events</fsummary>
- <type>
- <v>Tag = error | warning | info</v>
- </type>
<desc>
<p>Returns the current mapping for warning events. Events sent
using <c>warning_msg/1,2</c> or <c>warning_report/1,2</c>
diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml
index 772eff13cc..b2a259080d 100644
--- a/lib/kernel/doc/src/file.xml
+++ b/lib/kernel/doc/src/file.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -412,7 +412,7 @@
</desc>
</func>
<func>
- <name>file_info(Filename) -> {ok, FileInfo} | {error, Reason}</name>
+ <name name="file_info" arity="1"/>
<fsummary>Get information about a file (deprecated)</fsummary>
<desc>
<p>This function is obsolete. Use <c>read_file_info/1,2</c>
@@ -598,7 +598,7 @@
</desc>
</func>
<func>
- <name>native_name_encoding() -> latin1 | utf8</name>
+ <name name="native_name_encoding" arity="0"/>
<fsummary>Return the VM's configured filename encoding.</fsummary>
<desc>
<p>This function returns the configured default file name encoding to use for raw file names. Generally an application supplying file names raw (as binaries), should obey the character encoding returned by this function.</p>
diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml
index e327a4f907..7ea58fffff 100644
--- a/lib/kernel/doc/src/gen_sctp.xml
+++ b/lib/kernel/doc/src/gen_sctp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2007</year><year>2011</year>
+ <year>2007</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml
index 726dc30546..4850278a64 100644
--- a/lib/kernel/doc/src/gen_udp.xml
+++ b/lib/kernel/doc/src/gen_udp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2011</year>
+ <year>1997</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/kernel/doc/src/global.xml b/lib/kernel/doc/src/global.xml
index 304a9b1d88..9c50049503 100644
--- a/lib/kernel/doc/src/global.xml
+++ b/lib/kernel/doc/src/global.xml
@@ -163,7 +163,8 @@
<fsummary>Globally register a name for a pid</fsummary>
<type name="method"/>
<type_desc name="method">{<c>Module</c>, <c>Function</c>}
- is also allowed
+ is currently also allowed for backward compatibility, but its use is
+ deprecated
</type_desc>
<desc>
<p>Globally associates the name <c><anno>Name</anno></c> with a pid, that is,
@@ -180,6 +181,15 @@
unregistered. This function is called once for each name
clash.</p>
+ <warning>
+ <p>If you plan to change code without restarting your system,
+ you must use an external fun (<c>fun Module:Function/Arity</c>)
+ as the <c><anno>Resolve</anno></c> function; if you use a
+ local fun you can never replace the code for the module that
+ the fun belongs to.
+ </p>
+ </warning>
+
<p>There are three pre-defined resolve functions:
<c>random_exit_name/3</c>, <c>random_notify_name/3</c>, and
<c>notify_all_name/3</c>. If no <c><anno>Resolve</anno></c> function is
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index 096ddfd847..32b4a429dd 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -446,16 +446,6 @@ fe80::204:acff:fe17:bf38
faster than the receiver can read.</p>
</item>
- <tag><c>{bit8, clear | set | on | off}</c></tag>
- <item>
- <p>
- Scans every byte in received data-packets and checks if the 8 bit
- is set in any of them. Information is retrieved with
- <c>inet:getopts/2</c>.
- <note>Deprecated! Will be removed in Erlang/OTP R16.</note>
- </p>
- </item>
-
<tag><c>{broadcast, Boolean}</c>(UDP sockets)</tag>
<item>
<p>Enable/disable permission to send broadcasts.</p>
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index 94624bf512..8e911a406b 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,97 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 2.15.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed issue where using controlling_process/2 with self()
+ as the second argument caused the port to leak if self()
+ crashes. (Thanks to Ricardo Catalinas Jim�nez)</p>
+ <p>
+ Own Id: OTP-10094</p>
+ </item>
+ <item>
+ <p>
+ When sending large files using the file:sendfile fallback
+ file:sendfile would crash. This is now fixed.</p>
+ <p>
+ Own Id: OTP-10098</p>
+ </item>
+ <item>
+ <p>
+ Fix rpc:call/5 for local calls with a finite Timeout
+ (Thanks to Tomer Chachamu)</p>
+ <p>
+ Own Id: OTP-10149</p>
+ </item>
+ <item>
+ <p>
+ fix escript/primary archive reloading</p>
+ <p>
+ If the mtime of an escript/primary archive file changes
+ after being added to the code path, correctly reload the
+ archive and update the cache. (Thanks to Tuncer Ayaz)</p>
+ <p>
+ Own Id: OTP-10151</p>
+ </item>
+ <item>
+ <p>
+ Support added for home directories named with non-ASCII
+ characters (codepoints above 127) on a system running in
+ Unicode file mode (e.g. on MacOSX or Linux with startup
+ arguments +fnu or +fna with the right LOCALE). Also
+ environment variables with Unicode content are supported
+ in applicable environments.</p>
+ <p>
+ Own Id: OTP-10160</p>
+ </item>
+ <item>
+ <p>
+ Allow mixed IPv4 and IPv6 addresses to sctp_bindx</p>
+ <p>
+ Also allow mixed address families to bind, since the
+ first address on a multihomed sctp socket must be bound
+ with bind, while the rest are to be bound using
+ sctp_bindx. At least Linux supports adding address of
+ mixing families. Make inet_set_faddress function
+ available also when HAVE_SCTP is not defined, since we
+ use it to find an address for bind to be able to mix ipv4
+ and ipv6 addresses. Thanks to Tomas Abrahamsson</p>
+ <p>
+ Own Id: OTP-10217</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Document inet options: high_watermark, priority,
+ linger and a some other options that previously was
+ undocumented.</p>
+ <p>
+ Own Id: OTP-10053</p>
+ </item>
+ <item>
+ <p>Remove bit8 option support from inet</p>
+ <p>
+ Own Id: OTP-10056</p>
+ </item>
+ <item>
+ <p> The type of the disk log header has been corrected.
+ (Thanks to Niclas Eklund.) </p>
+ <p>
+ Own Id: OTP-10131</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 2.15.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml
index e94119845a..5e182de41d 100644
--- a/lib/kernel/doc/src/os.xml
+++ b/lib/kernel/doc/src/os.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2011</year>
+ <year>1997</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -73,65 +73,65 @@ DirOut = os:cmd("dir"), % on Win32 platform</code>
</desc>
</func>
<func>
- <name>getenv() -> [string()]</name>
+ <name name="getenv" arity="0"/>
<fsummary>List all environment variables</fsummary>
<desc>
<p>Returns a list of all environment variables.
Each environment variable is given as a single string on
the format <c>"VarName=Value"</c>, where <c>VarName</c> is
the name of the variable and <c>Value</c> its value.</p>
+ <p>If Unicode file name encoding is in effect (see the <seealso
+ marker="erts:erl#file_name_encoding">erl manual
+ page</seealso>), the strings may contain characters with
+ codepoints > 255.</p>
</desc>
</func>
<func>
- <name>getenv(VarName) -> Value | false</name>
+ <name name="getenv" arity="1"/>
<fsummary>Get the value of an environment variable</fsummary>
- <type>
- <v>VarName = string() </v>
- <v>Value = string()</v>
- </type>
<desc>
- <p>Returns the <c>Value</c> of the environment variable
- <c>VarName</c>, or <c>false</c> if the environment variable
+ <p>Returns the <c><anno>Value</anno></c> of the environment variable
+ <c><anno>VarName</anno></c>, or <c>false</c> if the environment variable
is undefined.</p>
+ <p>If Unicode file name encoding is in effect (see the <seealso
+ marker="erts:erl#file_name_encoding">erl manual
+ page</seealso>), the strings (both <c><anno>VarName</anno></c> and
+ <c><anno>Value</anno></c>) may contain characters with codepoints > 255.</p>
</desc>
</func>
<func>
- <name>getpid() -> Value </name>
+ <name name="getpid" arity="0"/>
<fsummary>Return the process identifier of the emulator process</fsummary>
- <type>
- <v>Value = string()</v>
- </type>
<desc>
<p>Returns the process identifier of the current Erlang emulator
in the format most commonly used by the operating system
- environment. <c>Value</c> is returned as a string containing
+ environment. <c><anno>Value</anno></c> is returned as a string containing
the (usually) numerical identifier for a process. On Unix,
this is typically the return value of the <c>getpid()</c>
- system call. On VxWorks, <c>Value</c> contains the task id
- (decimal notation) of the Erlang task. On Windows,
+ system call. On Windows,
the process id as returned by the <c>GetCurrentProcessId()</c>
system call is used.</p>
</desc>
</func>
<func>
- <name>putenv(VarName, Value) -> true</name>
+ <name name="putenv" arity="2"/>
<fsummary>Set a new value for an environment variable</fsummary>
- <type>
- <v>VarName = string() </v>
- <v>Value = string()</v>
- </type>
<desc>
- <p>Sets a new <c>Value</c> for the environment variable
- <c>VarName</c>.</p>
+ <p>Sets a new <c><anno>Value</anno></c> for the environment variable
+ <c><anno>VarName</anno></c>.</p>
+ <p>If Unicode filename encoding is in effect (see the <seealso
+ marker="erts:erl#file_name_encoding">erl manual
+ page</seealso>), the strings (both <c><anno>VarName</anno></c> and
+ <c><anno>Value</anno></c>) may contain characters with codepoints > 255.</p>
+ <p>On Unix platforms, the environment will be set using UTF-8 encoding
+ if Unicode file name translation is in effect. On Windows the
+ environment is set using wide character interfaces.</p>
</desc>
</func>
<func>
- <name>timestamp() -> Timestamp</name>
+ <name name="timestamp" arity="0"/>
+ <type_desc variable="Timestamp">Timestamp = {MegaSecs, Secs, MicroSecs}</type_desc>
<fsummary>Returna a timestamp from the OS in the erlang:now/0 format</fsummary>
- <type>
- <v>Timestamp = {MegaSecs, Secs, MicroSecs} = <seealso marker="erts:erlang#type-timestamp">erlang:timestamp()</seealso></v>
- <v>MegaSecs = Secs = MicroSecs = integer() >= 0</v>
- </type>
<desc>
<p>Returns a tuple in the same format as <seealso marker="erts:erlang#now/0">erlang:now/0</seealso>. The difference is that this function returns what the operating system thinks (a.k.a. the wall clock time) without any attempts at time correction. The result of two different calls to this function is <em>not</em> guaranteed to be different.</p>
<p>The most obvious use for this function is logging. The tuple can be used together with the function <seealso marker="stdlib:calendar#now_to_universal_time/1">calendar:now_to_universal_time/1</seealso>
@@ -168,8 +168,6 @@ format_utc_timestamp() ->
Solaris 1 and 2, it will be <c>sunos</c>.</p>
<p>In Windows, <c><anno>Osname</anno></c> will be either <c>nt</c> (on
Windows NT), or <c>windows</c> (on Windows 95).</p>
- <p>On VxWorks the OS family alone is returned, that is
- <c>vxworks</c>.</p>
<note>
<p>Think twice before using this function. Use the
<c>filename</c> module if you want to inspect or build
diff --git a/lib/kernel/doc/src/packages.xml b/lib/kernel/doc/src/packages.xml
index 80de2e05fc..8a82b91a90 100644
--- a/lib/kernel/doc/src/packages.xml
+++ b/lib/kernel/doc/src/packages.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2004</year><year>2009</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -204,11 +204,5 @@ ok
Explicitly
declaring each use of a module makes for safe code.</p>
</description>
- <funcs>
- <func>
- <name>no functions exported</name>
- <fsummary>x</fsummary>
- </func>
- </funcs>
</erlref>
diff --git a/lib/kernel/examples/uds_dist/c_src/uds_drv.c b/lib/kernel/examples/uds_dist/c_src/uds_drv.c
index 9327ab19dc..9ad6b85a0f 100644
--- a/lib/kernel/examples/uds_dist/c_src/uds_drv.c
+++ b/lib/kernel/examples/uds_dist/c_src/uds_drv.c
@@ -967,7 +967,7 @@ static void *my_malloc(size_t size)
void *ptr;
if ((ptr = driver_alloc(size)) == NULL) {
- erl_exit(1,"Could not allocate %d bytes of memory",(int) size);
+ erl_exit(1,"Could not allocate %lu bytes of memory",(unsigned long) size);
}
return ptr;
}
@@ -977,7 +977,7 @@ static void *my_realloc(void *ptr, size_t size)
void erl_exit(int, char *, ...);
void *nptr;
if ((nptr = driver_realloc(ptr, size)) == NULL) {
- erl_exit(1,"Could not reallocate %d bytes of memory",(int) size);
+ erl_exit(1,"Could not reallocate %lu bytes of memory",(unsigned long) size);
}
return nptr;
}
diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile
index a39864d6b7..c76ff9e2f0 100644
--- a/lib/kernel/src/Makefile
+++ b/lib/kernel/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/kernel/src/application.erl b/lib/kernel/src/application.erl
index c299fb085c..9b7c4aa7b8 100644
--- a/lib/kernel/src/application.erl
+++ b/lib/kernel/src/application.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -37,8 +37,7 @@
-type application_opt() :: {'description', Description :: string()}
| {'vsn', Vsn :: string()}
| {'id', Id :: string()}
- | {'modules', [(Module :: module()) |
- {Module :: module(), Version :: term()}]}
+ | {'modules', [Module :: module()]}
| {'registered', Names :: [Name :: atom()]}
| {'applications', [Application :: atom()]}
| {'included_applications', [Application :: atom()]}
diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl
index ebfe84463a..68cd26ec10 100644
--- a/lib/kernel/src/application_controller.erl
+++ b/lib/kernel/src/application_controller.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -162,7 +162,7 @@
%% appl_opt() = {description, string()} |
%% {vsn, string()} |
%% {id, string()}, |
-%% {modules, [Module|{Module,Vsn}]} |
+%% {modules, [Module]} |
%% {registered, [atom()]} |
%% {applications, [atom()]} |
%% {included_applications, [atom()]} |
@@ -172,7 +172,6 @@
%% {maxP, integer()|infinity} |
%% {mod, {Module, term()}}
%% Module = atom()
-%% Vsn = term()
%% Purpose: Starts the application_controller. This process starts all
%% application masters for the applications.
%% The kernel application is the only application that is
@@ -446,7 +445,7 @@ get_application_module(Module) ->
get_application_module(Module, AppModules).
get_application_module(Module, [[AppName, Modules]|AppModules]) ->
- case in_modules(Module, Modules) of
+ case lists:member(Module, Modules) of
true ->
{ok, AppName};
false ->
@@ -455,16 +454,6 @@ get_application_module(Module, [[AppName, Modules]|AppModules]) ->
get_application_module(_Module, []) ->
undefined.
-%% 'modules' key in .app is a list of Module or {Module,Vsn}
-in_modules(Module, [Module|_Modules]) ->
- true;
-in_modules(Module, [{Module, _Vsn}|_Modules]) ->
- true;
-in_modules(Module, [_Module|Modules]) ->
- in_modules(Module, Modules);
-in_modules(_Module, []) ->
- false.
-
permit_application(ApplName, Flag) ->
gen_server:call(?AC,
{permit_application, ApplName, Flag},
diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl
index b7fda69ce0..c808ac7cb7 100644
--- a/lib/kernel/src/code.erl
+++ b/lib/kernel/src/code.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -63,53 +63,13 @@
which/1,
where_is_file/1,
where_is_file/2,
- set_primary_archive/3,
+ set_primary_archive/4,
clash/0]).
-export_type([load_error_rsn/0, load_ret/0]).
-include_lib("kernel/include/file.hrl").
-%% User interface.
-%%
-%% objfile_extension() -> ".beam"
-%% get_path() -> [Dir]
-%% set_path([Dir]) -> true | {error, bad_directory | bad_path}
-%% add_path(Dir) -> true | {error, bad_directory}
-%% add_patha(Dir) -> true | {error, bad_directory}
-%% add_pathz(Dir) -> true | {error, bad_directory}
-%% add_paths([Dir]) -> ok
-%% add_pathsa([Dir]) -> ok
-%% add_pathsz([Dir]) -> ok
-%% del_path(Dir) -> boolean() | {error, bad_name}
-%% replace_path(Name, Dir) -> true | {error, bad_directory | bad_name
-%% | {badarg,_}}
-%% load_file(Module) -> {module, Module} | {error, What :: atom()}
-%% load_abs(File) -> {module, Module} | {error, What :: atom()}
-%% load_abs(File, Module) -> {module, Module} | {error, What :: atom()}
-%% load_binary(Module, File, Bin)-> {module, Module} | {error, What :: atom()}
-%% ensure_loaded(Module) -> {module, Module} | {error, What :: atom()}
-%% delete(Module) -> boolean()
-%% purge(Module) -> boolean() kills all procs running old code
-%% soft_purge(Module) -> boolean()
-%% is_loaded(Module) -> {file, loaded_filename()} | false
-%% all_loaded() -> [{Module, loaded_filename()}]
-%% get_object_code(Module) -> {Module, Bin, Filename} | error
-%% stop() -> no_return()
-%% root_dir() -> Dir
-%% compiler_dir() -> Dir
-%% lib_dir() -> Dir
-%% lib_dir(Application) -> Dir | {error, bad_name}
-%% priv_dir(Application) -> Dir | {error, bad_name}
-%% stick_dir(Dir) -> ok | error
-%% unstick_dir(Dir) -> ok | error
-%% stick_mod(Module) -> true
-%% unstick_mod(Module) -> true
-%% is_sticky(Module) -> boolean()
-%% which(Module) -> Filename | loaded_ret_atoms() | non_existing
-%% set_primary_archive((FileName, Bin, FileInfo) -> ok | {error, Reason}
-%% clash() -> ok prints out number of clashes
-
%%----------------------------------------------------------------------------
%% Some types for basic exported functions of this module
%%----------------------------------------------------------------------------
@@ -125,6 +85,39 @@
-type loaded_ret_atoms() :: 'cover_compiled' | 'preloaded'.
-type loaded_filename() :: (Filename :: file:filename()) | loaded_ret_atoms().
+%%% BIFs
+
+-export([get_chunk/2, is_module_native/1, make_stub_module/3, module_md5/1]).
+
+-spec get_chunk(Bin, Chunk) ->
+ binary() | undefined when
+ Bin :: binary(),
+ Chunk :: string().
+
+get_chunk(_, _) ->
+ erlang:nif_error(undef).
+
+-spec is_module_native(Module) -> true | false | undefined when
+ Module :: module().
+
+is_module_native(_) ->
+ erlang:nif_error(undef).
+
+-spec make_stub_module(Module, Beam, Info) -> Module when
+ Module :: module(),
+ Beam :: binary(),
+ Info :: {list(), list()}.
+
+make_stub_module(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec module_md5(binary()) -> binary() | undefined.
+
+module_md5(_) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
+
%%----------------------------------------------------------------------------
%% User interface
%%----------------------------------------------------------------------------
@@ -481,13 +474,16 @@ where_is_file(Path, File) when is_list(Path), is_list(File) ->
-spec set_primary_archive(ArchiveFile :: file:filename(),
ArchiveBin :: binary(),
- FileInfo :: file:file_info())
+ FileInfo :: file:file_info(),
+ ParserFun :: fun())
-> 'ok' | {'error', atom()}.
-set_primary_archive(ArchiveFile0, ArchiveBin, #file_info{} = FileInfo)
+set_primary_archive(ArchiveFile0, ArchiveBin, #file_info{} = FileInfo,
+ ParserFun)
when is_list(ArchiveFile0), is_binary(ArchiveBin) ->
ArchiveFile = filename:absname(ArchiveFile0),
- case call({set_primary_archive, ArchiveFile, ArchiveBin, FileInfo}) of
+ case call({set_primary_archive, ArchiveFile, ArchiveBin, FileInfo,
+ ParserFun}) of
{ok, []} ->
ok;
{ok, _Mode, Ebins} ->
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index a2db7c9790..00ad923466 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -394,8 +394,8 @@ handle_call(stop,{_From,_Tag}, S) ->
handle_call({is_cached,_File}, {_From,_Tag}, S=#state{cache=no_cache}) ->
{reply, no, S};
-handle_call({set_primary_archive, File, ArchiveBin, FileInfo}, {_From,_Tag}, S=#state{mode=Mode}) ->
- case erl_prim_loader:set_primary_archive(File, ArchiveBin, FileInfo) of
+handle_call({set_primary_archive, File, ArchiveBin, FileInfo, ParserFun}, {_From,_Tag}, S=#state{mode=Mode}) ->
+ case erl_prim_loader:set_primary_archive(File, ArchiveBin, FileInfo, ParserFun) of
{ok, Files} ->
{reply, {ok, Mode, Files}, S};
{error, _Reason} = Error ->
diff --git a/lib/kernel/src/disk_log_1.erl b/lib/kernel/src/disk_log_1.erl
index 266df84a03..0cb1ed579a 100644
--- a/lib/kernel/src/disk_log_1.erl
+++ b/lib/kernel/src/disk_log_1.erl
@@ -1495,7 +1495,7 @@ fwrite_close2(Fd, FileName, B) ->
pwrite_close2(Fd, FileName, Position, B) ->
case file:pwrite(Fd, Position, B) of
ok -> ok;
- Error -> file_error(FileName, {error, Error})
+ {error,Error} -> file_error(FileName, {error, Error})
end.
position2(Fd, FileName, Pos) ->
diff --git a/lib/kernel/src/erl_ddll.erl b/lib/kernel/src/erl_ddll.erl
index 646cac99c5..f967fcc2ef 100644
--- a/lib/kernel/src/erl_ddll.erl
+++ b/lib/kernel/src/erl_ddll.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -30,9 +30,99 @@
%%----------------------------------------------------------------------------
-type path() :: string() | atom().
--type driver() :: string() | atom().
+-type driver() :: iolist() | atom().
%%----------------------------------------------------------------------------
+%%% BIFs
+
+-export([demonitor/1, info/2, format_error_int/1, monitor/2,
+ try_load/3, try_unload/2, loaded_drivers/0]).
+
+-spec demonitor(MonitorRef) -> ok when
+ MonitorRef :: reference().
+
+demonitor(_) ->
+ erlang:nif_error(undef).
+
+-spec info(Name, Tag) -> Value when
+ Name :: driver(),
+ Tag :: processes | driver_options | port_count | linked_in_driver
+ | permanent | awaiting_load | awaiting_unload,
+ Value :: term().
+
+info(_, _) ->
+ erlang:nif_error(undef).
+
+-spec format_error_int(ErrSpec) -> string() when
+ ErrSpec :: inconsisten | linked_in_driver | permanent
+ | not_loaded | not_loaded_by_this_process | not_pending
+ | already_loaded | unloading.
+
+format_error_int(_) ->
+ erlang:nif_error(undef).
+
+-spec monitor(Tag, Item) -> MonitorRef when
+ Tag :: driver,
+ Item :: {Name, When},
+ Name :: driver(),
+ When :: loaded | unloaded | unloaded_only,
+ MonitorRef :: reference().
+
+monitor(_, _) ->
+ erlang:nif_error(undef).
+
+-spec try_load(Path, Name, OptionList) ->
+ {ok,Status} |
+ {ok, PendingStatus, Ref} |
+ {error, ErrorDesc} when
+ Path :: path(),
+ Name :: driver(),
+ OptionList :: [Option],
+ Option :: {driver_options, DriverOptionList}
+ | {monitor, MonitorOption}
+ | {reload, ReloadOption},
+ DriverOptionList :: [DriverOption],
+ DriverOption :: kill_ports,
+ MonitorOption :: pending_driver | pending,
+ ReloadOption :: pending_driver | pending,
+ Status :: loaded | already_loaded | PendingStatus,
+ PendingStatus :: pending_driver | pending_process,
+ Ref :: reference(),
+ ErrorDesc :: ErrorAtom | OpaqueError,
+ ErrorAtom :: linked_in_driver | inconsistent | permanent
+ | not_loaded_by_this_process | not_loaded
+ | pending_reload | pending_process,
+ OpaqueError :: term().
+
+try_load(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec try_unload(Name, OptionList) ->
+ {ok, Status} |
+ {ok, PendingStatus, Ref} |
+ {error, ErrorAtom} when
+ Name :: driver(),
+ OptionList :: [Option],
+ Option :: {monitor, MonitorOption} | kill_ports,
+ MonitorOption :: pending_driver | pending,
+ Status :: unloaded | PendingStatus,
+ PendingStatus :: pending_driver | pending_process,
+ Ref :: reference(),
+ ErrorAtom :: linked_in_driver | not_loaded |
+ not_loaded_by_this_process | permanent.
+
+try_unload(_, _) ->
+ erlang:nif_error(undef).
+
+-spec loaded_drivers() -> {ok, Drivers} when
+ Drivers :: [Driver],
+ Driver :: string().
+
+loaded_drivers() ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
+
-spec start() -> {'error', {'already_started', 'undefined'}}.
diff --git a/lib/kernel/src/error_handler.erl b/lib/kernel/src/error_handler.erl
index a67b11a888..f8bc5f499c 100644
--- a/lib/kernel/src/error_handler.erl
+++ b/lib/kernel/src/error_handler.erl
@@ -90,7 +90,7 @@ int() -> int.
crash(Fun, Args) ->
crash({Fun,Args,[]}).
--spec crash(atom(), atom(), arity()) -> no_return().
+-spec crash(atom(), atom(), arity() | [term()]) -> no_return().
crash(M, F, A) ->
crash({M,F,A,[]}).
diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl
index f94cca000f..92c1eb80dc 100644
--- a/lib/kernel/src/error_logger.erl
+++ b/lib/kernel/src/error_logger.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -42,6 +42,18 @@
-type state() :: {non_neg_integer(), non_neg_integer(), [term()]}.
+%%% BIF
+
+-export([warning_map/0]).
+
+-spec warning_map() -> Tag when
+ Tag :: error | warning | info.
+
+warning_map() ->
+ erlang:nif_error(undef).
+
+%%% End of BIF
+
%%-----------------------------------------------------------------
-spec start() -> {'ok', pid()} | {'error', any()}.
diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl
index b8871e0d45..6654cd9ee7 100644
--- a/lib/kernel/src/erts_debug.erl
+++ b/lib/kernel/src/erts_debug.erl
@@ -28,6 +28,135 @@
%% same/2
%% flat_size/1
+%%% BIFs
+
+-export([breakpoint/2, disassemble/1, display/1, dist_ext_to_term/2,
+ dump_monitors/1, dump_links/1, flat_size/1,
+ get_internal_state/1, instructions/0, lock_counters/1,
+ same/2, set_internal_state/2]).
+
+-spec breakpoint(MFA, Flag) -> non_neg_integer() when
+ MFA :: {Module :: module(),
+ Function :: atom(),
+ Arity :: arity() | '_'},
+ Flag :: boolean().
+
+breakpoint(_, _) ->
+ erlang:nif_error(undef).
+
+-spec disassemble(What) -> false | undef | Result when
+ What :: MFA | Address,
+ Result :: {Address, Code, MFA},
+ MFA :: mfa(),
+ Address :: non_neg_integer(),
+ Code :: binary().
+
+disassemble(_) ->
+ erlang:nif_error(undef).
+
+-spec display(Term) -> string() when
+ Term :: term().
+
+display(_) ->
+ erlang:nif_error(undef).
+
+-spec dist_ext_to_term(Tuple, Binary) -> term() when
+ Tuple :: tuple(),
+ Binary :: binary().
+
+dist_ext_to_term(_, _) ->
+ erlang:nif_error(undef).
+
+-spec dump_monitors(Id) -> true when
+ Id :: pid() | atom().
+
+dump_monitors(_) ->
+ erlang:nif_error(undef).
+
+-spec dump_links(Id) -> true when
+ Id :: pid() | port() | atom().
+
+dump_links(_) ->
+ erlang:nif_error(undef).
+
+-spec flat_size(Term) -> non_neg_integer() when
+ Term :: term().
+
+flat_size(_) ->
+ erlang:nif_error(undef).
+
+-spec get_internal_state(W) -> term() when
+ W :: reds_left | node_and_dist_references | monitoring_nodes
+ | next_pid | 'DbTable_words' | check_io_debug
+ | process_info_args | processes | processes_bif_info
+ | max_atom_out_cache_index | nbalance | available_internal_state
+ | force_heap_frags | memory
+ | {process_status, pid()}
+ | {link_list, pid() | port() | node()}
+ | {monitor_list, pid() | node()}
+ | {channel_number, non_neg_integer()}
+ | {have_pending_exit, pid() | port() | atom()}
+ | {binary_info, binary()}
+ | {term_to_binary_no_funs, term()}
+ | {dist_port, port()}
+ | {atom_out_cache_index, atom()}
+ | {fake_scheduler_bindings,
+ default_bind | spread | processor_spread | thread_spread
+ | thread_no_node_processor_spread | no_node_processor_spread
+ | no_node_thread_spread | no_spread | unbound}
+ | {reader_groups_map, non_neg_integer()}.
+
+get_internal_state(_) ->
+ erlang:nif_error(undef).
+
+-spec instructions() -> [string()].
+
+instructions() ->
+ erlang:nif_error(undef).
+
+-spec lock_counters(info) -> term();
+ (clear) -> ok;
+ ({copy_save, boolean()}) -> boolean();
+ ({process_locks, boolean()}) -> boolean().
+
+lock_counters(_) ->
+ erlang:nif_error(undef).
+
+-spec same(Term1, Term2) -> boolean() when
+ Term1 :: term(),
+ Term2 :: term().
+
+same(_, _) ->
+ erlang:nif_error(undef).
+
+-spec set_internal_state(available_internal_state, boolean()) -> boolean();
+ (reds_left, non_neg_integer()) -> true;
+ (block, non_neg_integer()) -> true;
+ (sleep, non_neg_integer()) -> true;
+ (block_scheduler, non_neg_integer()) -> true;
+ (next_pid, non_neg_integer()) -> false | integer();
+ (force_gc, pid() | atom()) -> boolean();
+ (send_fake_exit_signal, {pid() | port(), pid(), term()}) -> dead | message | unaffected | exit;
+ (colliding_names, {atom(), non_neg_integer()}) ->
+ [atom()];
+ (binary_loop_limit, default) -> -1;
+ (binary_loop_limit, non_neg_integer()) -> non_neg_integer();
+ (re_loop_limit, default) -> -1;
+ (re_loop_limit, non_neg_integer()) -> non_neg_integer();
+ (unicode_loop_limit, default) -> -1;
+ (unicode_loop_limit, non_neg_integer()) -> non_neg_integer();
+ (hipe_test_reschedule_suspend, term()) -> nil();
+ (hipe_test_reschedule_resume, pid() | port()) -> boolean();
+ (test_long_gc_sleep, non_neg_integer()) -> true;
+ (kill_dist_connection, port()) -> boolean();
+ (not_running_optimization, boolean()) -> boolean();
+ (wait, deallocations) -> ok.
+
+set_internal_state(_, _) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
+
%% size(Term)
%% Returns the size of Term in actual heap words. Shared subterms are
%% counted once. Example: If A = [a,b], B =[A,A] then size(B) returns 8,
diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl
index cdb984c333..22af38c598 100644
--- a/lib/kernel/src/file.erl
+++ b/lib/kernel/src/file.erl
@@ -111,6 +111,24 @@
-type sendfile_option() :: {chunk_size, non_neg_integer()}.
-type file_info_option() :: {'time', 'local'} | {'time', 'universal'}
| {'time', 'posix'}.
+%%% BIFs
+
+-export([file_info/1, native_name_encoding/0]).
+
+-spec file_info(Filename) -> {ok, FileInfo} | {error, Reason} when
+ Filename :: name(),
+ FileInfo :: file_info(),
+ Reason :: posix() | badarg.
+
+file_info(_) ->
+ erlang:nif_error(undef).
+
+-spec native_name_encoding() -> latin1 | utf8.
+
+native_name_encoding() ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
%%%-----------------------------------------------------------------
diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl
index 914854c65c..830ca61b3c 100644
--- a/lib/kernel/src/gen_udp.erl
+++ b/lib/kernel/src/gen_udp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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/lib/kernel/src/global.erl b/lib/kernel/src/global.erl
index 36cb713ee1..b24a9d5eac 100644
--- a/lib/kernel/src/global.erl
+++ b/lib/kernel/src/global.erl
@@ -232,7 +232,8 @@ register_name(Name, Pid) when is_pid(Pid) ->
Name :: term(),
Pid :: pid(),
Resolve :: method().
-register_name(Name, Pid, Method) when is_pid(Pid) ->
+register_name(Name, Pid, Method0) when is_pid(Pid) ->
+ Method = allow_tuple_fun(Method0),
Fun = fun(Nodes) ->
case (where(Name) =:= undefined) andalso check_dupname(Name, Pid) of
true ->
@@ -290,7 +291,8 @@ re_register_name(Name, Pid) when is_pid(Pid) ->
Name :: term(),
Pid :: pid(),
Resolve :: method().
-re_register_name(Name, Pid, Method) when is_pid(Pid) ->
+re_register_name(Name, Pid, Method0) when is_pid(Pid) ->
+ Method = allow_tuple_fun(Method0),
Fun = fun(Nodes) ->
gen_server:multi_call(Nodes,
global_name_server,
@@ -2218,3 +2220,9 @@ intersection(_, []) ->
[];
intersection(L1, L2) ->
L1 -- (L1 -- L2).
+
+%% Support legacy tuple funs as resolve functions.
+allow_tuple_fun({M, F}) when is_atom(M), is_atom(F) ->
+ fun M:F/3;
+allow_tuple_fun(Fun) when is_function(Fun, 3) ->
+ Fun.
diff --git a/lib/kernel/src/heart.erl b/lib/kernel/src/heart.erl
index 255ae4e51b..28452a377e 100644
--- a/lib/kernel/src/heart.erl
+++ b/lib/kernel/src/heart.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -18,6 +18,10 @@
%%
-module(heart).
+-compile(no_native).
+% 'no_native' as part of a crude fix to make init:restart/0 work by clearing
+% all hipe inter-module information (hipe_mfa_info's in hipe_bif0.c).
+
%%%--------------------------------------------------------------------
%%% This is a rewrite of pre_heart from BS.3.
%%%
diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl
index 8b3aa0286d..06d404905d 100644
--- a/lib/kernel/src/hipe_unified_loader.erl
+++ b/lib/kernel/src/hipe_unified_loader.erl
@@ -34,6 +34,13 @@
-module(hipe_unified_loader).
+-compile(no_native).
+% 'no_native' is a workaround to avoid "The code server called unloaded module"
+% caused by Mod:module_info(exports) in patch_to_emu_step1() called by post_beam_load.
+% Reproducable with hipelibs and asn1_SUITE.
+% I think the real solution would be to let BIF erlang:load_module/2 redirect all
+% hipe calls to the module and thereby remove post_beam_load.
+
-export([chunk_name/1,
%% Only the code and code_server modules may call the entries below!
load_native_code/2,
@@ -330,11 +337,16 @@ exports(ExportMap, BaseAddress) ->
exports(ExportMap, BaseAddress, [], []).
exports([Offset,M,F,A,IsClosure,IsExported|Rest], BaseAddress, MFAs, Addresses) ->
- MFA = {M,F,A},
- Address = BaseAddress + Offset,
- FunDef = #fundef{address=Address, mfa=MFA, is_closure=IsClosure,
- is_exported=IsExported},
- exports(Rest, BaseAddress, [MFA|MFAs], [FunDef|Addresses]);
+ case IsExported andalso erlang:is_builtin(M, F, A) of
+ true ->
+ exports(Rest, BaseAddress, MFAs, Addresses);
+ _false ->
+ MFA = {M,F,A},
+ Address = BaseAddress + Offset,
+ FunDef = #fundef{address=Address, mfa=MFA, is_closure=IsClosure,
+ is_exported=IsExported},
+ exports(Rest, BaseAddress, [MFA|MFAs], [FunDef|Addresses])
+ end;
exports([], _, MFAs, Addresses) ->
{MFAs, Addresses}.
@@ -498,7 +510,7 @@ patch_offset(Type, Data, Address, ConstAndZone, Addresses) ->
Atom = Data,
patch_atom(Address, Atom);
sdesc ->
- patch_sdesc(Data, Address, ConstAndZone);
+ patch_sdesc(Data, Address, ConstAndZone, Addresses);
x86_abs_pcrel ->
patch_instr(Address, Data, x86_abs_pcrel)
%% _ ->
@@ -511,14 +523,16 @@ patch_atom(Address, Atom) ->
patch_instr(Address, hipe_bifs:atom_to_word(Atom), atom).
patch_sdesc(?STACK_DESC(SymExnRA, FSize, Arity, Live),
- Address, {_ConstMap2,CodeAddress}) ->
+ Address, {_ConstMap2,CodeAddress}, _Addresses) ->
ExnRA =
case SymExnRA of
[] -> 0; % No catch
LabelOffset -> CodeAddress + LabelOffset
end,
?ASSERT(assert_local_patch(Address)),
- hipe_bifs:enter_sdesc({Address, ExnRA, FSize, Arity, Live}).
+ DBG_MFA = ?IF_DEBUG(address_to_mfa_lth(Address, _Addresses), {undefined,undefined,0}),
+ hipe_bifs:enter_sdesc({Address, ExnRA, FSize, Arity, Live, DBG_MFA}).
+
%%----------------------------------------------------------------
%% Handle a 'load_address'-type patch.
@@ -725,7 +739,7 @@ find_const(ConstNo, []) ->
%%
add_ref(CalleeMFA, Address, Addresses, RefType, Trampoline, RemoteOrLocal) ->
- CallerMFA = address_to_mfa(Address, Addresses),
+ CallerMFA = address_to_mfa_lth(Address, Addresses),
%% just a sanity assertion below
true = case RemoteOrLocal of
local ->
@@ -738,11 +752,31 @@ add_ref(CalleeMFA, Address, Addresses, RefType, Trampoline, RemoteOrLocal) ->
%% io:format("Adding ref ~w\n",[{CallerMFA, CalleeMFA, Address, RefType}]),
hipe_bifs:add_ref(CalleeMFA, {CallerMFA,Address,RefType,Trampoline,RemoteOrLocal}).
-address_to_mfa(Address, [#fundef{address=Adr, mfa=MFA}|_Rest]) when Address >= Adr -> MFA;
-address_to_mfa(Address, [_ | Rest]) -> address_to_mfa(Address, Rest);
-address_to_mfa(Address, []) ->
- ?error_msg("Local adddress not found ~w\n",[Address]),
- exit({?MODULE, local_address_not_found}).
+% For FunDefs sorted from low to high addresses
+address_to_mfa_lth(Address, FunDefs) ->
+ case address_to_mfa_lth(Address, FunDefs, false) of
+ false ->
+ ?error_msg("Local adddress not found ~w\n",[Address]),
+ exit({?MODULE, local_address_not_found});
+ MFA ->
+ MFA
+ end.
+
+address_to_mfa_lth(Address, [#fundef{address=Adr, mfa=MFA}|Rest], Prev) ->
+ if Address < Adr ->
+ Prev;
+ true ->
+ address_to_mfa_lth(Address, Rest, MFA)
+ end;
+address_to_mfa_lth(_Address, [], Prev) ->
+ Prev.
+
+% For FunDefs sorted from high to low addresses
+%% address_to_mfa_htl(Address, [#fundef{address=Adr, mfa=MFA}|_Rest]) when Address >= Adr -> MFA;
+%% address_to_mfa_htl(Address, [_ | Rest]) -> address_to_mfa_htl(Address, Rest);
+%% address_to_mfa_htl(Address, []) ->
+%% ?error_msg("Local adddress not found ~w\n",[Address]),
+%% exit({?MODULE, local_address_not_found}).
%%----------------------------------------------------------------
%% Change callers of the given module to instead trap to BEAM.
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index 0bb5444dbb..b4ebb1500c 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -535,7 +535,7 @@ options() ->
buffer, header, active, packet, deliver, mode,
multicast_if, multicast_ttl, multicast_loop,
exit_on_close, high_watermark, low_watermark,
- bit8, send_timeout, send_timeout_close
+ send_timeout, send_timeout_close
].
%% Return a list of statistics options
@@ -552,7 +552,7 @@ stats() ->
connect_options() ->
[tos, priority, reuseaddr, keepalive, linger, sndbuf, recbuf, nodelay,
header, active, packet, packet_size, buffer, mode, deliver,
- exit_on_close, high_watermark, low_watermark, bit8, send_timeout,
+ exit_on_close, high_watermark, low_watermark, send_timeout,
send_timeout_close, delay_send,raw].
connect_options(Opts, Family) ->
@@ -608,7 +608,7 @@ con_add(Name, Val, R, Opts, AllOpts) ->
listen_options() ->
[tos, priority, reuseaddr, keepalive, linger, sndbuf, recbuf, nodelay,
header, active, packet, buffer, mode, deliver, backlog,
- exit_on_close, high_watermark, low_watermark, bit8, send_timeout,
+ exit_on_close, high_watermark, low_watermark, send_timeout,
send_timeout_close, delay_send, packet_size,raw].
listen_options(Opts, Family) ->
@@ -763,8 +763,12 @@ sctp_opt([Opt|Opts], Mod, R, As) ->
{Name,Val} -> sctp_opt (Opts, Mod, R, As, Name, Val);
_ -> {error,badarg}
end;
-sctp_opt([], _Mod, R, _SockOpts) ->
- {ok, R}.
+sctp_opt([], _Mod, #sctp_opts{ifaddr=IfAddr}=R, _SockOpts) ->
+ if is_list(IfAddr) ->
+ {ok, R#sctp_opts{ifaddr=lists:reverse(IfAddr)}};
+ true ->
+ {ok, R}
+ end.
sctp_opt(Opts, Mod, R, As, Name, Val) ->
case add_opt(Name, Val, R#sctp_opts.opts, As) of
@@ -1015,11 +1019,7 @@ open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) when Fd < 0 ->
case prim_inet:setopts(S, Opts) of
ok ->
case if is_list(Addr) ->
- prim_inet:bind(S, add,
- [case A of
- {_,_} -> A;
- _ -> {A,Port}
- end || A <- Addr]);
+ bindx(S, Addr, Port);
true ->
prim_inet:bind(S, Addr, Port)
end of
@@ -1040,6 +1040,34 @@ open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) when Fd < 0 ->
open(Fd, _Addr, _Port, Opts, Protocol, Family, Type, Module) ->
fdopen(Fd, Opts, Protocol, Family, Type, Module).
+bindx(S, [Addr], Port0) ->
+ {IP, Port} = set_bindx_port(Addr, Port0),
+ prim_inet:bind(S, IP, Port);
+bindx(S, Addrs, Port0) ->
+ [{IP, Port} | Rest] = [set_bindx_port(Addr, Port0) || Addr <- Addrs],
+ case prim_inet:bind(S, IP, Port) of
+ {ok, AssignedPort} when Port =:= 0 ->
+ %% On newer Linux kernels, Solaris and FreeBSD, calling
+ %% bindx with port 0 is ok, but on SuSE 10, it results in einval
+ Rest2 = [change_bindx_0_port(Addr, AssignedPort) || Addr <- Rest],
+ prim_inet:bind(S, add, Rest2);
+ {ok, _} ->
+ prim_inet:bind(S, add, Rest);
+ Error ->
+ Error
+ end.
+
+set_bindx_port({_IP, _Port}=Addr, _OtherPort) ->
+ Addr;
+set_bindx_port(IP, Port) ->
+ {IP, Port}.
+
+change_bindx_0_port({IP, 0}, AssignedPort) ->
+ {IP, AssignedPort};
+change_bindx_0_port({_IP, _Port}=Addr, _AssignedPort) ->
+ Addr.
+
+
-spec fdopen(Fd :: non_neg_integer(),
Opts :: [socket_setopt()],
Protocol :: socket_protocol(),
@@ -1255,7 +1283,10 @@ tcp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) ->
_ ->
case prim_inet:getopt(S, active) of
{ok, A0} ->
- prim_inet:setopt(S, active, false),
+ case A0 of
+ false -> ok;
+ _ -> prim_inet:setopt(S, active, false)
+ end,
case tcp_sync_input(S, NewOwner, false) of
true -> %% socket already closed,
ok;
@@ -1263,7 +1294,10 @@ tcp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) ->
try erlang:port_connect(S, NewOwner) of
true ->
unlink(S), %% unlink from port
- prim_inet:setopt(S, active, A0),
+ case A0 of
+ false -> ok;
+ _ -> prim_inet:setopt(S, active, A0)
+ end,
ok
catch
error:Reason ->
diff --git a/lib/kernel/src/inet_config.erl b/lib/kernel/src/inet_config.erl
index 1ddbdcec25..526baca335 100644
--- a/lib/kernel/src/inet_config.erl
+++ b/lib/kernel/src/inet_config.erl
@@ -197,16 +197,6 @@ do_load_resolv({win32,Type}, longnames) ->
win32_load_from_registry(Type),
inet_db:set_lookup([native]);
-do_load_resolv(vxworks, _) ->
- vxworks_load_hosts(),
- inet_db:set_lookup([file, dns]),
- case os:getenv("ERLRESCONF") of
- false ->
- no_ERLRESCONF;
- Resolv ->
- load_resolv(Resolv, resolv)
- end;
-
do_load_resolv(_, _) ->
inet_db:set_lookup([native]).
@@ -408,55 +398,6 @@ win32_get_strings(Reg, [Name|Rest], Result) ->
win32_get_strings(_, [], Result) ->
lists:reverse(Result).
-%%
-%% Load host data from VxWorks hostShow command
-%%
-
-vxworks_load_hosts() ->
- HostShow = os:cmd("hostShow"),
- case check_hostShow(HostShow) of
- Hosts when is_list(Hosts) ->
- case inet_parse:hosts_vxworks({chars, Hosts}) of
- {ok, Ls} ->
- foreach(
- fun({IP, Name, Aliases}) ->
- inet_db:add_host(IP, [Name|Aliases])
- end,
- Ls);
- {error,Reason} ->
- error("parser error VxWorks hostShow ~s", [Reason])
- end;
- _Error ->
- error("error in VxWorks hostShow~s~n", [HostShow])
- end.
-
-%%
-%% Check if hostShow yields at least two line; the first one
-%% starting with "hostname", the second one starting with
-%% "--------".
-%% Returns: list of hosts in VxWorks notation
-%% rows of 'Name IP [Aliases] \n'
-%% if hostShow yielded these two lines, false otherwise.
-check_hostShow(HostShow) ->
- check_hostShow(["hostname", "--------"], HostShow).
-
-check_hostShow([], HostShow) ->
- HostShow;
-check_hostShow([String_match|Rest], HostShow) ->
- case lists:prefix(String_match, HostShow) of
- true ->
- check_hostShow(Rest, next_line(HostShow));
- false ->
- false
- end.
-
-next_line([]) ->
- [];
-next_line([$\n|Rest]) ->
- Rest;
-next_line([_First|Rest]) ->
- next_line(Rest).
-
read_rc() ->
{RcFile,CfgList} = read_inetrc(),
case extract_cfg_files(CfgList, [], []) of
diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl
index cf893c73eb..467c4d5065 100644
--- a/lib/kernel/src/inet_int.hrl
+++ b/lib/kernel/src/inet_int.hrl
@@ -134,7 +134,6 @@
-define(INET_LOPT_EXITONCLOSE, 26).
-define(INET_LOPT_TCP_HIWTRMRK, 27).
-define(INET_LOPT_TCP_LOWTRMRK, 28).
--define(INET_LOPT_BIT8, 29).
-define(INET_LOPT_TCP_SEND_TIMEOUT, 30).
-define(INET_LOPT_TCP_DELAY_SEND, 31).
-define(INET_LOPT_PACKET_SIZE, 32).
@@ -186,12 +185,6 @@
-define(TCP_PB_HTTP_BIN,13).
-define(TCP_PB_HTTPH_BIN,14).
-%% bit options, INET_LOPT_BIT8
--define(INET_BIT8_CLEAR, 0).
--define(INET_BIT8_SET, 1).
--define(INET_BIT8_ON, 2).
--define(INET_BIT8_OFF, 3).
-
%% getstat, INET_REQ_GETSTAT
-define(INET_STAT_RECV_CNT, 1).
diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl
index 65edddcb46..ba62a59068 100644
--- a/lib/kernel/src/inet_parse.erl
+++ b/lib/kernel/src/inet_parse.erl
@@ -23,7 +23,6 @@
%% Avoid warning for local function error/2 clashing with autoimported BIF.
-compile({no_auto_import,[error/2]}).
-export([hosts/1, hosts/2]).
--export([hosts_vxworks/1]).
-export([protocols/1, protocols/2]).
-export([netmasks/1, netmasks/2]).
-export([networks/1, networks/2]).
@@ -107,18 +106,6 @@ hosts(Fname,File) ->
parse_file(Fname, File, Fn).
%% --------------------------------------------------------------------------
-%% Parse hostShow vxworks style
-%% Syntax:
-%% Name IP [Aliases] \n
-%% --------------------------------------------------------------------------
-hosts_vxworks(Hosts) ->
- Fn = fun([Name, Address | Aliases]) ->
- {ok,IP} = address(Address),
- {IP, Name, Aliases}
- end,
- parse_file(Hosts, Fn).
-
-%% --------------------------------------------------------------------------
%% Parse resolv file unix style
%% Syntax:
%% domain Domain \n
@@ -291,9 +278,6 @@ networks(Fname, File) ->
%%
%% --------------------------------------------------------------------------
-parse_file(File, Fn) ->
- parse_file(noname, File, Fn).
-
parse_file(Fname, {fd,Fd}, Fn) ->
parse_fd(Fname,Fd, 1, Fn, []);
parse_file(Fname, {chars,Cs}, Fn) when is_list(Cs) ->
diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src
index bded2408a7..54628800a8 100644
--- a/lib/kernel/src/kernel.appup.src
+++ b/lib/kernel/src/kernel.appup.src
@@ -17,11 +17,11 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max two major revisions back
- [{<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
- {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
- {<<"2\\.13(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R13
+ [{<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R14
%% Down to - max two major revisions back
- [{<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
- {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
- {<<"2\\.13(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R13
+ [{<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R14
}.
diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl
index 9e3d730cee..0d59e7af67 100644
--- a/lib/kernel/src/net_kernel.erl
+++ b/lib/kernel/src/net_kernel.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -142,6 +142,17 @@
-include("net_address.hrl").
+%%% BIF
+
+-export([dflag_unicode_io/1]).
+
+-spec dflag_unicode_io(pid()) -> boolean().
+
+dflag_unicode_io(_) ->
+ erlang:nif_error(undef).
+
+%%% End of BIF
+
%% Interface functions
kernel_apply(M,F,A) -> request({apply,M,F,A}).
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index f6769df585..e20a2434b4 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -24,16 +24,48 @@
-include("file.hrl").
--spec type() -> vxworks | {Osfamily, Osname} when
+%%% BIFs
+
+-export([getenv/0, getenv/1, getpid/0, putenv/2, timestamp/0]).
+
+-spec getenv() -> [string()].
+
+getenv() -> erlang:nif_error(undef).
+
+-spec getenv(VarName) -> Value | false when
+ VarName :: string(),
+ Value :: string().
+
+getenv(_) ->
+ erlang:nif_error(undef).
+
+-spec getpid() -> Value when
+ Value :: string().
+
+getpid() ->
+ erlang:nif_error(undef).
+
+-spec putenv(VarName, Value) -> true when
+ VarName :: string(),
+ Value :: string().
+
+putenv(_, _) ->
+ erlang:nif_error(undef).
+
+-spec timestamp() -> Timestamp when
+ Timestamp :: erlang:timestamp().
+
+timestamp() ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
+
+-spec type() -> {Osfamily, Osname} when
Osfamily :: unix | win32,
Osname :: atom().
type() ->
- case erlang:system_info(os_type) of
- {vxworks, _} ->
- vxworks;
- Else -> Else
- end.
+ erlang:system_info(os_type).
-spec version() -> VersionString | {Major, Minor, Release} when
VersionString :: string(),
@@ -83,25 +115,14 @@ find_executable1(_Name, [], _Extensions) ->
verify_executable(Name0, [Ext|Rest], OrigExtensions) ->
Name1 = Name0 ++ Ext,
- case os:type() of
- vxworks ->
- %% We consider all existing VxWorks files to be executable
- case file:read_file_info(Name1) of
- {ok, _} ->
- {ok, Name1};
- _ ->
- verify_executable(Name0, Rest, OrigExtensions)
- end;
+ case file:read_file_info(Name1) of
+ {ok, #file_info{type=regular,mode=Mode}}
+ when Mode band 8#111 =/= 0 ->
+ %% XXX This test for execution permission is not fool-proof
+ %% on Unix, since we test if any execution bit is set.
+ {ok, Name1};
_ ->
- case file:read_file_info(Name1) of
- {ok, #file_info{type=regular,mode=Mode}}
- when Mode band 8#111 =/= 0 ->
- %% XXX This test for execution permission is not fool-proof
- %% on Unix, since we test if any execution bit is set.
- {ok, Name1};
- _ ->
- verify_executable(Name0, Rest, OrigExtensions)
- end
+ verify_executable(Name0, Rest, OrigExtensions)
end;
verify_executable(Name, [], OrigExtensions) when OrigExtensions =/= [""] -> %% Windows
%% Will only happen on windows, hence case insensitivity
@@ -154,8 +175,7 @@ reverse_element(List) ->
extensions() ->
case type() of
{win32, _} -> [".exe",".com",".cmd",".bat"];
- {unix, _} -> [""];
- vxworks -> [""]
+ {unix, _} -> [""]
end.
%% Executes the given command in the default shell for the operating system.
@@ -173,11 +193,6 @@ cmd(Cmd) ->
{Cspec,_} -> lists:concat([Cspec," /c",Cmd])
end,
Port = open_port({spawn, Command}, [stream, in, eof, hide]),
- get_data(Port, []);
- %% VxWorks uses a 'sh -c hook' in 'vxcall.c' to run os:cmd.
- vxworks ->
- Command = lists:concat(["sh -c '", Cmd, "'"]),
- Port = open_port({spawn, Command}, [stream, in, eof]),
get_data(Port, [])
end.
diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl
index e214ffa404..0b1fc6e939 100644
--- a/lib/kernel/src/rpc.erl
+++ b/lib/kernel/src/rpc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -286,7 +286,7 @@ call(N,M,F,A) ->
Reason :: term(),
Timeout :: timeout().
-call(N,M,F,A,_Timeout) when node() =:= N -> %% Optimize local call
+call(N,M,F,A,infinity) when node() =:= N -> %% Optimize local call
local_call(M,F,A);
call(N,M,F,A,infinity) ->
do_call(N, {call,M,F,A,group_leader()}, infinity);
diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile
index 7bb3e3a365..8eca37029d 100644
--- a/lib/kernel/test/Makefile
+++ b/lib/kernel/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/kernel/test/bif_SUITE.erl b/lib/kernel/test/bif_SUITE.erl
index a2826f34df..c369dca4e1 100644
--- a/lib/kernel/test/bif_SUITE.erl
+++ b/lib/kernel/test/bif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2012. 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/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 3e8bdaf1ff..5e0300639e 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -25,6 +25,7 @@
replace_path/1, load_file/1, load_abs/1, ensure_loaded/1,
delete/1, purge/1, soft_purge/1, is_loaded/1, all_loaded/1,
load_binary/1, dir_req/1, object_code/1, set_path_file/1,
+ upgrade/1,
sticky_dir/1, pa_pz_option/1, add_del_path/1,
dir_disappeared/1, ext_mod_dep/1, clash/1,
load_cached/1, start_node_with_cache/1, add_and_rehash/1,
@@ -43,6 +44,8 @@
handle_event/2, handle_call/2, handle_info/2,
terminate/2]).
+-export([compile_load/4]).
+
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
@@ -50,6 +53,7 @@ all() ->
replace_path, load_file, load_abs, ensure_loaded,
delete, purge, soft_purge, is_loaded, all_loaded,
load_binary, dir_req, object_code, set_path_file,
+ upgrade,
pa_pz_option, add_del_path, dir_disappeared,
ext_mod_dep, clash, load_cached, start_node_with_cache,
add_and_rehash, where_is_file_no_cache,
@@ -450,6 +454,46 @@ load_binary(Config) when is_list(Config) ->
code:delete(code_b_test),
ok.
+upgrade(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ %%T = [beam, hipe],
+ T = [beam],
+
+ [upgrade_do(DataDir, Client, U1, U2, O1, O2)
+ || Client<-T, U1<-T, U2<-T, O1<-T, O2<-T],
+
+ ok.
+
+upgrade_do(DataDir, Client, U1, U2, O1, O2) ->
+ compile_load(upgrade_client, DataDir, undefined, Client),
+ upgrade_client:run(DataDir, U1, U2, O1, O2),
+ ok.
+
+compile_load(Mod, Dir, Ver, CodeType) ->
+ Version = case Ver of
+ undefined ->
+ io:format("Compiling '~p' as ~p\n", [Mod, CodeType]),
+ [];
+ _ ->
+ io:format("Compiling version ~p of '~p' as ~p\n",
+ [Ver, Mod, CodeType]),
+ [{d,list_to_atom("VERSION_" ++ integer_to_list(Ver))}]
+ end,
+ Target = case CodeType of
+ beam -> [];
+ hipe -> [native]
+ end,
+ CompOpts = [binary, report] ++ Target ++ Version,
+
+ Src = filename:join(Dir, atom_to_list(Mod) ++ ".erl"),
+ %io:format("compile:file(~p,~p)\n", [Src, CompOpts]),
+ {ok,Mod,Code} = compile:file(Src, CompOpts),
+ ObjFile = filename:basename(Src,".erl") ++ ".beam",
+ {module,Mod} = code:load_binary(Mod, ObjFile, Code),
+ %IsNative = code:is_module_native(Mod),
+ ok.
+
dir_req(suite) -> [];
dir_req(doc) -> [];
dir_req(Config) when is_list(Config) ->
@@ -535,30 +579,25 @@ sticky_compiler(File) ->
pa_pz_option(suite) -> [];
pa_pz_option(doc) -> ["Test that the -pa and -pz options work as expected"];
pa_pz_option(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {comment, "Slave nodes not supported on VxWorks"};
- _ ->
- DDir = ?config(data_dir,Config),
- PaDir = filename:join(DDir,"pa"),
- PzDir = filename:join(DDir,"pz"),
- ?line {ok, Node}=?t:start_node(pa_pz1, slave,
- [{args,
- "-pa " ++ PaDir
- ++ " -pz " ++ PzDir}]),
- ?line Ret=rpc:call(Node, code, get_path, []),
- ?line [PaDir|Paths] = Ret,
- ?line [PzDir|_] = lists:reverse(Paths),
- ?t:stop_node(Node),
- ?line {ok, Node2}=?t:start_node(pa_pz2, slave,
- [{args,
- "-mode embedded " ++ "-pa "
- ++ PaDir ++ " -pz " ++ PzDir}]),
- ?line Ret2=rpc:call(Node2, code, get_path, []),
- ?line [PaDir|Paths2] = Ret2,
- ?line [PzDir|_] = lists:reverse(Paths2),
- ?t:stop_node(Node2)
- end.
+ DDir = ?config(data_dir,Config),
+ PaDir = filename:join(DDir,"pa"),
+ PzDir = filename:join(DDir,"pz"),
+ {ok, Node}=?t:start_node(pa_pz1, slave,
+ [{args,
+ "-pa " ++ PaDir
+ ++ " -pz " ++ PzDir}]),
+ Ret=rpc:call(Node, code, get_path, []),
+ [PaDir|Paths] = Ret,
+ [PzDir|_] = lists:reverse(Paths),
+ ?t:stop_node(Node),
+ {ok, Node2}=?t:start_node(pa_pz2, slave,
+ [{args,
+ "-mode embedded " ++ "-pa "
+ ++ PaDir ++ " -pz " ++ PzDir}]),
+ Ret2=rpc:call(Node2, code, get_path, []),
+ [PaDir|Paths2] = Ret2,
+ [PzDir|_] = lists:reverse(Paths2),
+ ?t:stop_node(Node2).
add_del_path(suite) ->
[];
@@ -1550,7 +1589,8 @@ native_early_modules_1(Architecture) ->
true ->
?line true = lists:all(fun code:is_module_native/1,
[ets,file,filename,gb_sets,gb_trees,
- hipe_unified_loader,lists,os,packages]),
+ %%hipe_unified_loader, no_native as workaround
+ lists,os,packages]),
ok
end.
diff --git a/lib/kernel/test/code_SUITE_data/other.erl b/lib/kernel/test/code_SUITE_data/other.erl
new file mode 100644
index 0000000000..58ce87f222
--- /dev/null
+++ b/lib/kernel/test/code_SUITE_data/other.erl
@@ -0,0 +1,38 @@
+-module(other).
+
+-ifdef(VERSION_1).
+-define(VERSION,1).
+-export([exp1/0]).
+-export([exp1loc2/0]).
+-export([exp1exp2/0]).
+exp1() -> check([loc1(),exp1loc2(),exp1exp2(),loc1exp2(),loc1loc2()]).
+loc1() -> check([exp1loc2(),exp1exp2(),loc1exp2(),loc1loc2()]).
+exp1loc2() -> check([exp1exp2(),loc1exp2(),loc1loc2()]).
+exp1exp2() -> check([loc1exp2(),loc1loc2()]).
+loc1exp2() -> check([loc1loc2()]).
+-endif. % VERSION_1
+
+-ifdef(VERSION_2).
+-define(VERSION,2).
+-export([exp2/0]).
+-export([loc1exp2/0]).
+-export([exp1exp2/0]).
+loc1exp2() -> check([exp1exp2(),exp1loc2(),loc2(),exp2(),loc1loc2()]).
+exp1exp2() -> check([exp1loc2(),loc2(),exp2(),loc1loc2()]).
+exp1loc2() -> check([loc2(),exp2(),loc1loc2()]).
+loc2() -> check([exp2(),loc1loc2()]).
+exp2() -> check([loc1loc2()]).
+
+-endif. % VERSION_2
+
+loc1loc2() -> ?VERSION.
+
+
+check(VerList) ->
+ case lists:all(fun(?VERSION) -> true; (_) -> false end,
+ VerList) of
+ true ->
+ ?VERSION;
+ false ->
+ VerList
+ end.
diff --git a/lib/kernel/test/code_SUITE_data/upgrade_client.erl b/lib/kernel/test/code_SUITE_data/upgrade_client.erl
new file mode 100644
index 0000000000..bb655e01d3
--- /dev/null
+++ b/lib/kernel/test/code_SUITE_data/upgrade_client.erl
@@ -0,0 +1,259 @@
+-module(upgrade_client).
+
+-export([run/5]).
+
+%%-define(line, io:format("~s:~p\n", [?MODULE,?LINE]),).
+-define(line,).
+
+run(Dir, Upgradee1, Upgradee2, Other1, Other2) ->
+ %% Load version 1 of upgradee
+ code_SUITE:compile_load(upgradee, Dir, 1, Upgradee1),
+
+ ?line 1 = upgradee:exp1(),
+ ?line 1 = upgradee:exp1exp2(),
+ ?line 1 = upgradee:exp1loc2(),
+
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1exp2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1loc2()),
+
+ P = spawn_link(upgradee,dispatch_loop,[]),
+
+ ?line 1 = proxy_call(P, local, exp1),
+ ?line 1 = proxy_call(P, local, loc1),
+ ?line 1 = proxy_call(P, local, exp1exp2),
+ ?line 1 = proxy_call(P, local, exp1loc2),
+ ?line 1 = proxy_call(P, local, loc1exp2),
+ ?line 1 = proxy_call(P, local, loc1loc2),
+ ?line 1 = proxy_call(P, external, exp1),
+ ?line 1 = proxy_call(P, external, exp1exp2),
+ ?line 1 = proxy_call(P, external, exp1loc2),
+
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc2),
+ ?line {cannot_compile,1} = proxy_call(P, local, exp2),
+ ?line {cannot_compile,1} = proxy_call(P, local, loc2),
+
+ ?line {'EXIT',{undef,_}} = (catch other:exp1()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch other:exp1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch other:exp1exp2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc11exp2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch other:exp2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc2()),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2),
+
+ %%
+ %% Load version 1 of other
+ %%
+ code_SUITE:compile_load(other, Dir, 1, Other1),
+ ?line 1 = other:exp1(),
+ ?line 1 = other:exp1loc2(),
+ ?line 1 = other:exp1exp2(),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1exp2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch other:exp2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc2()),
+
+ ?line 1 = proxy_call(P, other, exp1),
+ ?line 1 = proxy_call(P, other, exp1loc2),
+ ?line 1 = proxy_call(P, other, exp1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2),
+
+ %%
+ %% Load version 2 of upgradee
+ %%
+ code_SUITE:compile_load(upgradee, Dir, 2, Upgradee2),
+
+ ?line 2 = upgradee:exp2(),
+ ?line 2 = upgradee:exp1exp2(),
+ ?line 2 = upgradee:loc1exp2(),
+
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp1()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc2()),
+
+ ?line 1 = proxy_call(P, local, exp1),
+ ?line 1 = proxy_call(P, local, loc1),
+ ?line 1 = proxy_call(P, local, exp1exp2),
+ ?line 1 = proxy_call(P, local, exp1loc2),
+ ?line 1 = proxy_call(P, local, loc1exp2),
+ ?line 1 = proxy_call(P, local, loc1loc2),
+ ?line {cannot_compile,1} = proxy_call(P, local, exp2),
+ ?line {cannot_compile,1} = proxy_call(P, local, loc2),
+
+ ?line 2 = proxy_call(P, external, exp1exp2),
+ ?line 2 = proxy_call(P, external, loc1exp2),
+ ?line 2 = proxy_call(P, external, exp2),
+
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc2),
+
+ ?line 1 = other:exp1(),
+ ?line 1 = other:exp1loc2(),
+ ?line 1 = other:exp1exp2(),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1exp2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch other:exp2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc2()),
+
+ ?line 1 = proxy_call(P, other, exp1),
+ ?line 1 = proxy_call(P, other, exp1loc2),
+ ?line 1 = proxy_call(P, other, exp1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2),
+
+ %%
+ %% Load version 2 of other
+ %%
+ code_SUITE:compile_load(other, Dir, 2, Other2),
+
+ ?line 2 = upgradee:exp2(),
+ ?line 2 = upgradee:exp1exp2(),
+ ?line 2 = upgradee:loc1exp2(),
+
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp1()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc2()),
+
+ ?line 1 = proxy_call(P, local, exp1),
+ ?line 1 = proxy_call(P, local, loc1),
+ ?line 1 = proxy_call(P, local, exp1exp2),
+ ?line 1 = proxy_call(P, local, exp1loc2),
+ ?line 1 = proxy_call(P, local, loc1exp2),
+ ?line 1 = proxy_call(P, local, loc1loc2),
+ ?line {cannot_compile,1} = proxy_call(P, local, exp2),
+ ?line {cannot_compile,1} = proxy_call(P, local, loc2),
+
+ ?line 2 = proxy_call(P, external, exp1exp2),
+ ?line 2 = proxy_call(P, external, loc1exp2),
+ ?line 2 = proxy_call(P, external, exp2),
+
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc2),
+
+ ?line 2 = other:exp2(),
+ ?line 2 = other:loc1exp2(),
+ ?line 2 = other:exp1exp2(),
+ ?line {'EXIT',{undef,_}} = (catch other:exp1()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch other:exp1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc2()),
+
+ ?line 2 = proxy_call(P, other, exp2),
+ ?line 2 = proxy_call(P, other, loc1exp2),
+ ?line 2 = proxy_call(P, other, exp1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2),
+
+
+ %%
+ %% Upgrade proxy to version 2
+ %%
+ P ! upgrade_order,
+
+
+ %%
+ io:format("Delete version 2 of 'upgradee'\n",[]),
+ %%
+ code:purge(upgradee),
+ code:delete(upgradee),
+
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp1exp2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1exp2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp1()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc2()),
+
+ ?line 2 = proxy_call(P, local, exp2),
+ ?line 2 = proxy_call(P, local, loc2),
+ ?line 2 = proxy_call(P, local, exp1exp2),
+ ?line 2 = proxy_call(P, local, exp1loc2),
+ ?line 2 = proxy_call(P, local, loc1exp2),
+ ?line 2 = proxy_call(P, local, loc1loc2),
+ ?line {cannot_compile,2} = proxy_call(P, local, exp1),
+ ?line {cannot_compile,2} = proxy_call(P, local, loc1),
+
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc2),
+
+ ?line 2 = other:exp2(),
+ ?line 2 = other:loc1exp2(),
+ ?line 2 = other:exp1exp2(),
+ ?line {'EXIT',{undef,_}} = (catch other:exp1()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch other:exp1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc2()),
+
+ ?line 2 = proxy_call(P, other, exp2),
+ ?line 2 = proxy_call(P, other, loc1exp2),
+ ?line 2 = proxy_call(P, other, exp1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2),
+
+ unlink(P),
+ exit(P, die_please),
+
+ io:format("Purge 'upgradee'\n",[]),
+ code:purge(upgradee),
+
+ io:format("Delete and purge 'other'\n",[]),
+ code:purge(other),
+ code:delete(other),
+ code:purge(other),
+ ok.
+
+proxy_call(Pid, CallType, Func) ->
+ Pid ! {self(), CallType, Func},
+ receive
+ {Pid, call_result, Func, Ret} -> Ret
+ end.
diff --git a/lib/kernel/test/code_SUITE_data/upgradee.erl b/lib/kernel/test/code_SUITE_data/upgradee.erl
new file mode 100644
index 0000000000..62b1d95e30
--- /dev/null
+++ b/lib/kernel/test/code_SUITE_data/upgradee.erl
@@ -0,0 +1,123 @@
+-module(upgradee).
+
+-export([dispatch_loop/0]).
+
+-ifdef(VERSION_1).
+-define(VERSION,1).
+
+-export([exp1/0]). % only exported in v1
+-export([exp1loc2/0]). % exported in v1, local in v2
+-export([exp1exp2/0]). % exported in v1 and v2
+
+exp1() -> ?VERSION.
+loc1() -> ?VERSION.
+
+-endif. % VERSION_1
+
+-ifdef(VERSION_2).
+-define(VERSION,2).
+
+-export([exp2/0]).
+-export([loc1exp2/0]).
+-export([exp1exp2/0]).
+
+exp2() -> ?VERSION.
+loc2() -> ?VERSION.
+
+-endif. % VERSION_2
+
+exp1exp2() -> ?VERSION.
+exp1loc2() -> ?VERSION.
+loc1exp2() -> ?VERSION.
+loc1loc2() -> ?VERSION.
+
+dispatch_loop() ->
+ receive
+ upgrade_order ->
+ %%erlang:display({"upgradee version", ?VERSION, "got upgrade_order"}),
+ ?MODULE:dispatch_loop();
+
+ Msg ->
+ %%erlang:display({"upgradee version", ?VERSION, "got msg", Msg}),
+ {Func,Ret} = case Msg of
+ %% Local calls
+ {Pid, local, F=exp1} ->
+ {F, local_exp1()};
+ {Pid, local, F=loc1} ->
+ {F, local_loc1()};
+ {Pid, local, F=exp1exp2} ->
+ {F, catch exp1exp2()};
+ {Pid, local, F=exp1loc2} ->
+ {F, catch exp1loc2()};
+ {Pid, local, F=loc1exp2} ->
+ {F, catch loc1exp2()};
+ {Pid, local, F=loc1loc2} ->
+ {F, catch loc1loc2()};
+ {Pid, local, F=exp2} ->
+ {F, local_exp2()};
+ {Pid, local, F=loc2} ->
+ {F, local_loc2()};
+
+ %% Extern calls to own module
+ {Pid, external, F=exp1} ->
+ {F, catch ?MODULE:exp1()};
+ {Pid, external, F=loc1} ->
+ {F, catch ?MODULE:loc1()};
+ {Pid, external, F=exp1exp2} ->
+ {F, catch ?MODULE:exp1exp2()};
+ {Pid, external, F=exp1loc2} ->
+ {F, catch ?MODULE:exp1loc2()};
+ {Pid, external, F=loc1exp2} ->
+ {F, catch ?MODULE:loc1exp2()};
+ {Pid, external, F=loc1loc2} ->
+ {F, catch ?MODULE:loc1loc2()};
+ {Pid, external, F=exp2} ->
+ {F, catch ?MODULE:exp2()};
+ {Pid, external, F=loc2} ->
+ {F, catch ?MODULE:loc2()};
+
+ %% External calls to other module
+ {Pid, other, F=exp1} ->
+ {F, catch other:exp1()};
+ {Pid, other, F=loc1} ->
+ {F, catch other:loc1()};
+ {Pid, other, F=exp1exp2} ->
+ {F, catch other:exp1exp2()};
+ {Pid, other, F=exp1loc2} ->
+ {F, catch other:exp1loc2()};
+ {Pid, other, F=loc1exp2} ->
+ {F, catch other:loc1exp2()};
+ {Pid, other, F=loc1loc2} ->
+ {F, catch other:loc1loc2()};
+ {Pid, other, F=exp2} ->
+ {F, catch other:exp2()};
+ {Pid, other, F=loc2} ->
+ {F, catch other:loc2()}
+ end,
+ Pid ! {self(), call_result, Func, Ret},
+
+ dispatch_loop() % A local call, we don't want to upgrade the dispatcher
+ end.
+
+
+
+-ifdef(VERSION_1).
+local_exp1() -> catch exp1().
+local_loc1() -> catch loc1().
+-else.
+local_exp1() ->
+ %%erlang:display({"upgradee:local_exp1 in version", ?VERSION}),
+ {cannot_compile,?VERSION}.
+local_loc1() -> {cannot_compile,?VERSION}.
+-endif.
+
+-ifdef(VERSION_2).
+local_exp2() -> catch exp2().
+local_loc2() -> catch loc2().
+-else.
+local_exp2() ->
+ %%erlang:display({"upgradee:local_exp2 in version", ?VERSION}),
+ {cannot_compile,?VERSION}.
+local_loc2() ->
+ {cannot_compile,?VERSION}.
+-endif.
diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl
index 0c3f5c3514..0f811b8f73 100644
--- a/lib/kernel/test/disk_log_SUITE.erl
+++ b/lib/kernel/test/disk_log_SUITE.erl
@@ -126,11 +126,6 @@
error, chunk, truncate, many_users, info, change_size,
change_attribute, distribution, evil, otp_6278, otp_10131]).
-%% The following two lists should be mutually exclusive. To skip a case
-%% on VxWorks altogether, use the kernel.spec.vxworks file instead.
-%% PLEASE don't skip out of laziness, the goal is to make every
-%% testcase runnable on VxWorks.
-
%% These test cases should be skipped if the VxWorks card is
%% configured without NFS cache.
-define(SKIP_NO_CACHE,[distribution]).
@@ -5126,33 +5121,8 @@ stop_node(Node) ->
%% If the board is configured without NFS, the port program will fail to load
%% and this will return 0, which may or may not be the wrong thing to do.
-check_nfs(Config) ->
- case (catch check_cache(Config)) of
- N when is_integer(N) ->
- N;
- _ ->
- 0
- end.
-
-check_cache(Config) ->
- ?line Check = filename:join(?datadir(Config), "nfs_check"),
- ?line P = open_port({spawn, Check}, [{line,100}, eof]),
- ?line Size = receive
- {P,{data,{eol,S}}} ->
- list_to_integer(S)
- after 1000 ->
- erlang:display(got_timeout),
- exit(timeout)
- end,
- ?line receive
- {P, eof} ->
- ok
- end,
- ?line P ! {self(), close},
- ?line receive
- {P, closed} -> ok
- end,
- Size.
+check_nfs(_Config) ->
+ 0.
skip_expand([]) ->
[];
@@ -5175,13 +5145,8 @@ skip_list(Config) ->
skip_expand(?SKIP_LARGE_CACHE)
end.
-should_skip(Test,Config) ->
- case os:type() of
- vxworks ->
- lists:member(Test, skip_list(Config));
- _ ->
- false
- end.
+should_skip(_Test,_Config) ->
+ false.
%%-----------------------------------------------------------------
%% The error_logger handler used.
diff --git a/lib/kernel/test/disk_log_SUITE_data/Makefile.src b/lib/kernel/test/disk_log_SUITE_data/Makefile.src
deleted file mode 100644
index cae2f23d29..0000000000
--- a/lib/kernel/test/disk_log_SUITE_data/Makefile.src
+++ /dev/null
@@ -1,15 +0,0 @@
-CC = @CC@
-LD = @LD@
-CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@
-CROSSLDFLAGS = @CROSSLDFLAGS@
-
-PROGS = nfs_check@exe@
-
-all: $(PROGS)
-
-nfs_check@exe@: nfs_check@obj@
- $(LD) $(CROSSLDFLAGS) -o nfs_check nfs_check@obj@ @LIBS@
-
-nfs_check@obj@: nfs_check.c
- $(CC) -c -o nfs_check@obj@ $(CFLAGS) nfs_check.c
-
diff --git a/lib/kernel/test/disk_log_SUITE_data/nfs_check.c b/lib/kernel/test/disk_log_SUITE_data/nfs_check.c
deleted file mode 100644
index 31e9ba8190..0000000000
--- a/lib/kernel/test/disk_log_SUITE_data/nfs_check.c
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Author: Patrik Nyblom
- * Purpose: A port program to check the NFS cache size on VxWorks (returns 0
- * for other platforms).
- */
-
-#ifdef VXWORKS
-#include <vxWorks.h>
-#include <taskVarLib.h>
-#include <taskLib.h>
-#include <sysLib.h>
-#include <string.h>
-#include <ioLib.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#ifdef VXWORKS
-extern unsigned nfsCacheSize;
-#define MAIN(argc, argv) nfs_check(argc, argv)
-#else
-#define MAIN(argc, argv) main(argc, argv)
-#endif
-
-
-MAIN(argc, argv)
-int argc;
-char *argv[];
-{
-#ifdef VXWORKS
- char str[100];
- sprintf(str,"%d\n", nfsCacheSize);
- write(1, str, strlen(str));
-#else
- fprintf(stdout,"0");
- fflush(stdout);
-#endif
- return 0;
-}
-
diff --git a/lib/kernel/test/erl_prim_loader_SUITE.erl b/lib/kernel/test/erl_prim_loader_SUITE.erl
index 6f4f27d594..35502a1d27 100644
--- a/lib/kernel/test/erl_prim_loader_SUITE.erl
+++ b/lib/kernel/test/erl_prim_loader_SUITE.erl
@@ -110,56 +110,46 @@ get_file(Config) when is_list(Config) ->
inet_existing(doc) -> ["Start a node using the 'inet' loading method, ",
"from an already started boot server."];
inet_existing(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {comment, "VxWorks: tested separately"};
- _ ->
- ?line Name = erl_prim_test_inet_existing,
- ?line Host = host(),
- ?line Cookie = atom_to_list(erlang:get_cookie()),
- ?line IpStr = ip_str(Host),
- ?line LFlag = get_loader_flag(os:type()),
- ?line Args = LFlag ++ " -hosts " ++ IpStr ++
- " -setcookie " ++ Cookie,
- ?line {ok, BootPid} = erl_boot_server:start_link([Host]),
- ?line {ok, Node} = start_node(Name, Args),
- ?line {ok,[["inet"]]} = rpc:call(Node, init, get_argument, [loader]),
- ?line stop_node(Node),
- ?line unlink(BootPid),
- ?line exit(BootPid, kill),
- ok
- end.
+ Name = erl_prim_test_inet_existing,
+ Host = host(),
+ Cookie = atom_to_list(erlang:get_cookie()),
+ IpStr = ip_str(Host),
+ LFlag = get_loader_flag(os:type()),
+ Args = LFlag ++ " -hosts " ++ IpStr ++
+ " -setcookie " ++ Cookie,
+ {ok, BootPid} = erl_boot_server:start_link([Host]),
+ {ok, Node} = start_node(Name, Args),
+ {ok,[["inet"]]} = rpc:call(Node, init, get_argument, [loader]),
+ stop_node(Node),
+ unlink(BootPid),
+ exit(BootPid, kill),
+ ok.
inet_coming_up(doc) -> ["Start a node using the 'inet' loading method, ",
"but start the boot server afterwards."];
inet_coming_up(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {comment, "VxWorks: tested separately"};
- _ ->
- ?line Name = erl_prim_test_inet_coming_up,
- ?line Cookie = atom_to_list(erlang:get_cookie()),
- ?line Host = host(),
- ?line IpStr = ip_str(Host),
- ?line LFlag = get_loader_flag(os:type()),
- ?line Args = LFlag ++
- " -hosts " ++ IpStr ++
- " -setcookie " ++ Cookie,
- ?line {ok, Node} = start_node(Name, Args, [{wait, false}]),
-
- %% Wait a while, then start boot server, and wait for node to start.
- ?line test_server:sleep(test_server:seconds(6)),
- io:format("erl_boot_server:start_link([~p]).", [Host]),
- ?line {ok, BootPid} = erl_boot_server:start_link([Host]),
- ?line wait_really_started(Node, 25),
-
- %% Check loader argument, then cleanup.
- ?line {ok,[["inet"]]} = rpc:call(Node, init, get_argument, [loader]),
- ?line stop_node(Node),
- ?line unlink(BootPid),
- ?line exit(BootPid, kill),
- ok
- end.
+ Name = erl_prim_test_inet_coming_up,
+ Cookie = atom_to_list(erlang:get_cookie()),
+ Host = host(),
+ IpStr = ip_str(Host),
+ LFlag = get_loader_flag(os:type()),
+ Args = LFlag ++
+ " -hosts " ++ IpStr ++
+ " -setcookie " ++ Cookie,
+ {ok, Node} = start_node(Name, Args, [{wait, false}]),
+
+ %% Wait a while, then start boot server, and wait for node to start.
+ test_server:sleep(test_server:seconds(6)),
+ io:format("erl_boot_server:start_link([~p]).", [Host]),
+ {ok, BootPid} = erl_boot_server:start_link([Host]),
+ wait_really_started(Node, 25),
+
+ %% Check loader argument, then cleanup.
+ {ok,[["inet"]]} = rpc:call(Node, init, get_argument, [loader]),
+ stop_node(Node),
+ unlink(BootPid),
+ exit(BootPid, kill),
+ ok.
wait_really_started(Node, 0) ->
test_server:fail({not_booted,Node});
@@ -249,8 +239,6 @@ multiple_slaves(doc) ->
"verify that the boot server manages"];
multiple_slaves(Config) when is_list(Config) ->
case os:type() of
- vxworks ->
- {comment, "VxWorks: tested separately"};
{ose,_} ->
{comment, "OSE: multiple nodes not supported"};
_ ->
@@ -426,7 +414,9 @@ primary_archive(Config) when is_list(Config) ->
ExpectedEbins = [Archive, DictDir ++ "/ebin", DummyDir ++ "/ebin"],
io:format("ExpectedEbins: ~p\n", [ExpectedEbins]),
?line {ok, FileInfo} = prim_file:read_file_info(Archive),
- ?line {ok, Ebins} = rpc:call(Node, erl_prim_loader, set_primary_archive, [Archive, ArchiveBin, FileInfo]),
+ ?line {ok, Ebins} = rpc:call(Node, erl_prim_loader, set_primary_archive,
+ [Archive, ArchiveBin, FileInfo,
+ fun escript:parse_file/1]),
?line ExpectedEbins = lists:sort(Ebins), % assert
?line {ok, TopFiles2} = rpc:call(Node, erl_prim_loader, list_dir, [Archive]),
@@ -435,7 +425,9 @@ primary_archive(Config) when is_list(Config) ->
?line ok = test_archive(Node, Archive, DictDir, BeamName),
%% Cleanup
- ?line {ok, []} = rpc:call(Node, erl_prim_loader, set_primary_archive, [undefined, undefined, undefined]),
+ ?line {ok, []} = rpc:call(Node, erl_prim_loader, set_primary_archive,
+ [undefined, undefined, undefined,
+ fun escript:parse_file/1]),
?line stop_node(Node),
?line ok = file:delete(Archive),
ok.
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index 2b6af7e1fb..9c507fd437 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -492,8 +492,6 @@ cur_dir_1(Config) when is_list(Config) ->
?line case os:type() of
{unix, _} ->
?line {error, enotsup} = ?FILE_MODULE:get_cwd("d:");
- vxworks ->
- ?line {error, enotsup} = ?FILE_MODULE:get_cwd("d:");
{win32, _} ->
win_cur_dir_1(Config)
end,
@@ -1038,32 +1036,29 @@ file_info_basic_file(Config) when is_list(Config) ->
file_info_basic_directory(suite) -> [];
file_info_basic_directory(doc) -> [];
file_info_basic_directory(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ Dog = test_server:timetrap(test_server:seconds(5)),
%% Note: filename:join/1 removes any trailing slash,
%% which is essential for ?FILE_MODULE:file_info/1 to work on
%% platforms such as Windows95.
- ?line RootDir = filename:join([?config(priv_dir, Config)]),
+ RootDir = filename:join([?config(priv_dir, Config)]),
%% Test that the RootDir directory has the expected attributes.
- ?line test_directory(RootDir, read_write),
+ test_directory(RootDir, read_write),
%% Note that on Windows file systems,
%% "/" or "c:/" are *NOT* directories.
%% Therefore, test that ?FILE_MODULE:file_info/1 behaves as if they were
%% directories.
- ?line case os:type() of
- {win32, _} ->
- ?line test_directory("/", read_write),
- ?line test_directory("c:/", read_write),
- ?line test_directory("c:\\", read_write);
- {unix, _} ->
- ?line test_directory("/", read);
- vxworks ->
- %% Check is just done for owner
- ?line test_directory("/", read_write)
- end,
- ?line test_server:timetrap_cancel(Dog).
+ case os:type() of
+ {win32, _} ->
+ ?line test_directory("/", read_write),
+ ?line test_directory("c:/", read_write),
+ ?line test_directory("c:\\", read_write);
+ {unix, _} ->
+ ?line test_directory("/", read)
+ end,
+ test_server:timetrap_cancel(Dog).
test_directory(Name, ExpectedAccess) ->
?line {ok,#file_info{size=Size,type=Type,access=Access,
@@ -1784,9 +1779,7 @@ e_delete(Config) when is_list(Config) ->
Base, #file_info {mode=8#600});
{win32, _} ->
%% Remove a character device.
- ?line {error, eacces} = ?FILE_MODULE:delete("nul");
- vxworks ->
- ok
+ ?line {error, eacces} = ?FILE_MODULE:delete("nul")
end,
?line [] = flush(),
@@ -1801,148 +1794,133 @@ e_delete(Config) when is_list(Config) ->
e_rename(suite) -> [];
e_rename(doc) -> [];
e_rename(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {comment, "Windriver: dosFs must be fixed first!"};
- _ ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line RootDir = ?config(priv_dir, Config),
- ?line Base = filename:join(RootDir,
- atom_to_list(?MODULE)++"_e_rename"),
- ?line ok = ?FILE_MODULE:make_dir(Base),
-
- %% Create an empty directory.
- ?line EmptyDir = filename:join(Base, "empty_dir"),
- ?line ok = ?FILE_MODULE:make_dir(EmptyDir),
-
- %% Create a non-empty directory.
- ?line NonEmptyDir = filename:join(Base, "non_empty_dir"),
- ?line ok = ?FILE_MODULE:make_dir(NonEmptyDir),
- ?line ok = ?FILE_MODULE:write_file(
- filename:join(NonEmptyDir, "a_file"),
- "hello\n"),
-
- %% Create another non-empty directory.
- ?line ADirectory = filename:join(Base, "a_directory"),
- ?line ok = ?FILE_MODULE:make_dir(ADirectory),
- ?line ok = ?FILE_MODULE:write_file(
- filename:join(ADirectory, "a_file"),
- "howdy\n\n"),
-
- %% Create a data file.
- ?line File = filename:join(Base, "just_a_file"),
- ?line ok = ?FILE_MODULE:write_file(File, "anything goes\n\n"),
-
- %% Move an existing directory to a non-empty directory.
- ?line {error, eexist} =
- ?FILE_MODULE:rename(ADirectory, NonEmptyDir),
-
- %% Move a root directory.
- ?line {error, einval} = ?FILE_MODULE:rename("/", "arne"),
-
- %% Move Base into Base/new_name.
- ?line {error, einval} =
- ?FILE_MODULE:rename(Base, filename:join(Base, "new_name")),
-
- %% Overwrite a directory with a file.
- ?line expect({error, eexist}, %FreeBSD (?)
- {error, eisdir},
- ?FILE_MODULE:rename(File, EmptyDir)),
- ?line expect({error, eexist}, %FreeBSD (?)
- {error, eisdir},
- ?FILE_MODULE:rename(File, NonEmptyDir)),
-
- %% Move a non-existing file.
- ?line NonExistingFile =
- filename:join(Base, "non_existing_file"),
- ?line {error, enoent} =
- ?FILE_MODULE:rename(NonExistingFile, NonEmptyDir),
-
- %% Overwrite a file with a directory.
- ?line expect({error, eexist}, %FreeBSD (?)
- {error, enotdir},
- ?FILE_MODULE:rename(ADirectory, File)),
-
- %% Move a file to another filesystem.
- %% XXX - This test case is bogus. We cannot be guaranteed that
- %% the source and destination are on
- %% different filesystems.
- %%
- %% XXX - Gross hack!
- ?line Comment =
- case os:type() of
- {unix, _} ->
- OtherFs = "/tmp",
- ?line NameOnOtherFs =
- filename:join(OtherFs, filename:basename(File)),
- ?line {ok, Com} =
- case ?FILE_MODULE:rename(File, NameOnOtherFs) of
- {error, exdev} ->
- %% The file could be in
- %% the same filesystem!
- {ok, ok};
- ok ->
- {ok, {comment,
- "Moving between filesystems "
- "suceeded, files are probably "
- "in the same filesystem!"}};
- {error, eperm} ->
- {ok, {comment, "SBS! You don't "
- "have the permission to do "
- "this test!"}};
- Else ->
- Else
- end,
- Com;
- {win32, _} ->
- %% At least Windows NT can
- %% successfully move a file to
- %% another drive.
- ok
- end,
- ?line [] = flush(),
- ?line test_server:timetrap_cancel(Dog),
- Comment
- end.
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ RootDir = ?config(priv_dir, Config),
+ Base = filename:join(RootDir,
+ atom_to_list(?MODULE)++"_e_rename"),
+ ok = ?FILE_MODULE:make_dir(Base),
+
+ %% Create an empty directory.
+ EmptyDir = filename:join(Base, "empty_dir"),
+ ok = ?FILE_MODULE:make_dir(EmptyDir),
+
+ %% Create a non-empty directory.
+ NonEmptyDir = filename:join(Base, "non_empty_dir"),
+ ok = ?FILE_MODULE:make_dir(NonEmptyDir),
+ ok = ?FILE_MODULE:write_file(
+ filename:join(NonEmptyDir, "a_file"),
+ "hello\n"),
+
+ %% Create another non-empty directory.
+ ADirectory = filename:join(Base, "a_directory"),
+ ok = ?FILE_MODULE:make_dir(ADirectory),
+ ok = ?FILE_MODULE:write_file(
+ filename:join(ADirectory, "a_file"),
+ "howdy\n\n"),
+
+ %% Create a data file.
+ File = filename:join(Base, "just_a_file"),
+ ok = ?FILE_MODULE:write_file(File, "anything goes\n\n"),
+
+ %% Move an existing directory to a non-empty directory.
+ {error, eexist} = ?FILE_MODULE:rename(ADirectory, NonEmptyDir),
+
+ %% Move a root directory.
+ {error, einval} = ?FILE_MODULE:rename("/", "arne"),
+
+ %% Move Base into Base/new_name.
+ {error, einval} =
+ ?FILE_MODULE:rename(Base, filename:join(Base, "new_name")),
+
+ %% Overwrite a directory with a file.
+ expect({error, eexist}, %FreeBSD (?)
+ {error, eisdir},
+ ?FILE_MODULE:rename(File, EmptyDir)),
+ expect({error, eexist}, %FreeBSD (?)
+ {error, eisdir},
+ ?FILE_MODULE:rename(File, NonEmptyDir)),
+
+ %% Move a non-existing file.
+ NonExistingFile = filename:join(Base, "non_existing_file"),
+ {error, enoent} = ?FILE_MODULE:rename(NonExistingFile, NonEmptyDir),
+
+ %% Overwrite a file with a directory.
+ expect({error, eexist}, %FreeBSD (?)
+ {error, enotdir},
+ ?FILE_MODULE:rename(ADirectory, File)),
+
+ %% Move a file to another filesystem.
+ %% XXX - This test case is bogus. We cannot be guaranteed that
+ %% the source and destination are on
+ %% different filesystems.
+ %%
+ %% XXX - Gross hack!
+ Comment = case os:type() of
+ {unix, _} ->
+ OtherFs = "/tmp",
+ NameOnOtherFs = filename:join(OtherFs, filename:basename(File)),
+ {ok, Com} = case ?FILE_MODULE:rename(File, NameOnOtherFs) of
+ {error, exdev} ->
+ %% The file could be in
+ %% the same filesystem!
+ {ok, ok};
+ ok ->
+ {ok, {comment,
+ "Moving between filesystems "
+ "suceeded, files are probably "
+ "in the same filesystem!"}};
+ {error, eperm} ->
+ {ok, {comment, "SBS! You don't "
+ "have the permission to do "
+ "this test!"}};
+ Else ->
+ Else
+ end,
+ Com;
+ {win32, _} ->
+ %% At least Windows NT can
+ %% successfully move a file to
+ %% another drive.
+ ok
+ end,
+ [] = flush(),
+ test_server:timetrap_cancel(Dog),
+ Comment.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
e_make_dir(suite) -> [];
e_make_dir(doc) -> [];
e_make_dir(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line RootDir = ?config(priv_dir, Config),
- ?line Base = filename:join(RootDir,
- atom_to_list(?MODULE)++"_e_make_dir"),
- ?line ok = ?FILE_MODULE:make_dir(Base),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ RootDir = ?config(priv_dir, Config),
+ Base = filename:join(RootDir,
+ atom_to_list(?MODULE)++"_e_make_dir"),
+ ok = ?FILE_MODULE:make_dir(Base),
%% A component of the path does not exist.
- ?line {error, enoent} =
- ?FILE_MODULE:make_dir(filename:join([Base, "a", "b"])),
+ {error, enoent} = ?FILE_MODULE:make_dir(filename:join([Base, "a", "b"])),
%% Use a path-name with a non-directory component.
- ?line Afile = filename:join(Base, "a_directory"),
- ?line ok = ?FILE_MODULE:write_file(Afile, "hello\n"),
- ?line case ?FILE_MODULE:make_dir(
- filename:join(Afile, "another_directory")) of
- {error, enotdir} -> io:format("Result: enotdir");
- {error, enoent} -> io:format("Result: enoent")
- end,
+ Afile = filename:join(Base, "a_directory"),
+ ok = ?FILE_MODULE:write_file(Afile, "hello\n"),
+ case ?FILE_MODULE:make_dir(
+ filename:join(Afile, "another_directory")) of
+ {error, enotdir} -> io:format("Result: enotdir");
+ {error, enoent} -> io:format("Result: enoent")
+ end,
%% No permission (on Unix only).
case os:type() of
{unix, _} ->
- ?line ?FILE_MODULE:write_file_info(Base, #file_info {mode=0}),
- ?line {error, eacces} =
- ?FILE_MODULE:make_dir(filename:join(Base, "xxxx")),
- ?line ?FILE_MODULE:write_file_info(
+ ?FILE_MODULE:write_file_info(Base, #file_info {mode=0}),
+ {error, eacces} = ?FILE_MODULE:make_dir(filename:join(Base, "xxxx")),
+ ?FILE_MODULE:write_file_info(
Base, #file_info {mode=8#600});
{win32, _} ->
- ok;
- vxworks ->
ok
end,
- ?line test_server:timetrap_cancel(Dog),
+ test_server:timetrap_cancel(Dog),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1950,57 +1928,50 @@ e_make_dir(Config) when is_list(Config) ->
e_del_dir(suite) -> [];
e_del_dir(doc) -> [];
e_del_dir(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line RootDir = ?config(priv_dir, Config),
- ?line Base = test_server:temp_name(filename:join(RootDir, "e_del_dir")),
- ?line io:format("Base: ~p", [Base]),
- ?line ok = ?FILE_MODULE:make_dir(Base),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ RootDir = ?config(priv_dir, Config),
+ Base = test_server:temp_name(filename:join(RootDir, "e_del_dir")),
+ io:format("Base: ~p", [Base]),
+ ok = ?FILE_MODULE:make_dir(Base),
%% Delete a non-existent directory.
- ?line {error, enoent} =
+ {error, enoent} =
?FILE_MODULE:del_dir(filename:join(Base, "non_existing")),
%% Use a path-name with a non-directory component.
- ?line Afile = filename:join(Base, "a_directory"),
- ?line ok = ?FILE_MODULE:write_file(Afile, "hello\n"),
- ?line {error, E1} =
- expect({error, enotdir}, {error, enoent},
- ?FILE_MODULE:del_dir(
- filename:join(Afile, "another_directory"))),
- ?line io:format("Result: ~p", [E1]),
+ Afile = filename:join(Base, "a_directory"),
+ ok = ?FILE_MODULE:write_file(Afile, "hello\n"),
+ {error, E1} = expect({error, enotdir}, {error, enoent},
+ ?FILE_MODULE:del_dir(
+ filename:join(Afile, "another_directory"))),
+ io:format("Result: ~p", [E1]),
%% Delete a non-empty directory.
- ?line {error, E2} =
- expect({error, enotempty}, {error, eexist}, {error, eacces},
+ {error, E2} = expect({error, enotempty}, {error, eexist}, {error, eacces},
?FILE_MODULE:del_dir(Base)),
- ?line io:format("Result: ~p", [E2]),
+ io:format("Result: ~p", [E2]),
%% Remove the current directory.
- ?line {error, E3} =
- expect({error, einval},
+ {error, E3} = expect({error, einval},
{error, eperm}, % Linux and DUX
{error, eacces},
{error, ebusy},
?FILE_MODULE:del_dir(".")),
- ?line io:format("Result: ~p", [E3]),
+ io:format("Result: ~p", [E3]),
%% No permission.
case os:type() of
{unix, _} ->
- ?line ADirectory = filename:join(Base, "no_perm"),
- ?line ok = ?FILE_MODULE:make_dir(ADirectory),
- ?line ?FILE_MODULE:write_file_info(
- Base, #file_info {mode=0}),
- ?line {error, eacces} = ?FILE_MODULE:del_dir(ADirectory),
- ?line ?FILE_MODULE:write_file_info(
- Base, #file_info {mode=8#600});
+ ADirectory = filename:join(Base, "no_perm"),
+ ok = ?FILE_MODULE:make_dir(ADirectory),
+ ?FILE_MODULE:write_file_info( Base, #file_info {mode=0}),
+ {error, eacces} = ?FILE_MODULE:del_dir(ADirectory),
+ ?FILE_MODULE:write_file_info( Base, #file_info {mode=8#600});
{win32, _} ->
- ok;
- vxworks ->
ok
end,
- ?line [] = flush(),
- ?line test_server:timetrap_cancel(Dog),
+ [] = flush(),
+ test_server:timetrap_cancel(Dog),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2358,6 +2329,7 @@ symlinks(doc) -> "Test operations on symbolic links (for Unix).";
symlinks(suite) -> [];
symlinks(Config) when is_list(Config) ->
?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line {error, _} = ?FILE_MODULE:read_link(lists:duplicate(10000,$a)),
?line RootDir = ?config(priv_dir, Config),
?line NewDir = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -2563,147 +2535,123 @@ delayed_write(doc) ->
["Tests the file open option {delayed_write, Size, Delay}"];
delayed_write(Config) when is_list(Config) ->
- ?line Dog = ?t:timetrap(?t:seconds(20)),
- %%
- ?line RootDir = ?config(priv_dir, Config),
- ?line File = filename:join(RootDir,
- atom_to_list(?MODULE)++"_delayed_write.txt"),
- ?line Data1 = "asdfghjkl",
- ?line Data2 = "qwertyuio",
- ?line Data3 = "zxcvbnm,.",
- ?line Size = length(Data1),
- ?line Size = length(Data2),
- ?line Size = length(Data3),
- ?line Data1Data1 = Data1++Data1,
- ?line Data1Data1Data1 = Data1Data1++Data1,
- ?line Data1Data1Data1Data1 = Data1Data1++Data1Data1,
+ Dog = ?t:timetrap(?t:seconds(20)),
+
+ RootDir = ?config(priv_dir, Config),
+ File = filename:join(RootDir,
+ atom_to_list(?MODULE)++"_delayed_write.txt"),
+ Data1 = "asdfghjkl",
+ Data2 = "qwertyuio",
+ Data3 = "zxcvbnm,.",
+ Size = length(Data1),
+ Size = length(Data2),
+ Size = length(Data3),
+ Data1Data1 = Data1++Data1,
+ Data1Data1Data1 = Data1Data1++Data1,
+ Data1Data1Data1Data1 = Data1Data1++Data1Data1,
%%
%% Test caching and normal close of non-raw file
- ?line {ok, Fd1} =
+ {ok, Fd1} =
?FILE_MODULE:open(File, [write, {delayed_write, Size+1, 2000}]),
- ?line ok = ?FILE_MODULE:write(Fd1, Data1),
- ?line ?t:sleep(1000), % Just in case the file system is slow
- ?line {ok, Fd2} = ?FILE_MODULE:open(File, [read]),
- ?line case os:type() of
- vxworks ->
- io:format("Line ~p skipped on vxworks", [?LINE]);
- _ ->
- ?line eof = ?FILE_MODULE:read(Fd2, 1)
- end,
- ?line ok = ?FILE_MODULE:write(Fd1, Data1), % Data flush on size
- ?line ?t:sleep(1000), % Just in case the file system is slow
- ?line {ok, Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 2*Size+1),
- ?line ok = ?FILE_MODULE:write(Fd1, Data1),
- ?line ?t:sleep(3000), % Wait until data flush on timeout
- ?line {ok, Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 3*Size+1),
- ?line ok = ?FILE_MODULE:write(Fd1, Data1),
- ?line ok = ?FILE_MODULE:close(Fd1), % Data flush on close
- ?line ?t:sleep(1000), % Just in case the file system is slow
- ?line {ok, Data1Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 4*Size+1),
- ?line ok = ?FILE_MODULE:close(Fd2),
+ ok = ?FILE_MODULE:write(Fd1, Data1),
+ ?t:sleep(1000), % Just in case the file system is slow
+ {ok, Fd2} = ?FILE_MODULE:open(File, [read]),
+ eof = ?FILE_MODULE:read(Fd2, 1),
+ ok = ?FILE_MODULE:write(Fd1, Data1), % Data flush on size
+ ?t:sleep(1000), % Just in case the file system is slow
+ {ok, Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 2*Size+1),
+ ok = ?FILE_MODULE:write(Fd1, Data1),
+ ?t:sleep(3000), % Wait until data flush on timeout
+ {ok, Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 3*Size+1),
+ ok = ?FILE_MODULE:write(Fd1, Data1),
+ ok = ?FILE_MODULE:close(Fd1), % Data flush on close
+ ?t:sleep(1000), % Just in case the file system is slow
+ {ok, Data1Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 4*Size+1),
+ ok = ?FILE_MODULE:close(Fd2),
%%
%% Test implicit close through exit by file owning process,
%% raw file, default parameters.
- ?line Parent = self(),
- ?line Fun =
- fun () ->
- Child = self(),
- Test =
- fun () ->
- ?line {ok, Fd} =
- ?FILE_MODULE:open(File,
- [raw, write,
- delayed_write]),
- ?line ok = ?FILE_MODULE:write(Fd, Data1),
- ?line Parent ! {Child, wrote},
- ?line receive
- {Parent, continue, Reason} ->
- {ok, Reason}
- end
- end,
- case (catch Test()) of
- {ok, Reason} ->
- exit(Reason);
- Unknown ->
- exit({Unknown, get(test_server_loc)})
- end
- end,
- ?line Child1 = spawn(Fun),
- ?line Mref1 = erlang:monitor(process, Child1),
- ?line receive
- {Child1, wrote} ->
- ok;
- {'DOWN', Mref1, _, _, _} = Down1a ->
- ?t:fail(Down1a)
- end,
- ?line ?t:sleep(1000), % Just in case the file system is slow
- ?line {ok, Fd3} = ?FILE_MODULE:open(File, [read]),
- ?line case os:type() of
- vxworks ->
- io:format("Line ~p skipped on vxworks", [?LINE]);
- _ ->
- ?line eof = ?FILE_MODULE:read(Fd3, 1)
- end,
- ?line Child1 ! {Parent, continue, normal},
- ?line receive
- {'DOWN', Mref1, process, Child1, normal} ->
- ok;
- {'DOWN', Mref1, _, _, _} = Down1b ->
- ?t:fail(Down1b)
- end,
- ?line ?t:sleep(1000), % Just in case the file system is slow
- ?line {ok, Data1} = ?FILE_MODULE:pread(Fd3, bof, Size+1),
- ?line ok = ?FILE_MODULE:close(Fd3),
+ Parent = self(),
+ Fun = fun() ->
+ Child = self(),
+ Test =
+ fun () ->
+ {ok, Fd} = ?FILE_MODULE:open(File,
+ [raw, write, delayed_write]),
+ ok = ?FILE_MODULE:write(Fd, Data1),
+ Parent ! {Child, wrote},
+ receive
+ {Parent, continue, Reason} ->
+ {ok, Reason}
+ end
+ end,
+ case (catch Test()) of
+ {ok, Reason} -> exit(Reason);
+ Unknown ->
+ exit({Unknown, get(test_server_loc)})
+ end
+ end,
+ Child1 = spawn(Fun),
+ Mref1 = erlang:monitor(process, Child1),
+ receive
+ {Child1, wrote} ->
+ ok;
+ {'DOWN', Mref1, _, _, _} = Down1a ->
+ ?t:fail(Down1a)
+ end,
+ ?t:sleep(1000), % Just in case the file system is slow
+ {ok, Fd3} = ?FILE_MODULE:open(File, [read]),
+ eof = ?FILE_MODULE:read(Fd3, 1),
+ Child1 ! {Parent, continue, normal},
+ receive
+ {'DOWN', Mref1, process, Child1, normal} ->
+ ok;
+ {'DOWN', Mref1, _, _, _} = Down1b ->
+ ?t:fail(Down1b)
+ end,
+ ?t:sleep(1000), % Just in case the file system is slow
+ {ok, Data1} = ?FILE_MODULE:pread(Fd3, bof, Size+1),
+ ok = ?FILE_MODULE:close(Fd3),
%%
%% The same again, but this time with reason 'kill'.
- ?line Child2 = spawn(Fun),
- ?line Mref2 = erlang:monitor(process, Child2),
- ?line receive
- {Child2, wrote} ->
- ok;
- {'DOWN', Mref2, _, _, _} = Down2a ->
- ?t:fail(Down2a)
- end,
- ?line ?t:sleep(1000), % Just in case the file system is slow
- ?line {ok, Fd4} = ?FILE_MODULE:open(File, [read]),
- ?line case os:type() of
- vxworks ->
- io:format("Line ~p skipped on vxworks", [?LINE]);
- _ ->
- ?line eof = ?FILE_MODULE:read(Fd4, 1)
- end,
- ?line Child2 ! {Parent, continue, kill},
- ?line receive
- {'DOWN', Mref2, process, Child2, kill} ->
- ok;
- {'DOWN', Mref2, _, _, _} = Down2b ->
- ?t:fail(Down2b)
- end,
- ?line ?t:sleep(1000), % Just in case the file system is slow
- ?line eof = ?FILE_MODULE:pread(Fd4, bof, 1),
- ?line ok = ?FILE_MODULE:close(Fd4),
+ Child2 = spawn(Fun),
+ Mref2 = erlang:monitor(process, Child2),
+ receive
+ {Child2, wrote} ->
+ ok;
+ {'DOWN', Mref2, _, _, _} = Down2a ->
+ ?t:fail(Down2a)
+ end,
+ ?t:sleep(1000), % Just in case the file system is slow
+ {ok, Fd4} = ?FILE_MODULE:open(File, [read]),
+ eof = ?FILE_MODULE:read(Fd4, 1),
+ Child2 ! {Parent, continue, kill},
+ receive
+ {'DOWN', Mref2, process, Child2, kill} ->
+ ok;
+ {'DOWN', Mref2, _, _, _} = Down2b ->
+ ?t:fail(Down2b)
+ end,
+ ?t:sleep(1000), % Just in case the file system is slow
+ eof = ?FILE_MODULE:pread(Fd4, bof, 1),
+ ok = ?FILE_MODULE:close(Fd4),
%%
%% Test if file position works with delayed_write
- ?line {ok, Fd5} = ?FILE_MODULE:open(File, [raw, read, write,
- delayed_write]),
- ?line ok = ?FILE_MODULE:truncate(Fd5),
- ?line ok = ?FILE_MODULE:write(Fd5, [Data1|Data2]),
- ?line {ok, 0} = ?FILE_MODULE:position(Fd5, bof),
- ?line ok = ?FILE_MODULE:write(Fd5, [Data3]),
- ?line {ok, Data2} = ?FILE_MODULE:read(Fd5, Size+1),
- ?line {ok, 0} = ?FILE_MODULE:position(Fd5, bof),
- ?line Data3Data2 = Data3++Data2,
- ?line {ok, Data3Data2} = ?FILE_MODULE:read(Fd5, 2*Size+1),
- ?line ok = ?FILE_MODULE:close(Fd5),
+ {ok, Fd5} = ?FILE_MODULE:open(File, [raw, read, write,
+ delayed_write]),
+ ok = ?FILE_MODULE:truncate(Fd5),
+ ok = ?FILE_MODULE:write(Fd5, [Data1|Data2]),
+ {ok, 0} = ?FILE_MODULE:position(Fd5, bof),
+ ok = ?FILE_MODULE:write(Fd5, [Data3]),
+ {ok, Data2} = ?FILE_MODULE:read(Fd5, Size+1),
+ {ok, 0} = ?FILE_MODULE:position(Fd5, bof),
+ Data3Data2 = Data3++Data2,
+ {ok, Data3Data2} = ?FILE_MODULE:read(Fd5, 2*Size+1),
+ ok = ?FILE_MODULE:close(Fd5),
%%
- ?line [] = flush(),
- ?line ?t:timetrap_cancel(Dog),
- ?line case os:type() of
- vxworks ->
- {comment, "Some lines skipped on vxworks"};
- _ ->
- ok
- end.
+ [] = flush(),
+ ?t:timetrap_cancel(Dog),
+ ok.
pid2name(doc) -> "Tests file:pid2name/1.";
diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl
index 53bcb1162d..3aa010a708 100644
--- a/lib/kernel/test/file_name_SUITE.erl
+++ b/lib/kernel/test/file_name_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -74,7 +74,7 @@
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([normal/1,icky/1,very_icky/1,normalize/1]).
+-export([normal/1,icky/1,very_icky/1,normalize/1,home_dir/1]).
init_per_testcase(_Func, Config) ->
@@ -88,7 +88,7 @@ end_per_testcase(_Func, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [normal, icky, very_icky, normalize].
+ [normal, icky, very_icky, normalize, home_dir].
groups() ->
[].
@@ -105,6 +105,54 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+home_dir(suite) ->
+ [];
+home_dir(doc) ->
+ ["Check that Erlang can be started with unicode named home directory"];
+home_dir(Config) when is_list(Config) ->
+ try
+ Name=[960,945,964,961,953,954],
+ Priv = ?config(priv_dir, Config),
+ UniMode = file:native_name_encoding() =/= latin1,
+ if
+ not UniMode ->
+ throw(need_unicode_mode);
+ true ->
+ ok
+ end,
+ NewHome=filename:join(Priv,Name),
+ file:make_dir(NewHome),
+ {SaveOldName,SaveOldValue} = case os:type() of
+ {win32,nt} ->
+ HomePath=re:replace(filename:nativename(NewHome),"^[a-zA-Z]:","",[{return,list},unicode]),
+ Save = os:getenv("HOMEPATH"),
+ os:putenv("HOMEPATH",HomePath),
+ {"HOMEPATH",Save};
+ {unix,_} ->
+ Save = os:getenv("HOME"),
+ os:putenv("HOME",NewHome),
+ {"HOME",Save};
+ _ ->
+ rm_rf(prim_file,NewHome),
+ throw(unsupported_os)
+ end,
+ try
+ {ok,Node} = test_server:start_node(test_unicode_homedir,slave,[{args,"-setcookie "++atom_to_list(erlang:get_cookie())}]),
+ test_server:stop_node(Node),
+ ok
+ after
+ os:putenv(SaveOldName,SaveOldValue),
+ rm_rf(prim_file,NewHome)
+ end
+ catch
+ throw:need_unicode_mode ->
+ io:format("Sorry, can only run in unicode mode.~n"),
+ {skipped,"VM needs to be started in Unicode filename mode"};
+ throw:unsupported_os ->
+ io:format("Sorry, can only run on Unix/Windows.~n"),
+ {skipped,"Runs only on Unix/Windows"}
+ end.
+
normalize(suite) ->
[];
normalize(doc) ->
diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index 8f490b6643..bcc2f0b840 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -31,14 +31,22 @@
[basic/1,
api_open_close/1,api_listen/1,api_connect_init/1,api_opts/1,
xfer_min/1,xfer_active/1,def_sndrcvinfo/1,implicit_inet6/1,
- basic_stream/1, xfer_stream_min/1, peeloff/1, buffers/1]).
+ basic_stream/1, xfer_stream_min/1, peeloff/1, buffers/1,
+ open_multihoming_ipv4_socket/1,
+ open_unihoming_ipv6_socket/1,
+ open_multihoming_ipv6_socket/1,
+ open_multihoming_ipv4_and_ipv6_socket/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[basic, api_open_close, api_listen, api_connect_init,
api_opts, xfer_min, xfer_active, def_sndrcvinfo, implicit_inet6,
- basic_stream, xfer_stream_min, peeloff, buffers].
+ basic_stream, xfer_stream_min, peeloff, buffers,
+ open_multihoming_ipv4_socket,
+ open_unihoming_ipv6_socket,
+ open_multihoming_ipv6_socket,
+ open_multihoming_ipv4_and_ipv6_socket].
groups() ->
[].
@@ -1105,6 +1113,192 @@ mk_data(N, Bytes, Bin) when N < Bytes ->
mk_data(_, _, Bin) ->
Bin.
+
+
+open_multihoming_ipv4_socket(doc) ->
+ "Test opening a multihoming ipv4 socket";
+open_multihoming_ipv4_socket(suite) ->
+ [];
+open_multihoming_ipv4_socket(Config) when is_list(Config) ->
+ ?line case get_addrs_by_family(inet, 2) of
+ {ok, [Addr1, Addr2]} ->
+ ?line do_open_and_connect([Addr1, Addr2], Addr1);
+ {error, Reason} ->
+ {skip, Reason}
+ end.
+
+open_unihoming_ipv6_socket(doc) ->
+ %% This test is mostly aimed to indicate
+ %% whether host has a non-working ipv6 setup
+ "Test opening a unihoming (non-multihoming) ipv6 socket";
+open_unihoming_ipv6_socket(suite) ->
+ [];
+open_unihoming_ipv6_socket(Config) when is_list(Config) ->
+ ?line case get_addrs_by_family(inet6, 1) of
+ {ok, [Addr]} ->
+ ?line do_open_and_connect([Addr], Addr);
+ {error, Reason} ->
+ {skip, Reason}
+ end.
+
+
+open_multihoming_ipv6_socket(doc) ->
+ "Test opening a multihoming ipv6 socket";
+open_multihoming_ipv6_socket(suite) ->
+ [];
+open_multihoming_ipv6_socket(Config) when is_list(Config) ->
+ ?line case get_addrs_by_family(inet6, 2) of
+ {ok, [Addr1, Addr2]} ->
+ ?line do_open_and_connect([Addr1, Addr2], Addr1);
+ {error, Reason} ->
+ {skip, Reason}
+ end.
+
+open_multihoming_ipv4_and_ipv6_socket(doc) ->
+ "Test opening a multihoming ipv6 socket with ipv4 and ipv6 addresses";
+open_multihoming_ipv4_and_ipv6_socket(suite) ->
+ [];
+open_multihoming_ipv4_and_ipv6_socket(Config) when is_list(Config) ->
+ ?line case get_addrs_by_family(inet_and_inet6, 2) of
+ {ok, [[InetAddr1, InetAddr2], [Inet6Addr1, Inet6Addr2]]} ->
+ %% Connect to the first address to test bind
+ ?line do_open_and_connect([InetAddr1, Inet6Addr1, InetAddr2],
+ InetAddr1),
+ ?line do_open_and_connect([Inet6Addr1, InetAddr1],
+ Inet6Addr1),
+
+ %% Connect an address, not the first,
+ %% to test sctp_bindx
+ ?line do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1],
+ Inet6Addr2),
+ ?line do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1],
+ InetAddr1);
+ {error, Reason} ->
+ {skip, Reason}
+ end.
+
+
+get_addrs_by_family(Family, NumAddrs) ->
+ case os:type() of
+ {unix,linux} ->
+ get_addrs_by_family_aux(Family, NumAddrs);
+ {unix,freebsd} ->
+ get_addrs_by_family_aux(Family, NumAddrs);
+ {unix,sunos} ->
+ case get_addrs_by_family_aux(Family, NumAddrs) of
+ {ok, [InetAddrs, Inet6Addrs]} when Family =:= inet_and_inet6 ->
+ %% Man page for sctp_bindx on Solaris says: "If sock is an
+ %% Internet Protocol Version 6 (IPv6) socket, addrs should
+ %% be an array of sockaddr_in6 structures containing IPv6
+ %% or IPv4-mapped IPv6 addresses."
+ {ok, [ipv4_map_addrs(InetAddrs), Inet6Addrs]};
+ {ok, Addrs} ->
+ {ok, Addrs};
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ Os ->
+ Reason = if Family =:= inet_and_inet6 ->
+ f("Mixing ipv4 and ipv6 addresses for multihoming "
+ " has not been verified on ~p", [Os]);
+ true ->
+ f("Multihoming for ~p has not been verified on ~p",
+ [Family, Os])
+ end,
+ {error, Reason}
+ end.
+
+get_addrs_by_family_aux(Family, NumAddrs) when Family =:= inet;
+ Family =:= inet6 ->
+ ?line
+ case inet:getaddr(localhost, Family) of
+ {error,eafnosupport} ->
+ {skip, f("No support for ~p", Family)};
+ {ok, _} ->
+ ?line IfAddrs = ok(inet:getifaddrs()),
+ ?line case filter_addrs_by_family(IfAddrs, Family) of
+ Addrs when length(Addrs) >= NumAddrs ->
+ {ok, lists:sublist(Addrs, NumAddrs)};
+ [] ->
+ {error, f("Need ~p ~p address(es) found none~n",
+ [NumAddrs, Family])};
+ Addrs ->
+ {error,
+ f("Need ~p ~p address(es) found only ~p: ~p~n",
+ [NumAddrs, Family, length(Addrs), Addrs])}
+ end
+ end;
+get_addrs_by_family_aux(inet_and_inet6, NumAddrs) ->
+ ?line catch {ok, [case get_addrs_by_family_aux(Family, NumAddrs) of
+ {ok, Addrs} -> Addrs;
+ {error, Reason} -> throw({error, Reason})
+ end || Family <- [inet, inet6]]}.
+
+filter_addrs_by_family(IfAddrs, Family) ->
+ lists:flatten([[Addr || {addr, Addr} <- Info,
+ is_good_addr(Addr, Family)]
+ || {_IfName, Info} <- IfAddrs]).
+
+is_good_addr(Addr, inet) when tuple_size(Addr) =:= 4 ->
+ true;
+is_good_addr({0,0,0,0,0,16#ffff,_,_}, inet6) ->
+ false; %% ipv4 mapped
+is_good_addr({16#fe80,_,_,_,_,_,_,_}, inet6) ->
+ false; %% link-local
+is_good_addr(Addr, inet6) when tuple_size(Addr) =:= 8 ->
+ true;
+is_good_addr(_Addr, _Family) ->
+ false.
+
+ipv4_map_addrs(InetAddrs) ->
+ [begin
+ <<AB:16>> = <<A,B>>,
+ <<CD:16>> = <<C,D>>,
+ {0, 0, 0, 0, 0, 16#ffff, AB, CD}
+ end || {A,B,C,D} <- InetAddrs].
+
+f(F, A) ->
+ lists:flatten(io_lib:format(F, A)).
+
+do_open_and_connect(ServerAddresses, AddressToConnectTo) ->
+ ?line ServerFamily = get_family_by_addrs(ServerAddresses),
+ ?line io:format("Serving ~p addresses: ~p~n",
+ [ServerFamily, ServerAddresses]),
+ ?line S1 = ok(gen_sctp:open(0, [{ip,Addr} || Addr <- ServerAddresses] ++
+ [ServerFamily])),
+ ?line ok = gen_sctp:listen(S1, true),
+ ?line P1 = ok(inet:port(S1)),
+ ?line ClientFamily = get_family_by_addr(AddressToConnectTo),
+ ?line io:format("Connecting to ~p ~p~n",
+ [ClientFamily, AddressToConnectTo]),
+ ?line S2 = ok(gen_sctp:open(0, [ClientFamily])),
+ %% Verify client can connect
+ ?line #sctp_assoc_change{state=comm_up} =
+ ok(gen_sctp:connect(S2, AddressToConnectTo, P1, [])),
+ %% verify server side also receives comm_up from client
+ ?line recv_comm_up_eventually(S1),
+ ?line ok = gen_sctp:close(S2),
+ ?line ok = gen_sctp:close(S1).
+
+%% If at least one of the addresses is an ipv6 address, return inet6, else inet.
+get_family_by_addrs(Addresses) ->
+ ?line case lists:usort([get_family_by_addr(Addr) || Addr <- Addresses]) of
+ [inet, inet6] -> inet6;
+ [inet] -> inet;
+ [inet6] -> inet6
+ end.
+
+get_family_by_addr(Addr) when tuple_size(Addr) =:= 4 -> inet;
+get_family_by_addr(Addr) when tuple_size(Addr) =:= 8 -> inet6.
+
+recv_comm_up_eventually(S) ->
+ ?line case ok(gen_sctp:recv(S)) of
+ {_Addr, _Port, _Info, #sctp_assoc_change{state=comm_up}} ->
+ ok;
+ {_Addr, _Port, _Info, _OtherSctpMsg} ->
+ ?line recv_comm_up_eventually(S)
+ end.
+
%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% socket gen_server ultra light
diff --git a/lib/kernel/test/gen_tcp_echo_SUITE.erl b/lib/kernel/test/gen_tcp_echo_SUITE.erl
index 5bbaeb02ad..94f95798a0 100644
--- a/lib/kernel/test/gen_tcp_echo_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_echo_SUITE.erl
@@ -190,24 +190,19 @@ echo_test_1(SockOpts, EchoFun, Config0) ->
ok.
echo_packet(SockOpts, EchoFun, Opts) ->
- ?line Type =
- case lists:keysearch(type, 1, Opts) of
- {value, {type, T}} ->
- T;
- _ ->
- {value, {packet, T}} = lists:keysearch(packet, 1, SockOpts),
- T
- end,
+ Type = case lists:keysearch(type, 1, Opts) of
+ {value, {type, T}} ->
+ T;
+ _ ->
+ {value, {packet, T}} = lists:keysearch(packet, 1, SockOpts),
+ T
+ end,
%% Connect to the echo server.
- ?line EchoPort = ?config(echo_port, Opts),
- ?line {ok, Echo} = gen_tcp:connect(localhost, EchoPort, SockOpts),
+ EchoPort = ?config(echo_port, Opts),
+ {ok, Echo} = gen_tcp:connect(localhost, EchoPort, SockOpts),
- ?line SlowEcho =
- case os:type() of
- vxworks -> true;
- _ -> lists:member(slow_echo, Opts)
- end,
+ SlowEcho = lists:member(slow_echo, Opts),
case Type of
http ->
diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index 1592399996..5d45b91ee5 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -340,39 +340,23 @@ no_accept(doc) ->
"a tcp_closed message."];
no_accept(suite) -> [];
no_accept(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skip,"Too tough for vxworks"};
- _ ->
- no_accept2()
+ {ok, L} = gen_tcp:listen(0, []),
+ {ok, {_, Port}} = inet:sockname(L),
+ {ok, Client} = gen_tcp:connect(localhost, Port, []),
+ ok = gen_tcp:close(L),
+ receive
+ {tcp_closed, Client} ->
+ ok
+ after 5000 ->
+ ?line test_server:fail(never_closed)
+
end.
-no_accept2() ->
- ?line {ok, L} = gen_tcp:listen(0, []),
- ?line {ok, {_, Port}} = inet:sockname(L),
- ?line {ok, Client} = gen_tcp:connect(localhost, Port, []),
- ?line ok = gen_tcp:close(L),
- ?line receive
- {tcp_closed, Client} ->
- ok
- after 5000 ->
- ?line test_server:fail(never_closed)
-
- end.
-
close_with_pending_output(doc) ->
["Send several packets to a socket and close it. All packets should arrive ",
"to the other end."];
close_with_pending_output(suite) -> [];
close_with_pending_output(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skipped,"Too tough for vxworks"};
- _ ->
- close_with_pending_output2()
- end.
-
-close_with_pending_output2() ->
?line {ok, L} = gen_tcp:listen(0, [binary, {active, false}]),
?line {ok, {_, Port}} = inet:sockname(L),
?line Packets = 16,
@@ -423,22 +407,16 @@ otp_3924(doc) ->
otp_3924(suite) -> [];
otp_3924(Config) when is_list(Config) ->
MaxDelay = (case has_superfluous_schedulers() of
- true -> 4;
- false -> 1
- end
- * case {erlang:system_info(debug_compiled),
- erlang:system_info(lock_checking)} of
- {true, _} -> 6;
- {_, true} -> 2;
- _ -> 1
- end * ?OTP_3924_MAX_DELAY),
- case os:type() of
- vxworks ->
-%% {skip,"Too tough for vxworks"};
- otp_3924_1(MaxDelay);
- _ ->
- otp_3924_1(MaxDelay)
- end.
+ true -> 4;
+ false -> 1
+ end
+ * case {erlang:system_info(debug_compiled),
+ erlang:system_info(lock_checking)} of
+ {true, _} -> 6;
+ {_, true} -> 2;
+ _ -> 1
+ end * ?OTP_3924_MAX_DELAY),
+ otp_3924_1(MaxDelay).
otp_3924_1(MaxDelay) ->
Dog = test_server:timetrap(test_server:seconds(240)),
@@ -559,26 +537,18 @@ otp_3924_sender(Receiver, Host, Port, Data) ->
data_before_close(doc) ->
["Tests that a huge amount of data can be received before a close."];
data_before_close(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skip,"Too tough for vxworks"};
- _ ->
- data_before_close2()
- end.
-
-data_before_close2() ->
- ?line {ok, L} = gen_tcp:listen(0, [binary]),
- ?line {ok, {_, TcpPort}} = inet:sockname(L),
- ?line Bytes = 256*1024,
- ?line spawn_link(fun() -> huge_sender(TcpPort, Bytes) end),
- ?line {ok, A} = gen_tcp:accept(L),
- ?line case count_bytes_recv(A, 0) of
- {Bytes, Result} ->
- io:format("Result: ~p", [Result]);
- {Wrong, Result} ->
- io:format("Result: ~p", [Result]),
- test_server:fail({wrong_count, Wrong})
- end,
+ {ok, L} = gen_tcp:listen(0, [binary]),
+ {ok, {_, TcpPort}} = inet:sockname(L),
+ Bytes = 256*1024,
+ spawn_link(fun() -> huge_sender(TcpPort, Bytes) end),
+ {ok, A} = gen_tcp:accept(L),
+ case count_bytes_recv(A, 0) of
+ {Bytes, Result} ->
+ io:format("Result: ~p", [Result]);
+ {Wrong, Result} ->
+ io:format("Result: ~p", [Result]),
+ test_server:fail({wrong_count, Wrong})
+ end,
ok.
count_bytes_recv(Sock, Total) ->
@@ -611,32 +581,18 @@ get_status(Config) when is_list(Config) ->
?line {ok,{socket,Pid,_,_}} = gen_tcp:listen(5678,[]),
?line {status,Pid,_,_} = sys:get_status(Pid).
+-define(RECOVER_SLEEP, 60000).
+-define(RETRY_SLEEP, 15000).
+
iter_max_socks(doc) ->
["Open as many sockets as possible. Do this several times and check ",
"that we get the same number of sockets every time."];
iter_max_socks(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skip,"Too tough for vxworks"};
- _ ->
- iter_max_socks2()
- end.
-
--define(RECOVER_SLEEP, 60000).
--define(RETRY_SLEEP, 15000).
-
-iter_max_socks2() ->
- ?line N =
- case os:type() of
- vxworks ->
- 10;
- _ ->
- 20
- end,
+ N = 20,
L = do_iter_max_socks(N, initalize),
- ?line io:format("Result: ~p",[L]),
- ?line all_equal(L),
- ?line {comment, "Max sockets: " ++ integer_to_list(hd(L))}.
+ io:format("Result: ~p",[L]),
+ all_equal(L),
+ {comment, "Max sockets: " ++ integer_to_list(hd(L))}.
do_iter_max_socks(0, _) ->
[];
diff --git a/lib/kernel/test/heart_SUITE.erl b/lib/kernel/test/heart_SUITE.erl
index 233e438dc9..31005a01e2 100644
--- a/lib/kernel/test/heart_SUITE.erl
+++ b/lib/kernel/test/heart_SUITE.erl
@@ -266,13 +266,8 @@ dont_drop(doc) ->
"set just before halt on very high I/O load."];
dont_drop(Config) when is_list(Config) ->
%%% Have to do it some times to make it happen...
- case os:type() of
- vxworks ->
- {comment, "No use to run with slaves on other nodes..."};
- _ ->
- [ok,ok,ok,ok,ok,ok,ok,ok,ok,ok] = do_dont_drop(Config,10),
- ok
- end.
+ [ok,ok,ok,ok,ok,ok,ok,ok,ok,ok] = do_dont_drop(Config,10),
+ ok.
do_dont_drop(_,0) ->
[];
@@ -333,13 +328,7 @@ kill_pid(doc) ->
["Tests that heart kills the old erlang node before executing ",
"heart command."];
kill_pid(Config) when is_list(Config) ->
- %%% Have to do it some times to make it happen...
- case os:type() of
- vxworks ->
- {comment, "No use to run with slaves on other nodes..."};
- _ ->
- ok = do_kill_pid(Config)
- end.
+ ok = do_kill_pid(Config).
do_kill_pid(_Config) ->
Name = heart_test,
diff --git a/lib/kernel/test/heart_SUITE_data/simple_echo.c b/lib/kernel/test/heart_SUITE_data/simple_echo.c
index 0093dbce9b..a92bb8af95 100644
--- a/lib/kernel/test/heart_SUITE_data/simple_echo.c
+++ b/lib/kernel/test/heart_SUITE_data/simple_echo.c
@@ -2,11 +2,7 @@
#include <stdlib.h>
#include <string.h>
-#ifdef VXWORKS
-int simple_echo(void){
-#else
int main(void){
-#endif
int x;
while((x = getchar()) != EOF){
putchar(x);
@@ -14,4 +10,3 @@ int main(void){
}
return 0;
}
-
diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl
index 7241b093d0..ce138b6804 100644
--- a/lib/kernel/test/inet_SUITE.erl
+++ b/lib/kernel/test/inet_SUITE.erl
@@ -66,7 +66,7 @@ required(v6) ->
{require, test_dummy_ipv6_host}];
required(hosts) ->
case os:type() of
- {OS, _} when OS =:= win32; OS =:= vxworks ->
+ {OS, _} when OS =:= win32 ->
[{require, hardcoded_hosts},
{require, hardcoded_ipv6_hosts}];
_Else ->
@@ -614,17 +614,12 @@ t_gethostnative(Config) when is_list(Config) ->
%% this will result in 26 bytes sent which causes problem in Windows
%% if the port-program has not assured stdin to be read in BINARY mode
%% OTP-2555
- case os:type() of
- vxworks ->
- {skipped, "VxWorks has no native gethostbyname()"};
- _ ->
- ?line case inet_gethost_native:gethostbyname(
- "a23456789012345678901234") of
- {error,notfound} ->
- ?line ok;
- {error,no_data} ->
- ?line ok
- end
+ ?line case inet_gethost_native:gethostbyname(
+ "a23456789012345678901234") of
+ {error,notfound} ->
+ ?line ok;
+ {error,no_data} ->
+ ?line ok
end.
gethostnative_parallell(suite) ->
diff --git a/lib/kernel/test/inet_sockopt_SUITE.erl b/lib/kernel/test/inet_sockopt_SUITE.erl
index 0c63a6d653..087ae6055b 100644
--- a/lib/kernel/test/inet_sockopt_SUITE.erl
+++ b/lib/kernel/test/inet_sockopt_SUITE.erl
@@ -623,7 +623,6 @@ all_listen_options() ->
{exit_on_close, true, false, true, true},
%{high_watermark,4096,8192,true,true},
%{low_watermark,2048,4096,true,true},
- {bit8,on,off,true,true},
{send_timeout,infinity,1000,true,true},
{send_timeout_close,false,true,true,true},
{delay_send,false,true,true,true},
@@ -647,7 +646,6 @@ all_connect_options() ->
{exit_on_close, true, false, true, true},
{high_watermark,4096,8192,false,true},
{low_watermark,2048,4096,false,true},
- {bit8,on,off,true,true},
{send_timeout,infinity,1000,true,true},
{send_timeout_close,false,true,true,true},
{delay_send,false,true,true,true},
diff --git a/lib/kernel/test/inet_sockopt_SUITE_data/sockopt_helper.c b/lib/kernel/test/inet_sockopt_SUITE_data/sockopt_helper.c
index f24c93edf5..9c8f8eb91a 100644
--- a/lib/kernel/test/inet_sockopt_SUITE_data/sockopt_helper.c
+++ b/lib/kernel/test/inet_sockopt_SUITE_data/sockopt_helper.c
@@ -1,12 +1,3 @@
-#if defined(VXWORKS)
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-int sockopt_helper(void){
- return 0;
-}
-#else
-
#if defined(__WIN32__)
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
@@ -215,5 +206,3 @@ int main(void){
} while (x != C_QUIT);
return 0;
}
-#endif
-
diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl
index e3fa4642b7..6fe97ed04f 100644
--- a/lib/kernel/test/init_SUITE.erl
+++ b/lib/kernel/test/init_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -256,47 +256,42 @@ get_plain_arguments(Config) when is_list(Config) ->
boot_var(doc) -> [];
boot_var(suite) -> {req, [distribution, {local_slave_nodes, 1}]};
boot_var(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {comment, "Not run on VxWorks"};
+ ?line Dog = ?t:timetrap(?t:seconds(100)),
+
+ {BootScript, TEST_VAR, KernelVsn, StdlibVsn} = create_boot(Config),
+
+ %% Should fail as we have not given -boot_var TEST_VAR
+ ?line {error, timeout} =
+ start_node(init_test, "-boot " ++ BootScript),
+
+ case is_real_system(KernelVsn, StdlibVsn) of
+ true ->
+ %% Now it should work !!
+ ?line {ok, Node} =
+ start_node(init_test,
+ "-boot " ++ BootScript ++
+ " -boot_var TEST_VAR " ++ TEST_VAR),
+ stop_node(Node),
+ Res = ok;
_ ->
- ?line Dog = ?t:timetrap(?t:seconds(100)),
-
- {BootScript, TEST_VAR, KernelVsn, StdlibVsn} = create_boot(Config),
-
- %% Should fail as we have not given -boot_var TEST_VAR
- ?line {error, timeout} =
- start_node(init_test, "-boot " ++ BootScript),
-
- case is_real_system(KernelVsn, StdlibVsn) of
- true ->
- %% Now it should work !!
- ?line {ok, Node} =
- start_node(init_test,
- "-boot " ++ BootScript ++
- " -boot_var TEST_VAR " ++ TEST_VAR),
- stop_node(Node),
- Res = ok;
- _ ->
-%% What we need is not so much version numbers on the directories, but
-%% for the boot var TEST_VAR to appear in the boot script, and it doesn't
-%% if we give the 'local' option to systools:make_script.
- ?t:format(
- "Test case not complete as we are not~n"
- "running in a real system!~n"
- "Probably this test is performed in a "
- "clearcase view or source tree.~n"
- "Need version numbers on the kernel and "
- "stdlib directories!~n",
- []),
- Res = {skip,
- "Test case only partially run since it is run "
- "in a clearcase view or in a source tree. "
- "Need an installed system to complete this test."}
- end,
- ?line ?t:timetrap_cancel(Dog),
- Res
- end.
+ %% What we need is not so much version numbers on the directories, but
+ %% for the boot var TEST_VAR to appear in the boot script, and it doesn't
+ %% if we give the 'local' option to systools:make_script.
+ ?t:format(
+ "Test case not complete as we are not~n"
+ "running in a real system!~n"
+ "Probably this test is performed in a "
+ "clearcase view or source tree.~n"
+ "Need version numbers on the kernel and "
+ "stdlib directories!~n",
+ []),
+ Res = {skip,
+ "Test case only partially run since it is run "
+ "in a clearcase view or in a source tree. "
+ "Need an installed system to complete this test."}
+ end,
+ ?line ?t:timetrap_cancel(Dog),
+ Res.
create_boot(Config) ->
?line {ok, OldDir} = file:get_cwd(),
@@ -579,55 +574,47 @@ script_id(Config) when is_list(Config) ->
boot1(doc) -> [];
boot1(suite) -> {req, [distribution, {local_slave_nodes, 1}, {time, 35}]};
boot1(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {comment, "Not run on VxWorks"};
- _ ->
- ?line Dog = ?t:timetrap(?t:seconds(80)),
- Args = args() ++ " -boot start_sasl",
- ?line {ok, Node} = start_node(init_test, Args),
- ?line stop_node(Node),
-
- %% Try to start with non existing boot file.
- Args1 = args() ++ " -boot dummy_script",
- ?line {error, timeout} = start_node(init_test, Args1),
-
- ?line ?t:timetrap_cancel(Dog),
- ok
- end.
+ ?line Dog = ?t:timetrap(?t:seconds(80)),
+ Args = args() ++ " -boot start_sasl",
+ ?line {ok, Node} = start_node(init_test, Args),
+ ?line stop_node(Node),
+
+ %% Try to start with non existing boot file.
+ Args1 = args() ++ " -boot dummy_script",
+ ?line {error, timeout} = start_node(init_test, Args1),
+
+ ?line ?t:timetrap_cancel(Dog),
+ ok.
boot2(doc) -> [];
boot2(suite) -> {req, [distribution, {local_slave_nodes, 1}, {time, 35}]};
boot2(Config) when is_list(Config) ->
+ Dog = ?t:timetrap(?t:seconds(80)),
+
+ %% Absolute boot file name
+ Boot = filename:join([code:root_dir(), "bin", "start_sasl"]),
+
+ Args = args() ++ " -boot \"" ++ Boot++"\"",
+ {ok, Node} = start_node(init_test, Args),
+ stop_node(Node),
+
case os:type() of
- vxworks ->
- {comment, "Not run on VxWorks"};
+ {win32, _} ->
+ %% Absolute boot file name for Windows -- all slashes are
+ %% converted to backslashes.
+ Win_boot = lists:map(fun
+ ($/) -> $\\;
+ (C) -> C
+ end, Boot),
+ Args2 = args() ++ " -boot \"" ++ Win_boot ++ "\"",
+ {ok, Node2} = start_node(init_test, Args2),
+ stop_node(Node2);
_ ->
- ?line Dog = ?t:timetrap(?t:seconds(80)),
-
- %% Absolute boot file name
- Boot = filename:join([code:root_dir(), "bin", "start_sasl"]),
-
- Args = args() ++ " -boot \"" ++ Boot++"\"",
- ?line {ok, Node} = start_node(init_test, Args),
- ?line stop_node(Node),
-
- case os:type() of
- {win32, _} ->
- %% Absolute boot file name for Windows -- all slashes are
- %% converted to backslashes.
- Win_boot = lists:map(fun($/) -> $\\; (C) -> C end,
- Boot),
- Args2 = args() ++ " -boot \"" ++ Win_boot ++ "\"",
- ?line {ok, Node2} = start_node(init_test, Args2),
- ?line stop_node(Node2);
- _ ->
- ok
- end,
-
- ?line ?t:timetrap_cancel(Dog),
ok
- end.
+ end,
+
+ ?t:timetrap_cancel(Dog),
+ ok.
%% Misc. functions
diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl
index 96e45cc23f..4787f19250 100644
--- a/lib/kernel/test/interactive_shell_SUITE.erl
+++ b/lib/kernel/test/interactive_shell_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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/lib/kernel/test/kernel.spec.wxworks b/lib/kernel/test/kernel.spec.wxworks
deleted file mode 100644
index 370e474e64..0000000000
--- a/lib/kernel/test/kernel.spec.wxworks
+++ /dev/null
@@ -1,63 +0,0 @@
-%% -*- erlang -*-
-{suites,"kernel_test",all}.
-{skip_cases,"kernel_test",bif_SUITE,[spawn_link_race1],"Known bug."}.
-{skip_cases,"kernel_test",file_SUITE,
- [read_write_file],
- "VxWorks filesystem can't handle this"}.
-{skip_cases,"kernel_test",file_SUITE,
- [cur_dir_0],
- "VxWorks filesystem can't handle this"}.
-{skip_cases,"kernel_test",file_SUITE,
- [open1],
- "VxWorks filesystem can't handle this"}.
-{skip_cases,"kernel_test",file_SUITE,
- [file_info_times],
- "VxWorks filesystem can't handle this"}.
-{skip_cases,"kernel_test",file_SUITE,
- [file_write_file_info],
- "VxWorks filesystem can't handle this"}.
-{skip_cases,"kernel_test",file_SUITE,
- [truncate],
- "VxWorks filesystem can't handle this"}.
-{skip_cases,"kernel_test",file_SUITE,
- [rename],
- "VxWorks filesystem can't handle this"}.
-{skip_cases,"kernel_test",file_SUITE,
- [e_delete],
- "VxWorks filesystem can't handle this"}.
-{skip_cases,"kernel_test",file_SUITE,
- [e_rename],
- "VxWorks filesystem can't handle this"}.
-{skip_cases,"kernel_test",file_SUITE,
- [delayed_write],
- "VxWorks filesystem can't handle this"}.
-{skip_cases,"kernel_test",file_SUITE,
- [read_ahead],
- "VxWorks filesystem can't handle this"}.
-{skip_cases,"kernel_test",file_SUITE,
- [segment_write],
- "VxWorks filesystem would overload"}.
-{skip_cases,"kernel_test",file_SUITE,
- [segment_read],
- "VxWorks filesystem would overload"}.
-{skip_cases,"kernel_test",file_SUITE,
- [compress_errors],
- "VxWorks filesystem can't handle this"}.
-{skip_cases,"kernel_test",init_SUITE,[restart],"Uses peer nodes"}.
-{skip_cases,"kernel_test",os_SUITE,[space_in_cwd],"VxWorks can't handle this"}.
-{skip_cases,"kernel_test",os_SUITE,
- [space_in_name],
- "VxWorks can't handle this"}.
-{skip_cases,"kernel_test",os_SUITE,[quoting],"VxWorks can't handle this"}.
-{skip_cases,"kernel_test",prim_file_SUITE,
- [open1],
- "VxWorks filesystem can't handle this"}.
-{skip_cases,"kernel_test",prim_file_SUITE,
- [compress_errors],
- "VxWorks filesystem can't handle this"}.
-{skip_cases,"kernel_test",seq_trace_SUITE,
- [distributed_recv],
- "Test not adopted to slaves on different machine"}.
-{skip_cases,"kernel_test",seq_trace_SUITE,
- [distributed_exit],
- "Test not adopted to slaves on different machine"}.
diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index ae3410d13f..3f2195b609 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -202,8 +202,6 @@ find_executable(Config) when is_list(Config) ->
%% Never return a directory name.
?line false = os:find_executable("unix", [DataDir]),
- ok;
- vxworks ->
ok
end.
diff --git a/lib/kernel/test/pdict_SUITE.erl b/lib/kernel/test/pdict_SUITE.erl
index 8afdfc8a47..60b818cbe3 100644
--- a/lib/kernel/test/pdict_SUITE.erl
+++ b/lib/kernel/test/pdict_SUITE.erl
@@ -152,7 +152,6 @@ heavy(Config) when is_list(Config) ->
time(5000),
?M([],get()),
case {os:type(),?t:is_debug()} of
- {vxworks,_} -> ok;
{_,true} -> ok;
_ ->
time(50000),
diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl
index 3e2202922c..a56746bbc4 100644
--- a/lib/kernel/test/prim_file_SUITE.erl
+++ b/lib/kernel/test/prim_file_SUITE.erl
@@ -406,9 +406,6 @@ cur_dir_1(Config, Handle) ->
{unix, _} ->
?line {error, enotsup} =
?PRIM_FILE_call(get_cwd, Handle, ["d:"]);
- vxworks ->
- ?line {error, enotsup} =
- ?PRIM_FILE_call(get_cwd, Handle, ["d:"]);
{win32, _} ->
win_cur_dir_1(Config, Handle)
end,
@@ -843,10 +840,7 @@ file_info_basic_directory(Config, Handle) ->
?line test_directory("c:/", read_write, Handle),
?line test_directory("c:\\", read_write, Handle);
{unix, _} ->
- ?line test_directory("/", read, Handle);
- vxworks ->
- %% Check is just done for owner
- ?line test_directory("/", read_write, Handle)
+ ?line test_directory("/", read, Handle)
end,
?line test_server:timetrap_cancel(Dog).
@@ -1508,9 +1502,7 @@ e_delete(Config) when is_list(Config) ->
Base, #file_info {mode=8#600});
{win32, _} ->
%% Remove a character device.
- ?line {error, eacces} = ?PRIM_FILE:delete("nul");
- vxworks ->
- ok
+ ?line {error, eacces} = ?PRIM_FILE:delete("nul")
end,
?line test_server:timetrap_cancel(Dog),
@@ -1524,110 +1516,105 @@ e_delete(Config) when is_list(Config) ->
e_rename(suite) -> [];
e_rename(doc) -> [];
e_rename(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {comment, "Windriver: dosFs must be fixed first!"};
- _ ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
- ?line RootDir = ?config(priv_dir, Config),
- ?line Base = filename:join(RootDir,
- atom_to_list(?MODULE)++"_e_rename"),
- ?line ok = ?PRIM_FILE:make_dir(Base),
-
- %% Create an empty directory.
- ?line EmptyDir = filename:join(Base, "empty_dir"),
- ?line ok = ?PRIM_FILE:make_dir(EmptyDir),
-
- %% Create a non-empty directory.
- ?line NonEmptyDir = filename:join(Base, "non_empty_dir"),
- ?line ok = ?PRIM_FILE:make_dir(NonEmptyDir),
- ?line ok = ?PRIM_FILE:write_file(
- filename:join(NonEmptyDir, "a_file"),
- "hello\n"),
-
- %% Create another non-empty directory.
- ?line ADirectory = filename:join(Base, "a_directory"),
- ?line ok = ?PRIM_FILE:make_dir(ADirectory),
- ?line ok = ?PRIM_FILE:write_file(
- filename:join(ADirectory, "a_file"),
- "howdy\n\n"),
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line RootDir = ?config(priv_dir, Config),
+ ?line Base = filename:join(RootDir,
+ atom_to_list(?MODULE)++"_e_rename"),
+ ?line ok = ?PRIM_FILE:make_dir(Base),
- %% Create a data file.
- ?line File = filename:join(Base, "just_a_file"),
- ?line ok = ?PRIM_FILE:write_file(File, "anything goes\n\n"),
-
- %% Move an existing directory to a non-empty directory.
- ?line {error, eexist} =
- ?PRIM_FILE:rename(ADirectory, NonEmptyDir),
-
- %% Move a root directory.
- ?line {error, einval} = ?PRIM_FILE:rename("/", "arne"),
-
- %% Move Base into Base/new_name.
- ?line {error, einval} =
- ?PRIM_FILE:rename(Base, filename:join(Base, "new_name")),
-
- %% Overwrite a directory with a file.
- ?line expect({error, eexist}, % FreeBSD (?)
- {error, eisdir},
- ?PRIM_FILE:rename(File, EmptyDir)),
- ?line expect({error, eexist}, % FreeBSD (?)
- {error, eisdir},
- ?PRIM_FILE:rename(File, NonEmptyDir)),
-
- %% Move a non-existing file.
- ?line NonExistingFile = filename:join(
- Base, "non_existing_file"),
- ?line {error, enoent} =
- ?PRIM_FILE:rename(NonExistingFile, NonEmptyDir),
-
- %% Overwrite a file with a directory.
- ?line expect({error, eexist}, % FreeBSD (?)
- {error, enotdir},
- ?PRIM_FILE:rename(ADirectory, File)),
-
- %% Move a file to another filesystem.
- %% XXX - This test case is bogus. We cannot be guaranteed that
- %% the source and destination are on
- %% different filesystems.
- %%
- %% XXX - Gross hack!
- ?line Comment =
- case os:type() of
- {unix, _} ->
- OtherFs = "/tmp",
- ?line NameOnOtherFs =
- filename:join(OtherFs,
- filename:basename(File)),
- ?line {ok, Com} =
- case ?PRIM_FILE:rename(
- File, NameOnOtherFs) of
- {error, exdev} ->
- %% The file could be in
- %% the same filesystem!
- {ok, ok};
- ok ->
- {ok, {comment,
- "Moving between filesystems "
- "suceeded, files are probably "
- "in the same filesystem!"}};
- {error, eperm} ->
- {ok, {comment, "SBS! You don't "
- "have the permission to do "
- "this test!"}};
- Else ->
- Else
- end,
- Com;
- {win32, _} ->
- %% At least Windows NT can
- %% successfully move a file to
- %% another drive.
- ok
- end,
- ?line test_server:timetrap_cancel(Dog),
- Comment
- end.
+ %% Create an empty directory.
+ ?line EmptyDir = filename:join(Base, "empty_dir"),
+ ?line ok = ?PRIM_FILE:make_dir(EmptyDir),
+
+ %% Create a non-empty directory.
+ ?line NonEmptyDir = filename:join(Base, "non_empty_dir"),
+ ?line ok = ?PRIM_FILE:make_dir(NonEmptyDir),
+ ?line ok = ?PRIM_FILE:write_file(
+ filename:join(NonEmptyDir, "a_file"),
+ "hello\n"),
+
+ %% Create another non-empty directory.
+ ?line ADirectory = filename:join(Base, "a_directory"),
+ ?line ok = ?PRIM_FILE:make_dir(ADirectory),
+ ?line ok = ?PRIM_FILE:write_file(
+ filename:join(ADirectory, "a_file"),
+ "howdy\n\n"),
+
+ %% Create a data file.
+ ?line File = filename:join(Base, "just_a_file"),
+ ?line ok = ?PRIM_FILE:write_file(File, "anything goes\n\n"),
+
+ %% Move an existing directory to a non-empty directory.
+ ?line {error, eexist} =
+ ?PRIM_FILE:rename(ADirectory, NonEmptyDir),
+
+ %% Move a root directory.
+ ?line {error, einval} = ?PRIM_FILE:rename("/", "arne"),
+
+ %% Move Base into Base/new_name.
+ ?line {error, einval} =
+ ?PRIM_FILE:rename(Base, filename:join(Base, "new_name")),
+
+ %% Overwrite a directory with a file.
+ ?line expect({error, eexist}, % FreeBSD (?)
+ {error, eisdir},
+ ?PRIM_FILE:rename(File, EmptyDir)),
+ ?line expect({error, eexist}, % FreeBSD (?)
+ {error, eisdir},
+ ?PRIM_FILE:rename(File, NonEmptyDir)),
+
+ %% Move a non-existing file.
+ ?line NonExistingFile = filename:join(
+ Base, "non_existing_file"),
+ ?line {error, enoent} =
+ ?PRIM_FILE:rename(NonExistingFile, NonEmptyDir),
+
+ %% Overwrite a file with a directory.
+ ?line expect({error, eexist}, % FreeBSD (?)
+ {error, enotdir},
+ ?PRIM_FILE:rename(ADirectory, File)),
+
+ %% Move a file to another filesystem.
+ %% XXX - This test case is bogus. We cannot be guaranteed that
+ %% the source and destination are on
+ %% different filesystems.
+ %%
+ %% XXX - Gross hack!
+ ?line Comment =
+ case os:type() of
+ {unix, _} ->
+ OtherFs = "/tmp",
+ ?line NameOnOtherFs =
+ filename:join(OtherFs,
+ filename:basename(File)),
+ ?line {ok, Com} =
+ case ?PRIM_FILE:rename(
+ File, NameOnOtherFs) of
+ {error, exdev} ->
+ %% The file could be in
+ %% the same filesystem!
+ {ok, ok};
+ ok ->
+ {ok, {comment,
+ "Moving between filesystems "
+ "suceeded, files are probably "
+ "in the same filesystem!"}};
+ {error, eperm} ->
+ {ok, {comment, "SBS! You don't "
+ "have the permission to do "
+ "this test!"}};
+ Else ->
+ Else
+ end,
+ Com;
+ {win32, _} ->
+ %% At least Windows NT can
+ %% successfully move a file to
+ %% another drive.
+ ok
+ end,
+ ?line test_server:timetrap_cancel(Dog),
+ Comment.
e_make_dir(suite) -> [];
e_make_dir(doc) -> [];
@@ -1660,8 +1647,6 @@ e_make_dir(Config) when is_list(Config) ->
?line
?PRIM_FILE:write_file_info(Base, #file_info {mode=8#600});
{win32, _} ->
- ok;
- vxworks ->
ok
end,
?line test_server:timetrap_cancel(Dog),
@@ -1716,8 +1701,6 @@ e_del_dir(Config) when is_list(Config) ->
?line ?PRIM_FILE:write_file_info(
Base, #file_info {mode=8#600});
{win32, _} ->
- ok;
- vxworks ->
ok
end,
?line test_server:timetrap_cancel(Dog),
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index c494f8a864..46a991eb38 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 2.15.2
+KERNEL_VSN = 2.16
diff --git a/lib/megaco/Makefile b/lib/megaco/Makefile
index fc8f408b0a..3bf502ba1c 100644
--- a/lib/megaco/Makefile
+++ b/lib/megaco/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/megaco/configure.in b/lib/megaco/configure.in
index 42c50b8961..722ccc3fb4 100644
--- a/lib/megaco/configure.in
+++ b/lib/megaco/configure.in
@@ -145,7 +145,7 @@ AC_ARG_ENABLE(megaco_flex_scanner_lineno,
AC_SUBST(ENABLE_MEGACO_FLEX_SCANNER_LINENO)
-dnl This is the os flavour, should be unix, vxworks or win32
+dnl This is the os flavour, should be unix or win32
if test "X$host" = "Xwin32"; then
ERLANG_OSTYPE=win32
else
diff --git a/lib/megaco/doc/src/Makefile b/lib/megaco/doc/src/Makefile
index 493304523e..f35413a4fd 100644
--- a/lib/megaco/doc/src/Makefile
+++ b/lib/megaco/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/megaco/doc/src/notes.xml b/lib/megaco/doc/src/notes.xml
index 393064fbb5..928e3bea82 100644
--- a/lib/megaco/doc/src/notes.xml
+++ b/lib/megaco/doc/src/notes.xml
@@ -36,6 +36,78 @@
section is the version number of Megaco.</p>
+ <section><title>Megaco 3.16.0.2</title>
+
+ <p>Version 3.16.0.2 supports code replacement in runtime from/to
+ version 3.16.0.1, 3.16, 3.15.1.1, 3.15.1 and 3.15.</p>
+
+ <section>
+ <title>Improvements and new features</title>
+
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>Allow whitespaces in installation path. </p>
+ <p>It is now possible to give configure and make an
+ installation/release path with whitespaces in it. </p>
+ <p>Own Id: OTP-10107</p>
+ </item>
+
+ <item>
+ <p>Fix parallel make for behaviours. </p>
+ </item>
+
+ <item>
+ <p>Removed use of deprecated system flag,
+ <c>global_haeps_size</c>, in the measurement tool
+ <c>mstone1</c>. </p>
+ </item>
+
+ </list>
+
+ </section>
+
+ <section>
+ <title>Fixed bugs and malfunctions</title>
+
+ <p>-</p>
+
+ <!--
+ <list type="bulleted">
+ <item>
+ <p>Fixing miscellaneous things detected by dialyzer. </p>
+ <p>Own Id: OTP-9075</p>
+ </item>
+
+ </list>
+ -->
+
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>Due to the change in the flex driver API,
+ we may no longer be able to build and/or use
+ the flex driver without reentrant support. </p>
+ <p>Own Id: OTP-9795</p>
+ </item>
+
+ </list>
+-->
+
+ </section>
+
+ </section> <!-- 3.16.0.2 -->
+
+
<section><title>Megaco 3.16.0.1</title>
<p>Version 3.16.0.1 supports code replacement in runtime from/to
diff --git a/lib/megaco/examples/meas/Makefile.in b/lib/megaco/examples/meas/Makefile.in
index 607d81d440..c517fd21cf 100644
--- a/lib/megaco/examples/meas/Makefile.in
+++ b/lib/megaco/examples/meas/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2012. 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/lib/megaco/examples/simple/Makefile b/lib/megaco/examples/simple/Makefile
index 261acbb479..8ec9db71db 100644
--- a/lib/megaco/examples/simple/Makefile
+++ b/lib/megaco/examples/simple/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2009. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. 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/lib/megaco/src/app/Makefile b/lib/megaco/src/app/Makefile
index a7b458845d..42030c5b1c 100644
--- a/lib/megaco/src/app/Makefile
+++ b/lib/megaco/src/app/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2007-2009. All Rights Reserved.
+# Copyright Ericsson AB 2007-2012. 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/lib/megaco/src/app/megaco.appup.src b/lib/megaco/src/app/megaco.appup.src
index 7f89fa8bc2..a7b38eb107 100644
--- a/lib/megaco/src/app/megaco.appup.src
+++ b/lib/megaco/src/app/megaco.appup.src
@@ -145,10 +145,17 @@
%% |
%% v
%% 3.16.0.1
+%% |
+%% v
+%% 3.16.0.2
%%
%%
{"%VSN%",
[
+ {"3.16.0.1",
+ [
+ ]
+ },
{"3.16",
[
]
@@ -170,6 +177,10 @@
}
],
[
+ {"3.16.0.1",
+ [
+ ]
+ },
{"3.16",
[
]
diff --git a/lib/megaco/src/binary/Makefile b/lib/megaco/src/binary/Makefile
index 77b06c81d3..695599b9dc 100644
--- a/lib/megaco/src/binary/Makefile
+++ b/lib/megaco/src/binary/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2009. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/megaco/src/engine/Makefile b/lib/megaco/src/engine/Makefile
index ae2fadb3fc..7cecef1444 100644
--- a/lib/megaco/src/engine/Makefile
+++ b/lib/megaco/src/engine/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/megaco/src/engine/modules.mk b/lib/megaco/src/engine/modules.mk
index 6e2f9246f0..e6e5e29829 100644
--- a/lib/megaco/src/engine/modules.mk
+++ b/lib/megaco/src/engine/modules.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. 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/lib/megaco/src/flex/Makefile.in b/lib/megaco/src/flex/Makefile.in
index 976ecaa4f7..cb5f5412f4 100644
--- a/lib/megaco/src/flex/Makefile.in
+++ b/lib/megaco/src/flex/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. 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
@@ -104,10 +104,6 @@ ENABLE_MEGACO_FLEX_SCANNER_LINENO = @ENABLE_MEGACO_FLEX_SCANNER_LINENO@
endif
endif
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
- DED_INCLUDES += -I$(ERL_TOP)/erts/etc/vxworks
-endif
-
PRIVDIR = ../../priv
LIBDIR = $(PRIVDIR)/lib/$(TARGET)
OBJDIR = $(PRIVDIR)/obj/$(TARGET)
@@ -146,15 +142,10 @@ ifeq ($(findstring win32,$(TARGET)), win32)
FLEX_SCANNER_SO =
SOLIBS = $(FLEX_SCANNER_SO)
else
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-FLEX_SCANNER_SO =
-SOLIBS = $(FLEX_SCANNER_SO)
-else
FLEX_SCANNER_SO = $(LIBDIR)/$(STD_DRV).$(DED_EXT)
FLEX_SCANNER_MT_SO = $(LIBDIR)/$(MT_DRV).$(DED_EXT)
SOLIBS = $(FLEX_SCANNER_SO) $(FLEX_SCANNER_MT_SO)
endif
-endif
# ----------------------------------------------------
diff --git a/lib/megaco/src/rules.mk b/lib/megaco/src/rules.mk
index 20fbed2a76..a59060032d 100644
--- a/lib/megaco/src/rules.mk
+++ b/lib/megaco/src/rules.mk
@@ -29,10 +29,6 @@ PERL = perl
# Erlang language section
# ----------------------------------------------------
EMULATOR = beam
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-# VxWorks jam object files should be compressed
-ERL_COMPILE_FLAGS += +compressed
-endif
ERLC_WFLAGS = -W
ERLC = erlc $(ERLC_WFLAGS) $(ERLC_FLAGS)
ERL.beam = erl.beam -boot start_clean
diff --git a/lib/megaco/src/tcp/Makefile b/lib/megaco/src/tcp/Makefile
index efc76bb2c5..0c30abae2c 100644
--- a/lib/megaco/src/tcp/Makefile
+++ b/lib/megaco/src/tcp/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2009. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/megaco/src/text/Makefile b/lib/megaco/src/text/Makefile
index 2c7703cd33..cab4256e68 100644
--- a/lib/megaco/src/text/Makefile
+++ b/lib/megaco/src/text/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2009. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/megaco/src/udp/Makefile b/lib/megaco/src/udp/Makefile
index db9bb72d82..9486804d38 100644
--- a/lib/megaco/src/udp/Makefile
+++ b/lib/megaco/src/udp/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2009. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/lib/megaco/test/Makefile b/lib/megaco/test/Makefile
index b1bca10ca8..b78f21f608 100644
--- a/lib/megaco/test/Makefile
+++ b/lib/megaco/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2010. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/megaco/test/megaco.spec.vxworks b/lib/megaco/test/megaco.spec.vxworks
deleted file mode 100644
index 2ac250e443..0000000000
--- a/lib/megaco/test/megaco.spec.vxworks
+++ /dev/null
@@ -1,5 +0,0 @@
-{topcase, {dir, "../megaco_test"}}.
-{require_nodenames, 1}.
-{skip, {megaco_digit_map_test, all, "Not yet implemented"}}.
-{skip, {megaco_measure_test, all, "Not yet implemented"}}.
-%{skip, {M, F, "Not yet implemented"}}.
diff --git a/lib/megaco/vsn.mk b/lib/megaco/vsn.mk
index 11a951a23e..5e72ade769 100644
--- a/lib/megaco/vsn.mk
+++ b/lib/megaco/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = megaco
-MEGACO_VSN = 3.16.0.1
+MEGACO_VSN = 3.16.0.2
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(MEGACO_VSN)$(PRE_VSN)"
diff --git a/lib/mnesia/doc/src/Makefile b/lib/mnesia/doc/src/Makefile
index 173e43ac1f..6a72b98ebc 100644
--- a/lib/mnesia/doc/src/Makefile
+++ b/lib/mnesia/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml
index 612ccb5ee8..04b8c106fd 100644
--- a/lib/mnesia/doc/src/notes.xml
+++ b/lib/mnesia/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -38,7 +38,37 @@
thus constitutes one section in this document. The title of each
section is the version number of Mnesia.</p>
- <section><title>Mnesia 4.7</title>
+ <section><title>Mnesia 4.7.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add tests showing that trying to delete non-existing
+ object may corrupt the </p>
+ <p>
+ In case of bag tables, trying to delete a non-existing
+ object leads to the index becoming corrupt. This happens
+ if the non-existing object we try to delete happens to
+ share its key and index field value with a single
+ existing object in the table. Result: The index entry
+ corresponding to the existing object is removed.</p>
+ <p>
+ Prevent index from being corrupted if a nonexistent item
+ is deleted</p>
+ <p>
+ We have to ensure that we actually delete the last object
+ with a given (key, index) pair before removing the index.
+ Thanks to Bartlomiej Puzon</p>
+ <p>
+ Own Id: OTP-10220</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/mnesia/examples/Makefile b/lib/mnesia/examples/Makefile
index 5379761c64..d9e6def283 100644
--- a/lib/mnesia/examples/Makefile
+++ b/lib/mnesia/examples/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/mnesia/examples/mnesia_tpcb.erl b/lib/mnesia/examples/mnesia_tpcb.erl
index 903c53a21c..f36b43a495 100644
--- a/lib/mnesia/examples/mnesia_tpcb.erl
+++ b/lib/mnesia/examples/mnesia_tpcb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2010. 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
@@ -99,9 +99,13 @@
replica_test/1,
sticky_replica_test/1,
remote_test/1,
- remote_frag2_test/1
+ remote_frag2_test/1,
+
+ conflict_benchmark/1
]).
+-include_lib("common_test/include/ct_event.hrl").
+
-define(SECOND, 1000000).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -191,8 +195,10 @@
driver_nodes = [node()],
n_drivers_per_node = 1,
use_running_mnesia = false,
+ seed,
stop_after = timer:minutes(15), % Minimum 15 min
report_interval = timer:minutes(1),
+ send_bench_report = false,
use_sticky_locks = false,
spawn_near_branch = false,
activity_type = transaction,
@@ -397,8 +403,30 @@ config(remote_frag2_test, ReplicaType) ->
{stop_after, timer:minutes(1)},
{report_interval, timer:seconds(10)},
{reuse_history_id, true}
+ ];
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Ten drivers per node, tables replicated to all nodes, single branch
+
+config(conflict_benchmark, ReplicaType) ->
+ Remote = nodes(),
+ Local = node(),
+ Nodes = [Local | Remote],
+ [{seed, {1326,448637,337711}},
+ {db_nodes, Nodes},
+ {driver_nodes, Nodes},
+ {replica_nodes, Nodes},
+ {n_drivers_per_node, 10},
+ {n_branches, 1},
+ {n_accounts_per_branch, 10},
+ {replica_type, ReplicaType},
+ {stop_after, timer:minutes(1)},
+ {report_interval, timer:seconds(10)},
+ {send_bench_report, true},
+ {reuse_history_id, true}
].
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
start(What, ReplicaType) ->
@@ -422,6 +450,9 @@ remote_test(ReplicaType) ->
remote_frag2_test(ReplicaType) ->
start(remote_frag2_test, ReplicaType).
+conflict_benchmark(ReplicaType) ->
+ start(config(conflict_benchmark, ReplicaType)).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Args is a list of {Key, Val} tuples where Key is a field name
%% in either the record tab_config or run_config. Unknown keys are ignored.
@@ -866,6 +897,7 @@ add_time(Acc, New) ->
show_report(State) ->
Now = now_to_micros(erlang:now()),
Iters = State#reporter_state.n_iters,
+ Cfg = State#reporter_state.run_config,
Time = State#reporter_state.curr,
Max = Time#time.max_time,
N = Time#time.n_trans,
@@ -888,7 +920,17 @@ show_report(State) ->
"duration of longest transaction was ~p milliseconds~n",
[Tps, BruttoTps, Max div 1000])
end,
- State#reporter_state{prev_tps = Tps, prev_micros = Now}.
+ case Cfg#run_config.send_bench_report of
+ true ->
+ ct_event:notify(
+ #event{name = benchmark_data,
+ data = [{suite,"mnesia_tpcb"},
+ {value,Tps}]});
+ _ ->
+ ok
+ end,
+
+ State#reporter_state{prev_tps = Tps, prev_micros = Now}.
signed_diff(Iters, Curr, Prev) ->
case Iters > 1 of
@@ -955,7 +997,13 @@ alloc_local_branches([], Specs, OrphanBranches) ->
{Specs, OrphanBranches}.
driver_init(DS, AllBranches) ->
- Seed = erlang:now(),
+ case (DS#driver_state.run_config)#run_config.seed of
+ undefined ->
+ Seed = erlang:now();
+ Seed ->
+ Seed
+ end,
+
DS2 =
if
DS#driver_state.n_local_branches =:= 0 ->
diff --git a/lib/mnesia/include/Makefile b/lib/mnesia/include/Makefile
index 20f62b5460..eea28045ea 100644
--- a/lib/mnesia/include/Makefile
+++ b/lib/mnesia/include/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+# Copyright Ericsson AB 1998-2012. 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/lib/mnesia/src/Makefile b/lib/mnesia/src/Makefile
index 1432eabc37..6f289433ff 100644
--- a/lib/mnesia/src/Makefile
+++ b/lib/mnesia/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/mnesia/src/mnesia.appup.src b/lib/mnesia/src/mnesia.appup.src
index 304a15242f..355aafb215 100644
--- a/lib/mnesia/src/mnesia.appup.src
+++ b/lib/mnesia/src/mnesia.appup.src
@@ -1,6 +1,9 @@
%% -*- erlang -*-
{"%VSN%",
[
+ {"4.7.1", [{restart_application, mnesia}]},
+ {"4.7", [{restart_application, mnesia}]},
+ {"4.6", [{restart_application, mnesia}]},
{"4.5.1", [{restart_application, mnesia}]},
{"4.5", [{restart_application, mnesia}]},
{"4.4.19", [{restart_application, mnesia}]},
@@ -9,6 +12,9 @@
{"4.4.16", [{restart_application, mnesia}]}
],
[
+ {"4.7.1", [{restart_application, mnesia}]},
+ {"4.7", [{restart_application, mnesia}]},
+ {"4.6", [{restart_application, mnesia}]},
{"4.5.1", [{restart_application, mnesia}]},
{"4.5", [{restart_application, mnesia}]},
{"4.4.19", [{restart_application, mnesia}]},
diff --git a/lib/mnesia/src/mnesia_index.erl b/lib/mnesia/src/mnesia_index.erl
index 61210d7e55..f9f3ce2ea4 100644
--- a/lib/mnesia/src/mnesia_index.erl
+++ b/lib/mnesia/src/mnesia_index.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -120,9 +120,9 @@ del_object_bag(Tab, Key, Obj, Pos, Ixt, undefined) ->
IxKey = element(Pos, Obj),
Old = [X || X <- mnesia_lib:db_get(Tab, Key), element(Pos, X) =:= IxKey],
del_object_bag(Tab, Key, Obj, Pos, Ixt, Old);
-%% If Tab type is bag we need remove index identifier if Tab
-%% contains less than 2 elements.
-del_object_bag(_Tab, Key, Obj, Pos, Ixt, Old) when length(Old) < 2 ->
+%% If Tab type is bag we need remove index identifier if the object being
+%% deleted was the last one
+del_object_bag(_Tab, Key, Obj, Pos, Ixt, Old) when Old =:= [Obj] ->
del_ixes(Ixt, [Obj], Pos, Key);
del_object_bag(_Tab, _Key, _Obj, _Pos, _Ixt, _Old) -> ok.
diff --git a/lib/mnesia/test/Makefile b/lib/mnesia/test/Makefile
index 0451da697d..45ce5b1983 100644
--- a/lib/mnesia/test/Makefile
+++ b/lib/mnesia/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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
@@ -26,6 +26,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
MODULES= \
mt \
mnesia_SUITE \
+ mnesia_bench_SUITE \
mnesia_test_lib \
mnesia_install_test \
mnesia_registry_test \
@@ -117,7 +118,7 @@ release_spec: opt
release_tests_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)"
- $(INSTALL_DATA) mnesia.spec mnesia.cover $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)"
+ $(INSTALL_DATA) mnesia.spec mnesia_bench.spec mnesia.cover $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)"
$(INSTALL_SCRIPT) mt $(INSTALL_PROGS) "$(RELSYSDIR)"
# chmod -R u+w "$(RELSYSDIR)"
# @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
diff --git a/lib/mnesia/test/mnesia.spec.vxworks b/lib/mnesia/test/mnesia.spec.vxworks
deleted file mode 100644
index 11c01ea3fe..0000000000
--- a/lib/mnesia/test/mnesia.spec.vxworks
+++ /dev/null
@@ -1,362 +0,0 @@
-{topcase, {dir, "../mnesia_test"}}.
-{require_nodenames, 3}.
-{diskless, true}.
-{skip, {mnesia_measure_test, all, "Too heavy"}}.
-%{mnesia_install_test, silly_durability} 'IMPL'
-%{mnesia_install_test, silly_move} 'IMPL'
-{skip, {mnesia_install_test, silly_upgrade, "Uses disk"}}.
-%{mnesia_install_test, conflict} 'IMPL'
-%{mnesia_install_test, dist} 'IMPL'
-{skip, {mnesia_examples_test, all, "Uses disk"}}.
-{skip, {mnesia_nice_coverage_test, all, "Uses disk"}}.
-
-%{mnesia_evil_coverage_test, system_info} 'IMPL'
-%{mnesia_evil_coverage_test, table_info} 'IMPL'
-%{mnesia_evil_coverage_test, error_description} 'IMPL'
-{skip, {mnesia_evil_coverage_test, db_node_lifecycle, "Uses disk"}}.
-{skip, {mnesia_evil_coverage_test, local_content, "Uses disk"}}.
-%{mnesia_evil_coverage_test, start_and_stop} 'IMPL'
-%{mnesia_evil_coverage_test, transaction} 'IMPL'
-{skip, {mnesia_evil_coverage_test, checkpoint, "Uses disk"}}.
-{skip, {mnesia_evil_backup, backup, "Uses disk"}}.
-{skip, {mnesia_evil_backup, global_backup_checkpoint, "Uses disk"}}.
-{skip, {mnesia_evil_backup, incremental_backup_checkpoint, "Uses disk"}}.
-{skip, {mnesia_evil_backup, local_backup_checkpoint, "Uses disk"}}.
-{skip, {mnesia_evil_backup, selective_backup_checkpoint, "Uses disk"}}.
-{skip, {mnesia_evil_backup, restore_errors, "Uses disk"}}.
-{skip, {mnesia_evil_backup, restore_clear, "Uses disk"}}.
-{skip, {mnesia_evil_backup, restore_keep, "Uses disk"}}.
-{skip, {mnesia_evil_backup, restore_recreate, "Uses disk"}}.
-{skip, {mnesia_evil_backup, traverse_backup, "Uses disk"}}.
-{skip, {mnesia_evil_backup, install_fallback, "Uses disk"}}.
-{skip, {mnesia_evil_backup, uninstall_fallback, "Uses disk"}}.
-{skip, {mnesia_evil_backup, local_fallback, "Uses disk"}}.
-%{mnesia_evil_coverage_test, table_lifecycle} 'IMPL'
-{skip, {mnesia_evil_coverage_test, replica_management, "Uses disk"}}.
-%{mnesia_evil_coverage_test, change_table_access_mode} 'IMPL'
-%{mnesia_evil_coverage_test, change_table_load_order} 'IMPL'
-{skip, {mnesia_evil_coverage_test, set_master_nodes, "Uses disk"}}.
-{skip, {mnesia_evil_coverage_test, offline_set_master_nodes, "Uses disk"}}.
-{skip, {mnesia_evil_coverage_test, replica_location, "Uses disk"}}.
-%{mnesia_evil_coverage_test, add_table_index_ram} 'IMPL'
-{skip, {mnesia_trans_access_test, add_table_index_disc, "Uses disc"}}.
-{skip, {mnesia_trans_access_test, add_table_index_disc_only, "Uses disc"}}.
-%{mnesia_evil_coverage_test, create_live_table_index_ram} 'IMPL'
-{skip, {mnesia_trans_access_test, create_live_table_index_disc, "Uses disc"}}.
-{skip, {mnesia_trans_access_test, create_live_table_index_disc_only, "Uses disc"}}.
-%{mnesia_evil_coverage_test, del_table_index_ram} 'IMPL'
-{skip, {mnesia_trans_access_test, del_table_index_disc, "Uses disc"}}.
-{skip, {mnesia_trans_access_test, del_table_index_disc_only, "Uses disc"}}.
-{skip, {mnesia_trans_access_test, idx_schema_changes_ram, "Uses disk"}}.
-{skip, {mnesia_trans_access_test, idx_schema_changes_disc, "Uses disc"}}.
-{skip, {mnesia_trans_access_test, idx_schema_changes_disc_only, "Uses disc"}}.
-%{mnesia_dirty_access_test, dirty_write_ram} 'IMPL'
-
-{skip, {mnesia_dirty_access_test, dirty_write_disc, "Uses disc"}}.
-{skip, {mnesia_dirty_access_test, dirty_write_disc_only, "Uses disc"}}.
-%{mnesia_dirty_access_test, dirty_read_ram} 'IMPL'
-{skip, {mnesia_dirty_access_test, dirty_read_disc, "Uses disc"}}.
-{skip, {mnesia_dirty_access_test, dirty_read_disc_only, "Uses disc"}}.
-%{mnesia_dirty_access_test, dirty_update_counter_ram} 'IMPL'
-{skip, {mnesia_dirty_access_test, dirty_update_counter_disc, "Uses disc"}}.
-{skip, {mnesia_dirty_access_test, dirty_update_counter_disc_only, "Uses disc"}}.
-%{mnesia_dirty_access_test, dirty_delete_ram} 'IMPL'
-{skip, {mnesia_dirty_access_test, dirty_delete_disc, "Uses disc"}}.
-{skip, {mnesia_dirty_access_test, dirty_delete_disc_only, "Uses disc"}}.
-%{mnesia_dirty_access_test, dirty_delete_object_ram} 'IMPL'
-{skip, {mnesia_dirty_access_test, dirty_delete_object_disc, "Uses disc"}}.
-{skip, {mnesia_dirty_access_test, dirty_delete_object_disc_only, "Uses disc"}}.
-%{mnesia_dirty_access_test, dirty_match_object_ram} 'IMPL'
-{skip, {mnesia_dirty_access_test, dirty_match_object_disc, "Uses disc"}}.
-{skip, {mnesia_dirty_access_test, dirty_match_object_disc_only, "Uses disc"}}.
-%{mnesia_dirty_access_test, dirty_index_match_object_ram} 'IMPL'
-{skip, {mnesia_dirty_access_test, dirty_index_match_object_disc, "Uses disc"}}.
-{skip, {mnesia_dirty_access_test, dirty_index_match_object_disc_only, "Uses disc"}}.
-%{mnesia_dirty_access_test, dirty_index_read_ram} 'IMPL'
-{skip, {mnesia_dirty_access_test, dirty_index_read_disc, "Uses disc"}}.
-{skip, {mnesia_dirty_access_test, dirty_index_read_disc_only, "Uses disc"}}.
-%{mnesia_dirty_access_test, dirty_index_update_set_ram} 'IMPL'
-{skip, {mnesia_dirty_access_test, dirty_index_update_set_disc, "Uses disc"}}.
-{skip, {mnesia_dirty_access_test, dirty_index_update_set_disc_only, "Uses disc"}}.
-%{mnesia_dirty_access_test, dirty_index_update_bag_ram} 'IMPL'
-{skip, {mnesia_dirty_access_test, dirty_index_update_bag_disc, "Uses disc"}}.
-{skip, {mnesia_dirty_access_test, dirty_index_update_bag_disc_only, "Uses disc"}}.
-%{mnesia_dirty_access_test, dirty_iter_ram} 'IMPL'
-{skip, {mnesia_dirty_access_test, dirty_iter_disc, "Uses disc"}}.
-{skip, {mnesia_dirty_access_test, dirty_iter_disc_only, "Uses disc"}}.
-{skip, {mnesia_dirty_access_test, admin_tests, "Uses disk"}}.
-
-%{mnesia_trans_access_test, write} 'IMPL'
-%{mnesia_trans_access_test, read} 'IMPL'
-%{mnesia_trans_access_test, wread} 'IMPL'
-%{mnesia_trans_access_test, delete} 'IMPL'
-%{mnesia_trans_access_test, delete_object} 'IMPL'
-%{mnesia_trans_access_test, match_object} 'IMPL'
-%{mnesia_trans_access_test, all_keys} 'IMPL'
-%{mnesia_trans_access_test, index_match_object} 'IMPL'
-%{mnesia_trans_access_test, index_read} 'IMPL'
-%{mnesia_trans_access_test, index_update_set} 'IMPL'
-%{mnesia_trans_access_test, index_update_bag} 'IMPL'
-{skip, {mnesia_evil_coverage_test, dump_tables, "Uses disk"}}.
-{skip, {mnesia_evil_coverage_test, dump_log, "Uses disk"}}.
-%{mnesia_evil_coverage_test, wait_for_tables} 'IMPL'
-{skip, {mnesia_evil_coverage_test, force_load_table, "Uses disk"}}.
-%{mnesia_evil_coverage_test, user_properties} 'IMPL'
-%{mnesia_evil_coverage_test, record_name_dirty_access_ram} 'IMPL'
-{skip, {mnesia_evil_coverage_test, record_name_dirty_access_disc, "Uses disc"}}.
-{skip, {mnesia_evil_coverage_test, record_name_dirty_access_disc_only, "Uses disc"}}.
-%{mnesia_evil_coverage_test, snmp_open_table} 'IMPL'
-%{mnesia_evil_coverage_test, snmp_close_table} 'IMPL'
-%{mnesia_evil_coverage_test, snmp_get_next_index} 'IMPL'
-%{mnesia_evil_coverage_test, snmp_get_row} 'IMPL'
-%{mnesia_evil_coverage_test, snmp_get_mnesia_key} 'IMPL'
-%{mnesia_evil_coverage_test, snmp_update_counter} 'IMPL'
-%{mnesia_evil_coverage_test, info} 'IMPL'
-%{mnesia_evil_coverage_test, schema_0} 'IMPL'
-%{mnesia_evil_coverage_test, schema_1} 'IMPL'
-%{mnesia_evil_coverage_test, view_0} 'IMPL'
-{skip, {mnesia_evil_coverage_test, view_1, "Uses disk"}}.
-{skip, {mnesia_evil_coverage_test, view_2, "Uses disk"}}.
-%{mnesia_evil_coverage_test, lkill} 'IMPL'
-%{mnesia_evil_coverage_test, kill} 'IMPL'
-
-%{mnesia_config_test, access_module} 'IMPL'
-%{mnesia_config_test, auto_repair} 'IMPL'
-{skip, {mnesia_config_test, backup_module, "Uses disk"}}.
-{skip, {mnesia_config_test, dynamic_connect, "Uses disk"}}.
-%{mnesia_config_test, debug} 'IMPL'
-%{mnesia_config_test, dir} 'IMPL'
-{skip, {mnesia_config_test, dump_log_load_regulation, "Uses disk"}}.
-{skip, {mnesia_config_test, dump_log_time_threshold, "Uses disk"}}.
-{skip, {mnesia_config_test, dump_log_write_threshold, "Uses disk"}}.
-{skip, {mnesia_config_test, dump_log_update_in_place, "Uses disk"}}.
-{skip, {mnesia_config_test, embedded_mnemosyne, "Uses Mnemosyne"}}.
-%{mnesia_config_test, event_module} 'IMPL'
-{skip, {mnesia_config_test, ignore_fallback_at_startup, "Not Yet impl"}}.
-%{mnesia_config_test, inconsistent_database} 'IMPL'
-{skip, {mnesia_config_test, max_wait_for_decision, "Not Yet impl"}}.
-{skip, {mnesia_config_test, start_one_disc_full_then_one_disc_less, "Uses disc"}}.
-{skip, {mnesia_config_test, start_first_one_disc_less_then_one_disc_full, "Uses disc"}}.
-%%{skip, {mnesia_config_test, start_first_one_disc_less_then_two_more_disc_less, "Uses disc"}}.
-{skip, {mnesia_config_test, schema_location_and_extra_db_nodes_combinations, "Uses disk"}}.
-{skip, {mnesia_config_test, table_load_to_disc_less_nodes, "Uses disc"}}.
-{skip, {mnesia_config_test, schema_merge, "Uses Disc"}}.
-%{mnesia_config_test, unknown_config} 'IMPL'
-%{mnesia_registry_test, good_dump} 'IMPL'
-%{mnesia_registry_test, bad_dump} 'IMPL'
-
-%{mnesia_atomicity_test, explicit_abort_in_middle_of_trans} 'IMPL'
-%{mnesia_atomicity_test, runtime_error_in_middle_of_trans} 'IMPL'
-%{mnesia_atomicity_test, kill_self_in_middle_of_trans} 'IMPL'
-%{mnesia_atomicity_test, throw_in_middle_of_trans} 'IMPL'
-%{mnesia_atomicity_test, mnesia_down_during_infinite_trans} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_sw_rt} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_sw_wt} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_wr_r} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_sw_sw} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_sw_w} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_sw_wr} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_wr_wt} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_wr_sw} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_wr_w} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_r_sw} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_r_w} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_r_wt} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_rt_sw} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_rt_w} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_rt_wt} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_wt_r} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_wt_w} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_wt_rt} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_wt_wt} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_wt_wr} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_wt_sw} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_w_wr} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_w_sw} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_w_r} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_w_w} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_w_rt} 'IMPL'
-%{mnesia_atomicity_test, lock_waiter_w_wt} 'IMPL'
-%{mnesia_atomicity_test, restart_r_one} 'IMPL'
-%{mnesia_atomicity_test, restart_w_one} 'IMPL'
-%{mnesia_atomicity_test, restart_rt_one} 'IMPL'
-%{mnesia_atomicity_test, restart_wt_one} 'IMPL'
-%{mnesia_atomicity_test, restart_wr_one} 'IMPL'
-%{mnesia_atomicity_test, restart_sw_one} 'IMPL'
-%{mnesia_atomicity_test, restart_r_two} 'IMPL'
-%{mnesia_atomicity_test, restart_w_two} 'IMPL'
-%{mnesia_atomicity_test, restart_rt_two} 'IMPL'
-%{mnesia_atomicity_test, restart_wt_two} 'IMPL'
-%{mnesia_atomicity_test, restart_wr_two} 'IMPL'
-%{mnesia_atomicity_test, restart_sw_two} 'IMPL'
-
-%{mnesia_isolation_test, no_conflict} 'IMPL'
-%{mnesia_isolation_test, simple_queue_conflict} 'IMPL'
-%{mnesia_isolation_test, advanced_queue_conflict} 'IMPL'
-%{mnesia_isolation_test, simple_deadlock_conflict} 'IMPL'
-%{mnesia_isolation_test, advanced_deadlock_conflict} 'IMPL'
-%{mnesia_isolation_test, lock_burst} 'IMPL'
-%{mnesia_isolation_test, basic_sticky_functionality} 'IMPL'
-%{mnesia_isolation_test, create_table} 'IMPL'
-%{mnesia_isolation_test, delete_table} 'IMPL'
-%{mnesia_isolation_test, move_table_copy} 'IMPL'
-%{mnesia_isolation_test, add_table_index} 'IMPL'
-%{mnesia_isolation_test, del_table_index} 'IMPL'
-%{mnesia_isolation_test, transform_table} 'IMPL'
-%{mnesia_isolation_test, snmp_open_table} 'IMPL'
-%{mnesia_isolation_test, snmp_close_table} 'IMPL'
-{skip, {mnesia_isolation_test, change_table_copy_type, "Uses disk"}}.
-%{mnesia_isolation_test, change_table_access} 'IMPL'
-%{mnesia_isolation_test, add_table_copy} 'IMPL'
-%{mnesia_isolation_test, del_table_copy} 'IMPL'
-{skip, {mnesia_isolation_test, dump_tables, "Uses disk"}}.
-{skip, {mnesia_isolation_test, extra_admin_tests, "Uses disk"}}.
-%{mnesia_isolation_test, del_table_copy_1} 'IMPL'
-%{mnesia_isolation_test, del_table_copy_2} 'IMPL'
-%{mnesia_isolation_test, del_table_copy_3} 'IMPL'
-%{mnesia_isolation_test, add_table_copy_1} 'IMPL'
-%{mnesia_isolation_test, add_table_copy_2} 'IMPL'
-%{mnesia_isolation_test, add_table_copy_3} 'IMPL'
-%{mnesia_isolation_test, add_table_copy_4} 'IMPL'
-%{mnesia_isolation_test, move_table_copy_1} 'IMPL'
-%{mnesia_isolation_test, move_table_copy_2} 'IMPL'
-%{mnesia_isolation_test, move_table_copy_3} 'IMPL'
-%{mnesia_isolation_test, move_table_copy_4} 'IMPL'
-%{mnesia_isolation_test, dirty_updates_visible_direct} 'IMPL'
-%{mnesia_isolation_test, dirty_reads_regardless_of_trans} 'IMPL'
-%{mnesia_isolation_test, trans_update_invisibible_outside_trans} 'IMPL'
-%{mnesia_isolation_test, trans_update_visible_inside_trans} 'IMPL'
-%{mnesia_isolation_test, write_shadows} 'IMPL'
-%{mnesia_isolation_test, delete_shadows} 'IMPL'
-%{mnesia_isolation_test, write_delete_shadows_bag} 'IMPL'
-
-{skip, {mnesia_durability_test, all, "Uses disk "}}.
-%{mnesia_durability_test, load_local_contents_directly} 'IMPL'
-%{mnesia_durability_test, load_directly_when_all_are_ram_copiesA} 'IMPL'
-%{mnesia_durability_test, load_directly_when_all_are_ram_copiesB} 'IMPL'
-%{skip, {mnesia_durability_test, late_load_when_all_are_ram_copies_on_ram_nodes1, "Uses disk schema"}}.
-%{skip, {mnesia_durability_test, late_load_when_all_are_ram_copies_on_ram_nodes2, "Uses disk schema"}}.
-%{skip, {mnesia_durability_test, load_when_last_replica_becomes_available, "Uses disk"}}.
-%{skip, {mnesia_durability_test, load_when_we_have_down_from_all_other_replica_nodes, "Uses disk"}}.
-%{skip, {mnesia_durability_test, late_load_transforms_into_disc_load, "Uses disc"}}.
-%{mnesia_durability_test, late_load_leads_to_hanging} 'IMPL'
-%{mnesia_durability_test, force_load_when_nobody_intents_to_load} 'IMPL'
-%{mnesia_durability_test, force_load_when_someone_has_decided_to_load} 'IMPL'
-%{mnesia_durability_test, force_load_when_someone_else_already_has_loaded} 'IMPL'
-%{mnesia_durability_test, force_load_when_we_has_loaded} 'IMPL'
-%{mnesia_durability_test, force_load_on_a_non_local_table} 'IMPL'
-%{mnesia_durability_test, force_load_when_the_table_does_not_exist} 'IMPL'
-%{mnesia_durability_test, master_nodes} 'IMPL'
-%{mnesia_durability_test, master_on_non_local_tables} 'IMPL'
-%{mnesia_durability_test, remote_force_load_with_local_master_node} 'IMPL'
-%{mnesia_durability_test, dump_ram_copies} 'IMPL'
-%{skip, {mnesia_durability_test, dump_disc_copies, "Uses disc"}}.
-%{skip, {mnesia_durability_test, dump_disc_only, "Uses disc"}}.
-%{skip, {mnesia_durability_test, durability_of_disc_copies, "Uses disc"}}.
-%{skip, {mnesia_durability_test, durability_of_disc_only_copies, "Uses disc"}}.
-
-{skip, {mnesia_recovery_test, mnesia_down, "Uses Disk"}}.
-%{mnesia_recovery_test, no_master_2} 'IMPL'
-%{mnesia_recovery_test, no_master_3} 'IMPL'
-%{mnesia_recovery_test, one_master_2} 'IMPL'
-%{mnesia_recovery_test, one_master_3} 'IMPL'
-%{mnesia_recovery_test, two_master_2} 'IMPL'
-%{mnesia_recovery_test, two_master_3} 'IMPL'
-%{mnesia_recovery_test, all_master_2} 'IMPL'
-%{mnesia_recovery_test, all_master_3} 'IMPL'
-{skip, {mnesia_recovery_test, mnesia_down_during_startup_disk_ram, "Uses disk"}}.
-%{mnesia_recovery_test, mnesia_down_during_startup_init_ram} 'IMPL'
-{skip, {mnesia_recovery_test, mnesia_down_during_startup_init_disc, "Uses disc"}}.
-{skip, {mnesia_recovery_test, mnesia_down_during_startup_init_disc_only, "Uses disc"}}.
-%{mnesia_recovery_test, mnesia_down_during_startup_tm_ram} 'IMPL'
-{skip, {mnesia_recovery_test, mnesia_down_during_startup_tm_disc, "Uses disc"}}.
-{skip, {mnesia_recovery_test, mnesia_down_during_startup_tm_disc_only, "Uses disc"}}.
-%{mnesia_recovery_test, explicit_stop_during_snmp} 'IMPL'
-
-{skip, {mnesia_recovery_test, schema_trans, "Uses Disk, needs disk log"}}.
-{skip, {mnesia_recovery_test, async_dirty, "Uses disc"}}.
-{skip, {mnesia_recovery_test, sync_dirty, "Uses disc"}}.
-{skip, {mnesia_recovery_test, sym_trans, "Uses disc"}}.
-{skip, {mnesia_recovery_test, asym_trans, "Uses disc"}}.
-
-{skip, {mnesia_recovery_test, after_full_disc_partition, "Not Yet impl"}}.
-{skip, {mnesia_recovery_test, after_corrupt_files, "Uses disk"}}.
-
-%{mnesia_evil_coverage_test, subscriptions} 'IMPL'
-%{mnesia_evil_coverage_test, nested_trans_both_ok} 'IMPL'
-%{mnesia_evil_coverage_test, nested_trans_child_dies} 'IMPL'
-%{mnesia_evil_coverage_test, nested_trans_parent_dies} 'IMPL'
-%{mnesia_evil_coverage_test, nested_trans_both_dies} 'IMPL'
-%{mnesia_evil_coverage_test, mix_of_trans_sync_dirty} 'IMPL'
-%{mnesia_evil_coverage_test, mix_of_trans_async_dirty} 'IMPL'
-%{mnesia_evil_coverage_test, mix_of_trans_ets} 'IMPL'
-
-{skip, {mnesia_recovery_test, disc_less, "Uses disc (on the other nodes)"}}.
-{skip, {mnesia_recovery_test, system_upgrade, "Not Yet impl"}}.
-%{mnesia_consistency_test, consistency_after_restart_1_ram} 'IMPL'
-{skip, {mnesia_consistency_test, consistency_after_restart_1_disc, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_restart_1_disc_only, "Uses disc"}}.
-%{mnesia_consistency_test, consistency_after_restart_2_ram} 'IMPL'
-{skip, {mnesia_consistency_test, consistency_after_restart_2_disc, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_restart_2_disc_only, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_dump_tables_1_ram, "Uses disk"}}.
-{skip, {mnesia_consistency_test, consistency_after_dump_tables_2_ram, "Uses disk"}}.
-%{mnesia_consistency_test, consistency_after_add_replica_2_ram} 'IMPL'
-{skip, {mnesia_consistency_test, consistency_after_add_replica_2_disc, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_add_replica_2_disc_only, "Uses disc"}}.
-%{mnesia_consistency_test, consistency_after_add_replica_3_ram} 'IMPL'
-{skip, {mnesia_consistency_test, consistency_after_add_replica_3_disc, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_add_replica_3_disc_only, "Uses disc"}}.
-%{mnesia_consistency_test, consistency_after_del_replica_2_ram} 'IMPL'
-{skip, {mnesia_consistency_test, consistency_after_del_replica_2_disc, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_del_replica_2_disc_only, "Uses disc"}}.
-%{mnesia_consistency_test, consistency_after_del_replica_3_ram} 'IMPL'
-{skip, {mnesia_consistency_test, consistency_after_del_replica_3_disc, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_del_replica_3_disc_only, "Uses disc"}}.
-%{mnesia_consistency_test, consistency_after_move_replica_2_ram} 'IMPL'
-{skip, {mnesia_consistency_test, consistency_after_move_replica_2_disc, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_move_replica_2_disc_only, "Uses disc"}}.
-%{mnesia_consistency_test, consistency_after_move_replica_3_ram} 'IMPL'
-{skip, {mnesia_consistency_test, consistency_after_move_replica_3_disc, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_move_replica_3_disc_only, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_transform_table, "Not yet implemented"}}.
-{skip, {mnesia_consistency_test, consistency_after_change_table_copy_type, "Not yet implemented"}}.
-{skip, {mnesia_consistency_test, consistency_after_fallback_2_ram, "Uses disk"}}.
-{skip, {mnesia_consistency_test, consistency_after_fallback_2_disc, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_fallback_2_disc_only, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_fallback_3_ram, "Uses disk"}}.
-{skip, {mnesia_consistency_test, consistency_after_fallback_3_disc, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_fallback_3_disc_only, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_restore_clear_ram, "Uses disk"}}.
-{skip, {mnesia_consistency_test, consistency_after_restore_clear_disc, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_restore_clear_disc_only, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_restore_recreate_ram, "Uses disk"}}.
-{skip, {mnesia_consistency_test, consistency_after_restore_recreate_disc, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_restore_recreate_disc_only, "Uses disc"}}.
-{skip, {mnesia_consistency_test, consistency_after_rename_of_node, "Not yet implemented"}}.
-{skip, {mnesia_consistency_test, updates_during_checkpoint_activation, "Uses disk"}}.
-%{skip, {mnesia_consistency_test, updates_during_checkpoint_activation_2_disc, "Uses disc"}}.
-%{skip, {mnesia_consistency_test, updates_during_checkpoint_activation_2_disc_only, "Uses disc"}}.
-%%{mnesia_consistency_test, updates_during_checkpoint_activation_3_ram} 'IMPL'
-%{skip, {mnesia_consistency_test, updates_during_checkpoint_activation_3_disc, "Uses disc"}}.
-%{skip, {mnesia_consistency_test, updates_during_checkpoint_activation_3_disc_only, "Uses disc"}}.
-{skip, {mnesia_consistency_test, updates_during_checkpoint_iteration, "Uses disk"}}.
-%{skip, {mnesia_consistency_test, updates_during_checkpoint_iteration_2_disc, "Uses disc"}}.
-%{skip, {mnesia_consistency_test, updates_during_checkpoint_iteration_2_disc_only, "Uses disc"}}.
-{skip, {mnesia_consistency_test, load_table_with_activated_checkpoint_ram, "Uses disk"}}.
-{skip, {mnesia_consistency_test, load_table_with_activated_checkpoint_disc, "Uses disc"}}.
-{skip, {mnesia_consistency_test, load_table_with_activated_checkpoint_disc_only, "Uses disc"}}.
-{skip, {mnesia_consistency_test, add_table_copy_to_table_with_activated_checkpoint_ram, "Uses disk"}}.
-{skip, {mnesia_consistency_test, add_table_copy_to_table_with_activated_checkpoint_disc, "Uses disc"}}.
-{skip, {mnesia_consistency_test, add_table_copy_to_table_with_activated_checkpoint_disc_only, "Uses disc"}}.
-{skip, {mnesia_consistency_test, inst_fallback_process_dies, "Uses disk"}}.
-{skip, {mnesia_consistency_test, fatal_when_inconsistency, "Uses disk"}}.
-{skip, {mnesia_consistency_test, after_delete, "Uses disk"}}.
-{skip, {mnesia_consistency_test, mnesia_down_during_backup_causes_switch, "Uses disk"}}.
-{skip, {mnesia_consistency_test, mnesia_down_during_backup_causes_abort, "Uses disk"}}.
-%{mnesia_consistency_test, cause_switch_after} 'IMPL'
-%{mnesia_consistency_test, cause_abort_before} 'IMPL'
-%{mnesia_consistency_test, cause_abort_after} 'IMPL'
-%{mnesia_consistency_test, change_schema_before} 'IMPL'
-%{mnesia_consistency_test, change_schema_after} 'IMPL'
-
diff --git a/lib/mnesia/test/mnesia_bench.spec b/lib/mnesia/test/mnesia_bench.spec
new file mode 100644
index 0000000000..7b17cb5c4e
--- /dev/null
+++ b/lib/mnesia/test/mnesia_bench.spec
@@ -0,0 +1 @@
+{suites,"../mnesia_test",[mnesia_bench_SUITE]}. \ No newline at end of file
diff --git a/lib/mnesia/test/mnesia_bench_SUITE.erl b/lib/mnesia/test/mnesia_bench_SUITE.erl
new file mode 100644
index 0000000000..7cbf77f046
--- /dev/null
+++ b/lib/mnesia/test/mnesia_bench_SUITE.erl
@@ -0,0 +1,69 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_bench_SUITE).
+-author('[email protected]').
+-compile(export_all).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}].
+
+
+all() ->
+ [{group,tpcb}].
+
+groups() ->
+ [{tpcb,[{repeat,2}],[tpcb_conflict_ramcopies,
+ tpcb_conflict_disk_only_copies]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ Config.
+
+init_per_testcase(_Func, Conf) ->
+ Conf.
+
+end_per_testcase(_Func, _Conf) ->
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+tpcb_conflict_ramcopies(_Config) ->
+ mnesia_tpcb:conflict_benchmark(ram_copies).
+
+tpcb_conflict_disk_only_copies(_Config) ->
+ mnesia_tpcb:conflict_benchmark(disc_only_copies).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+
+
+
diff --git a/lib/mnesia/test/mnesia_consistency_test.erl b/lib/mnesia/test/mnesia_consistency_test.erl
index bb21723e27..922b89ec2b 100644
--- a/lib/mnesia/test/mnesia_consistency_test.erl
+++ b/lib/mnesia/test/mnesia_consistency_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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/lib/mnesia/test/mnesia_dirty_access_test.erl b/lib/mnesia/test/mnesia_dirty_access_test.erl
index abbdab48c0..6017092095 100644
--- a/lib/mnesia/test/mnesia_dirty_access_test.erl
+++ b/lib/mnesia/test/mnesia_dirty_access_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -527,6 +527,9 @@ dirty_index_update_bag(Config, Storage) ->
?match(ok, mnesia:dirty_write(Rec1)),
?match([Rec1], mnesia:dirty_index_read(Tab, 2, ValPos)),
+ ?match(ok, mnesia:dirty_delete_object(Rec5)),
+ ?match([Rec1], mnesia:dirty_index_read(Tab, 2, ValPos)),
+
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec2) end)),
R1 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec1, Rec2], lists:sort(R1)),
diff --git a/lib/mnesia/test/mnesia_recovery_test.erl b/lib/mnesia/test/mnesia_recovery_test.erl
index 625e6e824c..c4910a4b11 100644
--- a/lib/mnesia/test/mnesia_recovery_test.erl
+++ b/lib/mnesia/test/mnesia_recovery_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -99,21 +99,21 @@ groups() ->
async_dirty_post_kill_coord_node,
async_dirty_post_kill_coord_pid]},
{asym_trans, [],
- [asym_trans_kill_part_ask,
- asym_trans_kill_part_commit_vote,
- asym_trans_kill_part_pre_commit,
- asym_trans_kill_part_log_commit,
- asym_trans_kill_part_do_commit,
- asym_trans_kill_coord_got_votes,
- asym_trans_kill_coord_pid_got_votes,
- asym_trans_kill_coord_log_commit_rec,
- asym_trans_kill_coord_pid_log_commit_rec,
- asym_trans_kill_coord_log_commit_dec,
- asym_trans_kill_coord_pid_log_commit_dec,
- asym_trans_kill_coord_rec_acc_pre_commit_log_commit,
- asym_trans_kill_coord_pid_rec_acc_pre_commit_log_commit,
- asym_trans_kill_coord_rec_acc_pre_commit_done_commit,
- asym_trans_kill_coord_pid_rec_acc_pre_commit_done_commit]},
+ [asymtrans_part_ask,
+ asymtrans_part_commit_vote,
+ asymtrans_part_pre_commit,
+ asymtrans_part_log_commit,
+ asymtrans_part_do_commit,
+ asymtrans_coord_got_votes,
+ asymtrans_coord_pid_got_votes,
+ asymtrans_coord_log_commit_rec,
+ asymtrans_coord_pid_log_commit_rec,
+ asymtrans_coord_log_commit_dec,
+ asymtrans_coord_pid_log_commit_dec,
+ asymtrans_coord_rec_acc_pre_commit_log_commit,
+ asymtrans_coord_pid_rec_acc_pre_commit_log_commit,
+ asymtrans_coord_rec_acc_pre_commit_done_commit,
+ asymtrans_coord_pid_rec_acc_pre_commit_done_commit]},
{after_corrupt_files, [],
[after_corrupt_files_decision_log_head,
after_corrupt_files_decision_log_tail,
@@ -978,8 +978,8 @@ do_async_dirty([Tab], _Fahter) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-asym_trans_kill_part_ask(suite) -> [];
-asym_trans_kill_part_ask(Config) when is_list(Config) ->
+asymtrans_part_ask(suite) -> [];
+asymtrans_part_ask(Config) when is_list(Config) ->
?is_debug_compiled,
Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
[Coord, Part1, Part2] = Nodes,
@@ -989,8 +989,8 @@ asym_trans_kill_part_ask(Config) when is_list(Config) ->
kill_after_debug_point(Part1, {Part1, {mnesia_tm, doit_ask_commit}},
TransFun, [Tab1, Tab2], Nodes).
-asym_trans_kill_part_commit_vote(suite) -> [];
-asym_trans_kill_part_commit_vote(Config) when is_list(Config) ->
+asymtrans_part_commit_vote(suite) -> [];
+asymtrans_part_commit_vote(Config) when is_list(Config) ->
?is_debug_compiled,
Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
[Coord, Part1, Part2] = Nodes,
@@ -1000,8 +1000,8 @@ asym_trans_kill_part_commit_vote(Config) when is_list(Config) ->
kill_after_debug_point(Part1, {Part1, {mnesia_tm, commit_participant, vote_yes}},
TransFun, [Tab1, Tab2], Nodes).
-asym_trans_kill_part_pre_commit(suite) -> [];
-asym_trans_kill_part_pre_commit(Config) when is_list(Config) ->
+asymtrans_part_pre_commit(suite) -> [];
+asymtrans_part_pre_commit(Config) when is_list(Config) ->
?is_debug_compiled,
Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
[Coord, Part1, Part2] = Nodes,
@@ -1011,8 +1011,8 @@ asym_trans_kill_part_pre_commit(Config) when is_list(Config) ->
kill_after_debug_point(Part1, {Part1, {mnesia_tm, commit_participant, pre_commit}},
TransFun, [Tab1, Tab2], Nodes).
-asym_trans_kill_part_log_commit(suite) -> [];
-asym_trans_kill_part_log_commit(Config) when is_list(Config) ->
+asymtrans_part_log_commit(suite) -> [];
+asymtrans_part_log_commit(Config) when is_list(Config) ->
?is_debug_compiled,
Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
[Coord, Part1, Part2] = Nodes,
@@ -1022,8 +1022,8 @@ asym_trans_kill_part_log_commit(Config) when is_list(Config) ->
kill_after_debug_point(Part1, {Part1, {mnesia_tm, commit_participant, log_commit}},
TransFun, [Tab1, Tab2], Nodes).
-asym_trans_kill_part_do_commit(suite) -> [];
-asym_trans_kill_part_do_commit(Config) when is_list(Config) ->
+asymtrans_part_do_commit(suite) -> [];
+asymtrans_part_do_commit(Config) when is_list(Config) ->
?is_debug_compiled,
Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
[Coord, Part1, Part2] = Nodes,
@@ -1033,8 +1033,8 @@ asym_trans_kill_part_do_commit(Config) when is_list(Config) ->
kill_after_debug_point(Part1, {Part1, {mnesia_tm, commit_participant, do_commit}},
TransFun, [Tab1, Tab2], Nodes).
-asym_trans_kill_coord_got_votes(suite) -> [];
-asym_trans_kill_coord_got_votes(Config) when is_list(Config) ->
+asymtrans_coord_got_votes(suite) -> [];
+asymtrans_coord_got_votes(Config) when is_list(Config) ->
?is_debug_compiled,
Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
[Coord, Part1, Part2] = Nodes,
@@ -1044,8 +1044,8 @@ asym_trans_kill_coord_got_votes(Config) when is_list(Config) ->
kill_after_debug_point(Coord, {Coord, {mnesia_tm, multi_commit_asym_got_votes}},
TransFun, [Tab1, Tab2], Nodes).
-asym_trans_kill_coord_pid_got_votes(suite) -> [];
-asym_trans_kill_coord_pid_got_votes(Config) when is_list(Config) ->
+asymtrans_coord_pid_got_votes(suite) -> [];
+asymtrans_coord_pid_got_votes(Config) when is_list(Config) ->
?is_debug_compiled,
Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
[Coord, Part1, Part2] = Nodes,
@@ -1055,8 +1055,8 @@ asym_trans_kill_coord_pid_got_votes(Config) when is_list(Config) ->
kill_after_debug_point(coord_pid, {Coord, {mnesia_tm, multi_commit_asym_got_votes}},
TransFun, [Tab1, Tab2], Nodes).
-asym_trans_kill_coord_log_commit_rec(suite) -> [];
-asym_trans_kill_coord_log_commit_rec(Config) when is_list(Config) ->
+asymtrans_coord_log_commit_rec(suite) -> [];
+asymtrans_coord_log_commit_rec(Config) when is_list(Config) ->
?is_debug_compiled,
Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
[Coord, Part1, Part2] = Nodes,
@@ -1066,8 +1066,8 @@ asym_trans_kill_coord_log_commit_rec(Config) when is_list(Config) ->
kill_after_debug_point(Coord, {Coord, {mnesia_tm, multi_commit_asym_log_commit_rec}},
TransFun, [Tab1, Tab2], Nodes).
-asym_trans_kill_coord_pid_log_commit_rec(suite) -> [];
-asym_trans_kill_coord_pid_log_commit_rec(Config) when is_list(Config) ->
+asymtrans_coord_pid_log_commit_rec(suite) -> [];
+asymtrans_coord_pid_log_commit_rec(Config) when is_list(Config) ->
?is_debug_compiled,
Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
[Coord, Part1, Part2] = Nodes,
@@ -1077,8 +1077,8 @@ asym_trans_kill_coord_pid_log_commit_rec(Config) when is_list(Config) ->
kill_after_debug_point(coord_pid, {Coord, {mnesia_tm, multi_commit_asym_log_commit_rec}},
TransFun, [Tab1, Tab2], Nodes).
-asym_trans_kill_coord_log_commit_dec(suite) -> [];
-asym_trans_kill_coord_log_commit_dec(Config) when is_list(Config) ->
+asymtrans_coord_log_commit_dec(suite) -> [];
+asymtrans_coord_log_commit_dec(Config) when is_list(Config) ->
?is_debug_compiled,
Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
[Coord, Part1, Part2] = Nodes,
@@ -1088,8 +1088,8 @@ asym_trans_kill_coord_log_commit_dec(Config) when is_list(Config) ->
kill_after_debug_point(Coord, {Coord, {mnesia_tm, multi_commit_asym_log_commit_dec}},
TransFun, [Tab1, Tab2], Nodes).
-asym_trans_kill_coord_pid_log_commit_dec(suite) -> [];
-asym_trans_kill_coord_pid_log_commit_dec(Config) when is_list(Config) ->
+asymtrans_coord_pid_log_commit_dec(suite) -> [];
+asymtrans_coord_pid_log_commit_dec(Config) when is_list(Config) ->
?is_debug_compiled,
Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
[Coord, Part1, Part2] = Nodes,
@@ -1099,8 +1099,8 @@ asym_trans_kill_coord_pid_log_commit_dec(Config) when is_list(Config) ->
kill_after_debug_point(coord_pid, {Coord, {mnesia_tm, multi_commit_asym_log_commit_dec}},
TransFun, [Tab1, Tab2], Nodes).
-asym_trans_kill_coord_rec_acc_pre_commit_log_commit(suite) -> [];
-asym_trans_kill_coord_rec_acc_pre_commit_log_commit(Config) when is_list(Config) ->
+asymtrans_coord_rec_acc_pre_commit_log_commit(suite) -> [];
+asymtrans_coord_rec_acc_pre_commit_log_commit(Config) when is_list(Config) ->
?is_debug_compiled,
Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
[Coord, Part1, Part2] = Nodes,
@@ -1110,8 +1110,8 @@ asym_trans_kill_coord_rec_acc_pre_commit_log_commit(Config) when is_list(Config)
kill_after_debug_point(Coord, {Coord, {mnesia_tm, rec_acc_pre_commit_log_commit}},
TransFun, [Tab1, Tab2], Nodes).
-asym_trans_kill_coord_pid_rec_acc_pre_commit_log_commit(suite) -> [];
-asym_trans_kill_coord_pid_rec_acc_pre_commit_log_commit(Config) when is_list(Config) ->
+asymtrans_coord_pid_rec_acc_pre_commit_log_commit(suite) -> [];
+asymtrans_coord_pid_rec_acc_pre_commit_log_commit(Config) when is_list(Config) ->
?is_debug_compiled,
Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
[Coord, Part1, Part2] = Nodes,
@@ -1121,8 +1121,8 @@ asym_trans_kill_coord_pid_rec_acc_pre_commit_log_commit(Config) when is_list(Con
kill_after_debug_point(coord_pid, {Coord, {mnesia_tm, rec_acc_pre_commit_log_commit}},
TransFun, [Tab1, Tab2], Nodes).
-asym_trans_kill_coord_rec_acc_pre_commit_done_commit(suite) -> [];
-asym_trans_kill_coord_rec_acc_pre_commit_done_commit(Config) when is_list(Config) ->
+asymtrans_coord_rec_acc_pre_commit_done_commit(suite) -> [];
+asymtrans_coord_rec_acc_pre_commit_done_commit(Config) when is_list(Config) ->
?is_debug_compiled,
Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
[Coord, Part1, Part2] = Nodes,
@@ -1132,8 +1132,8 @@ asym_trans_kill_coord_rec_acc_pre_commit_done_commit(Config) when is_list(Config
kill_after_debug_point(Coord, {Coord, {mnesia_tm, rec_acc_pre_commit_done_commit}},
TransFun, [Tab1, Tab2], Nodes).
-asym_trans_kill_coord_pid_rec_acc_pre_commit_done_commit(suite) -> [];
-asym_trans_kill_coord_pid_rec_acc_pre_commit_done_commit(Config) when is_list(Config) ->
+asymtrans_coord_pid_rec_acc_pre_commit_done_commit(suite) -> [];
+asymtrans_coord_pid_rec_acc_pre_commit_done_commit(Config) when is_list(Config) ->
?is_debug_compiled,
Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
[Coord, Part1, Part2] = Nodes,
diff --git a/lib/mnesia/test/mnesia_test_lib.erl b/lib/mnesia/test/mnesia_test_lib.erl
index ba5bf84e24..57cbc61495 100644
--- a/lib/mnesia/test/mnesia_test_lib.erl
+++ b/lib/mnesia/test/mnesia_test_lib.erl
@@ -272,25 +272,13 @@ slave_start_link(Host, Name, Retries) ->
end.
starter(Host, Name, Args) ->
- case os:type() of
- vxworks ->
- X = test_server:start_node(Name, slave, [{args,Args}]),
- timer:sleep(5000),
- X;
- _ ->
- slave:start(Host, Name, Args)
- end.
+ slave:start(Host, Name, Args).
slave_sup() ->
process_flag(trap_exit, true),
receive
{'EXIT', _, _} ->
- case os:type() of
- vxworks ->
- erlang:halt();
- _ ->
- ignore
- end
+ ignore
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/mnesia/test/mnesia_trans_access_test.erl b/lib/mnesia/test/mnesia_trans_access_test.erl
index c040d0ca3f..157e441b27 100644
--- a/lib/mnesia/test/mnesia_trans_access_test.erl
+++ b/lib/mnesia/test/mnesia_trans_access_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -896,6 +896,10 @@ index_update_bag(Config)when is_list(Config) ->
?match({atomic, [Rec1]},
mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec5) end)),
+ ?match({atomic, [Rec1]},
+ mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
+
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(Rec2) end)),
{atomic, R1} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk
index da292cbb43..6d5df448c9 100644
--- a/lib/mnesia/vsn.mk
+++ b/lib/mnesia/vsn.mk
@@ -1 +1 @@
-MNESIA_VSN = 4.7
+MNESIA_VSN = 4.7.1
diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml
index a9554a08f4..ea665eee88 100644
--- a/lib/observer/doc/src/notes.xml
+++ b/lib/observer/doc/src/notes.xml
@@ -31,6 +31,75 @@
<p>This document describes the changes made to the Observer
application.</p>
+<section><title>Observer 1.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The module name in the link to the detail page for each
+ loaded module was earlier not URL encoded. If the module
+ name contained e.g. a # this could cause the crashdump
+ viewer to crash when opening the link. This has been
+ corrected.</p>
+ <p>
+ Own Id: OTP-10090 Aux Id: seq12068 </p>
+ </item>
+ <item>
+ <p>
+ Escape control characters in Table Viewer</p>
+ <p>
+ Similar behaviour to old tv. Objects in tables supposed
+ to be printed in a single line and it looks ugly when a
+ [...,10,...] integer list creates a new-line. Fix Table
+ Viewer search crash on new|changed|deleted rows.</p>
+ <p>
+ Fix Table Viewer crash after a 'Found' -&gt; 'Not found'
+ search sequence</p>
+ <p>
+ Start position was lost after a 'Found' -&gt; 'Not found'
+ search sequence leading an undefined position in the next
+ search. Thanks to Peti G�mori</p>
+ <p>
+ Own Id: OTP-10218</p>
+ </item>
+ <item>
+ <p>
+ observer: fix app file (Noticed-by: Motiejus Jakstys)</p>
+ <p>
+ Add missing observer modules to observer.app.src. Thanks
+ to Tuncer Ayaz.</p>
+ <p>
+ Own Id: OTP-10221</p>
+ </item>
+ <item>
+ <p>
+ Make Table Viewer search a bit faster</p>
+ <p>
+ Edit table row in a multiline text dialog. Thanks to Peti
+ Gomori.</p>
+ <p>
+ Own Id: OTP-10225</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Allow tracing on bifs.</p> <p>Ask epmd for local
+ nodes, and remember users last input in connect.</p>
+ <p>Fix crashes when a table or process information could
+ not be retrieved.</p>
+ <p>
+ Own Id: OTP-10075</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Observer 1.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/observer/src/observer.app.src b/lib/observer/src/observer.app.src
index 5c65ea5c8f..e881cb3c97 100644
--- a/lib/observer/src/observer.app.src
+++ b/lib/observer/src/observer.app.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2012. 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
@@ -25,6 +25,18 @@
etop_gui,
etop_tr,
etop_txt,
+ observer,
+ observer_app_wx,
+ observer_lib,
+ observer_perf_wx,
+ observer_pro_wx,
+ observer_procinfo,
+ observer_sys_wx,
+ observer_trace_wx,
+ observer_traceoptions_wx,
+ observer_tv_table,
+ observer_tv_wx,
+ observer_wx,
ttb,
ttb_et]},
{registered, []},
diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl
index f9be11e05a..380532e90c 100644
--- a/lib/observer/src/observer_app_wx.erl
+++ b/lib/observer/src/observer_app_wx.erl
@@ -267,9 +267,15 @@ handle_call(Event, From, _State) ->
handle_cast(Event, _State) ->
error({unhandled_cast, Event}).
%%%%%%%%%%
-handle_info({active, Node}, State = #state{parent=Parent, current=Curr}) ->
+handle_info({active, Node}, State = #state{parent=Parent, current=Curr, appmon=Appmon}) ->
create_menus(Parent, []),
- {ok, Pid} = appmon_info:start_link(Node, self(), []),
+ Pid = try
+ Node = node(Appmon),
+ Appmon
+ catch _:_ ->
+ {ok, P} = appmon_info:start_link(Node, self(), []),
+ P
+ end,
appmon_info:app_ctrl(Pid, Node, true, []),
(Curr =/= undefined) andalso appmon_info:app(Pid, Curr, true, []),
{noreply, State#state{appmon=Pid}};
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index 3b924d46cf..4077f8371a 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -19,7 +19,7 @@
-module(observer_lib).
-export([get_wx_parent/1,
- display_info_dialog/1, user_term/3,
+ display_info_dialog/1, user_term/3, user_term_multiline/3,
interval_dialog/4, start_timer/1, stop_timer/1,
display_info/2, fill_info/2, update_info/2, to_str/1,
create_menus/3, create_menu_item/3,
@@ -347,6 +347,58 @@ user_term(Parent, Title, Default) ->
cancel
end.
+user_term_multiline(Parent, Title, Default) ->
+ Dialog = wxDialog:new(Parent, ?wxID_ANY, Title,
+ [{style, ?wxDEFAULT_DIALOG_STYLE bor
+ ?wxRESIZE_BORDER}]),
+ Panel = wxPanel:new(Dialog),
+
+ TextCtrl = wxTextCtrl:new(Panel, ?wxID_ANY,
+ [{value, Default},
+ {style, ?wxDEFAULT bor ?wxTE_MULTILINE}]),
+ Line = wxStaticLine:new(Panel, [{style, ?wxLI_HORIZONTAL}]),
+
+ Buttons = wxDialog:createButtonSizer(Dialog, ?wxOK bor ?wxCANCEL),
+
+ InnerSizer = wxBoxSizer:new(?wxVERTICAL),
+ wxSizer:add(InnerSizer, TextCtrl,
+ [{flag, ?wxEXPAND bor ?wxALL},{proportion, 1},{border, 5}]),
+ wxSizer:add(InnerSizer, Line,
+ [{flag, ?wxEXPAND},{proportion, 0},{border, 5}]),
+ wxPanel:setSizer(Panel, InnerSizer),
+
+ TopSizer = wxBoxSizer:new(?wxVERTICAL),
+ wxSizer:add(TopSizer, Panel,
+ [{flag, ?wxEXPAND bor ?wxALL},{proportion, 1},{border, 5}]),
+ wxSizer:add(TopSizer, Buttons,
+ [{flag, ?wxEXPAND bor ?wxBOTTOM bor ?wxRIGHT},{border, 10}]),
+
+ % calculate the size of TopSizer when the whole user_term
+ % fits in the TextCtrl
+ DC = wxClientDC:new(Panel),
+ W = wxDC:getCharWidth(DC),
+ H = wxDC:getCharHeight(DC),
+ {EW, EH} = wxDC:getMultiLineTextExtent(DC, Default),
+ wxSizer:setItemMinSize(InnerSizer, 0, EW+2*W, EH+H),
+ TopSize = wxSizer:getMinSize(TopSizer),
+ % reset min size of TextCtrl to 40 chararacters * 4 lines
+ wxSizer:setItemMinSize(InnerSizer, 0, 40*W, 4*H),
+
+ wxWindow:setSizerAndFit(Dialog, TopSizer),
+ wxSizer:setSizeHints(TopSizer, Dialog),
+
+ wxWindow:setClientSize(Dialog, TopSize),
+
+ case wxDialog:showModal(Dialog) of
+ ?wxID_OK ->
+ Str = wxTextCtrl:getValue(TextCtrl),
+ wxDialog:destroy(Dialog),
+ parse_string(ensure_last_is_dot(Str));
+ ?wxID_CANCEL ->
+ wxDialog:destroy(Dialog),
+ cancel
+ end.
+
parse_string(Str) ->
try
Tokens = case erl_scan:string(Str) of
diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl
index fa867e12f6..abf90ac612 100644
--- a/lib/observer/src/observer_perf_wx.erl
+++ b/lib/observer/src/observer_perf_wx.erl
@@ -123,7 +123,7 @@ init([Notebook, Parent]) ->
}}
catch _:Err ->
io:format("~p crashed ~p: ~p~n",[?MODULE, Err, erlang:get_stacktrace()]),
- {error, Err}
+ {stop, Err}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl
index e2f3ddb02b..ee67664539 100644
--- a/lib/observer/src/observer_pro_wx.erl
+++ b/lib/observer/src/observer_pro_wx.erl
@@ -258,8 +258,7 @@ terminate(_Reason, #state{holder=Holder}) ->
ok.
code_change(_, _, State) ->
- {stop, not_yet_implemented, State}.
-
+ {ok, State}.
handle_call(Msg, _From, State) ->
io:format("~p:~p: Unhandled call ~p~n",[?MODULE, ?LINE, Msg]),
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index 13e41cfe33..45218c177b 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -127,17 +127,15 @@ terminate(_Reason, #state{parent=Parent,pid=Pid,frame=Frame}) ->
ok.
code_change(_, _, State) ->
- {stop, not_yet_implemented, State}.
+ {ok, State}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init_process_page(Panel, Pid) ->
Fields0 = process_info_fields(Pid),
{FPanel, _, UpFields} = observer_lib:display_info(Panel, Fields0),
- {FPanel, fun() -> case process_info_fields(Pid) of
- Fields when is_list(Fields) ->
- observer_lib:update_info(UpFields, Fields);
- _ -> ok
- end
+ {FPanel, fun() ->
+ Fields = process_info_fields(Pid),
+ observer_lib:update_info(UpFields, Fields)
end}.
init_text_page(Parent) ->
diff --git a/lib/observer/src/observer_sys_wx.erl b/lib/observer/src/observer_sys_wx.erl
index 09602bbd9e..f00a666a35 100644
--- a/lib/observer/src/observer_sys_wx.erl
+++ b/lib/observer/src/observer_sys_wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2012. 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
@@ -147,7 +147,7 @@ terminate(_Reason, _State) ->
ok.
code_change(_, _, State) ->
- {stop, not_yet_implemented, State}.
+ {ok, State}.
handle_call(Msg, _From, State) ->
io:format("~p~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]),
diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl
index d0b6a1e063..f2a1084f85 100644
--- a/lib/observer/src/observer_trace_wx.erl
+++ b/lib/observer/src/observer_trace_wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2012. 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
@@ -489,7 +489,7 @@ terminate(_Reason, #state{nodes=_Nodes}) ->
ok.
code_change(_, _, State) ->
- {stop, not_yet_implemented, State}.
+ {ok, State}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
do_add_patterns({Module, NewPs}, State=#state{tpatterns=TPs0, m_view=Mview, f_view=Fview}) ->
diff --git a/lib/observer/src/observer_tv_table.erl b/lib/observer/src/observer_tv_table.erl
index 3930f9ee26..c41f0f006a 100644
--- a/lib/observer/src/observer_tv_table.erl
+++ b/lib/observer/src/observer_tv_table.erl
@@ -24,6 +24,8 @@
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
handle_event/2, handle_sync_event/3, handle_cast/2]).
+-export([format/1]).
+
-include("observer_defs.hrl").
-import(observer_lib, [to_str/1]).
@@ -218,8 +220,8 @@ search_area(Parent) ->
search=TC1,goto=TC2,radio={Nbtn,Pbtn,Cbtn}}.
edit(Index, #state{pid=Pid, frame=Frame}) ->
- Str = get_row(Pid, Index, all),
- case observer_lib:user_term(Frame, "Edit object:", Str) of
+ Str = get_row(Pid, Index, all_multiline),
+ case observer_lib:user_term_multiline(Frame, "Edit object:", Str) of
cancel -> ok;
{ok, Term} -> Pid ! {edit, Index, Term};
Err = {error, _} -> self() ! Err
@@ -265,7 +267,8 @@ handle_event(#wx{id=?ID_DELETE},
wxStatusBar:setStatusText(StatusBar, io_lib:format("Deleted object: ~s",[Str])),
{noreply, State};
-handle_event(#wx{id=?wxID_CLOSE}, State) ->
+handle_event(#wx{id=?wxID_CLOSE}, State = #state{frame=Frame}) ->
+ wxFrame:destroy(Frame),
{stop, normal, State};
handle_event(Help = #wx{id=?wxID_HELP}, State) ->
@@ -321,7 +324,7 @@ handle_event(#wx{id=?SEARCH_ENTRY, event=#wxCommand{type=command_text_enter,cmdS
wxStatusBar:setStatusText(SB, "Not found"),
Pid ! {mark_search_hit, Find#find.start},
wxListCtrl:refreshItem(Grid, Find#find.start),
- {noreply, State#state{search=Search#search{find=#find{found=false}}}};
+ {noreply, State#state{search=Search#search{find=Find#find{found=false}}}};
Row ->
wxListCtrl:ensureVisible(Grid, Row),
wxListCtrl:refreshItem(Grid, Row),
@@ -594,7 +597,7 @@ keysort(Col, Table) ->
lists:sort(Sort, Table).
search([Str, Row, Dir0, CaseSens],
- S=#holder{parent=Parent, table=Table}) ->
+ S=#holder{parent=Parent, table=Table0}) ->
Opt = case CaseSens of
true -> [];
false -> [caseless]
@@ -605,29 +608,35 @@ search([Str, Row, Dir0, CaseSens],
end,
Res = case re:compile(Str, Opt) of
{ok, Re} ->
+ Table =
+ case Dir0 of
+ true ->
+ lists:nthtail(Row, Table0);
+ false ->
+ lists:reverse(lists:sublist(Table0, Row+1))
+ end,
search(Row, Dir, Re, Table);
{error, _} -> false
end,
Parent ! {self(), Res},
S#holder{search=Res}.
-search(Row, Dir, Re, Table) ->
- Res = try lists:nth(Row+1, Table) of
- Term ->
- Str = format(Term),
- re:run(Str, Re)
- catch _:_ -> no_more
- end,
+search(Row, Dir, Re, [ [Term|_] |Table]) ->
+ Str = format(Term),
+ Res = re:run(Str, Re),
case Res of
nomatch -> search(Row+Dir, Dir, Re, Table);
- no_more -> false;
{match,_} -> Row
- end.
+ end;
+search(_, _, _, []) ->
+ false.
get_row(From, Row, Col, Table) ->
case lists:nth(Row+1, Table) of
[Object|_] when Col =:= all ->
From ! {self(), format(Object)};
+ [Object|_] when Col =:= all_multiline ->
+ From ! {self(), io_lib:format("~p", [Object])};
[Object|_] when tuple_size(Object) >= Col ->
From ! {self(), format(element(Col, Object))};
_ ->
@@ -747,6 +756,13 @@ format(List) when is_list(List) ->
format_list(List);
format(Bin) when is_binary(Bin), byte_size(Bin) > 100 ->
io_lib:format("<<#Bin:~w>>", [byte_size(Bin)]);
+format(Bin) when is_binary(Bin) ->
+ try
+ true = printable_list(unicode:characters_to_list(Bin)),
+ io_lib:format("<<\"~ts\">>", [Bin])
+ catch _:_ ->
+ io_lib:format("~w", [Bin])
+ end;
format(Float) when is_float(Float) ->
io_lib:format("~.3g", [Float]);
format(Term) ->
@@ -762,7 +778,7 @@ format_tuple(_Tuple, 1, 0) ->
format_list([]) -> "[]";
format_list(List) ->
case printable_list(List) of
- true -> io_lib:format("\"~ts\"", [List]);
+ true -> io_lib:format("\"~ts\"", [map_printable_list(List)]);
false -> [$[ | make_list(List)]
end.
@@ -771,6 +787,24 @@ make_list([Last]) ->
make_list([Head|Tail]) ->
[format(Head), $,|make_list(Tail)].
+map_printable_list([$\n|Cs]) ->
+ [$\\, $n|map_printable_list(Cs)];
+map_printable_list([$\r|Cs]) ->
+ [$\\, $r|map_printable_list(Cs)];
+map_printable_list([$\t|Cs]) ->
+ [$\\, $t|map_printable_list(Cs)];
+map_printable_list([$\v|Cs]) ->
+ [$\\, $v|map_printable_list(Cs)];
+map_printable_list([$\b|Cs]) ->
+ [$\\, $b|map_printable_list(Cs)];
+map_printable_list([$\f|Cs]) ->
+ [$\\, $f|map_printable_list(Cs)];
+map_printable_list([$\e|Cs]) ->
+ [$\\, $e|map_printable_list(Cs)];
+map_printable_list([]) -> [];
+map_printable_list([C|Cs]) ->
+ [C|map_printable_list(Cs)].
+
%% printable_list([Char]) -> bool()
%% Return true if CharList is a list of printable characters, else
%% false.
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index ce3f48a05d..e433bea8c2 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -195,10 +195,13 @@ setup(#state{frame = Frame} = State) ->
%%Callbacks
handle_event(#wx{event=#wxNotebook{type=command_notebook_page_changing}},
#state{active_tab=Previous, node=Node} = State) ->
- Pid = get_active_pid(State),
- Previous ! not_active,
- Pid ! {active, Node},
- {noreply, State#state{active_tab=Pid}};
+ case get_active_pid(State) of
+ Previous -> {noreply, State};
+ Pid ->
+ Previous ! not_active,
+ Pid ! {active, Node},
+ {noreply, State#state{active_tab=Pid}}
+ end;
handle_event(#wx{event = #wxClose{}}, State) ->
{stop, normal, State};
@@ -350,7 +353,7 @@ terminate(_Reason, #state{frame = Frame}) ->
ok.
code_change(_, _, State) ->
- {stop, not_yet_implemented, State}.
+ {ok, State}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -410,7 +413,9 @@ connect2(NodeName, Opts, Cookie) ->
end.
change_node_view(Node, State) ->
- get_active_pid(State) ! {active, Node},
+ Tab = get_active_pid(State),
+ Tab ! not_active,
+ Tab ! {active, Node},
StatusText = ["Observer - " | atom_to_list(Node)],
wxFrame:setTitle(State#state.frame, StatusText),
wxStatusBar:setStatusText(State#state.status_bar, StatusText),
diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk
index 4eb10ae4e8..32e13004b6 100644
--- a/lib/observer/vsn.mk
+++ b/lib/observer/vsn.mk
@@ -1 +1 @@
-OBSERVER_VSN = 1.1
+OBSERVER_VSN = 1.2
diff --git a/lib/odbc/aclocal.m4 b/lib/odbc/aclocal.m4
index 339a15a2bb..b1cf1fe404 100644
--- a/lib/odbc/aclocal.m4
+++ b/lib/odbc/aclocal.m4
@@ -1,7 +1,7 @@
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1998-2011. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2012. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
@@ -59,6 +59,7 @@ AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only us
dnl Cross compilation variables
AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)])
@@ -606,6 +607,103 @@ ifelse([$5], , , [$5
fi
])
+dnl ----------------------------------------------------------------------
+dnl
+dnl AC_DOUBLE_MIDDLE_ENDIAN
+dnl
+dnl Checks whether doubles are represented in "middle-endian" format.
+dnl Sets ac_cv_double_middle_endian={no,yes,unknown} accordingly,
+dnl as well as DOUBLE_MIDDLE_ENDIAN.
+dnl
+dnl
+
+AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN],
+[AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian,
+[# It does not; compile a test program.
+AC_RUN_IFELSE(
+[AC_LANG_SOURCE([[#include <stdlib.h>
+
+int
+main(void)
+{
+ int i = 0;
+ int zero = 0;
+ int bigendian;
+ int zero_index = 0;
+
+ union
+ {
+ long int l;
+ char c[sizeof (long int)];
+ } u;
+
+ /* we'll use the one with 32-bit words */
+ union
+ {
+ double d;
+ unsigned int c[2];
+ } vint;
+
+ union
+ {
+ double d;
+ unsigned long c[2];
+ } vlong;
+
+ union
+ {
+ double d;
+ unsigned short c[2];
+ } vshort;
+
+
+ /* Are we little or big endian? From Harbison&Steele. */
+ u.l = 1;
+ bigendian = (u.c[sizeof (long int) - 1] == 1);
+
+ zero_index = bigendian ? 1 : 0;
+
+ vint.d = 1.0;
+ vlong.d = 1.0;
+ vshort.d = 1.0;
+
+ if (sizeof(unsigned int) == 4)
+ {
+ if (vint.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned long) == 4)
+ {
+ if (vlong.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned short) == 4)
+ {
+ if (vshort.c[zero_index] != 0)
+ zero = 1;
+ }
+
+ exit (zero);
+}
+]])],
+ [ac_cv_c_double_middle_endian=no],
+ [ac_cv_c_double_middle_endian=yes],
+ [ac_cv_c_double_middle=unknown])])
+case $ac_cv_c_double_middle_endian in
+ yes)
+ m4_default([$1],
+ [AC_DEFINE([DOUBLE_MIDDLE_ENDIAN], 1,
+ [Define to 1 if your processor stores the words in a double in
+ middle-endian format (like some ARMs).])]) ;;
+ no)
+ $2 ;;
+ *)
+ m4_default([$3],
+ [AC_MSG_WARN([unknown double endianness
+presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;;
+esac
+])# AC_C_DOUBLE_MIDDLE_ENDIAN
+
dnl ----------------------------------------------------------------------
dnl
@@ -1337,6 +1435,14 @@ if test "$ac_cv_c_bigendian" = "yes"; then
AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian])
fi
+case X$erl_xcomp_double_middle_endian in
+ X) ;;
+ Xyes|Xno|Xunknown) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;;
+ *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);;
+esac
+
+AC_C_DOUBLE_MIDDLE_ENDIAN
+
AC_ARG_ENABLE(native-ethr-impls,
AS_HELP_STRING([--disable-native-ethr-impls],
[disable native ethread implementations]),
diff --git a/lib/odbc/c_src/Makefile.in b/lib/odbc/c_src/Makefile.in
index 5b37b352fb..026da39e6f 100644
--- a/lib/odbc/c_src/Makefile.in
+++ b/lib/odbc/c_src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c
index ab2d7fe210..6d4460014f 100644
--- a/lib/odbc/c_src/odbcserver.c
+++ b/lib/odbc/c_src/odbcserver.c
@@ -176,7 +176,7 @@ static void encode_column_dyn(db_column column, int column_nr,
static void encode_data_type(SQLSMALLINT sql_type, SQLINTEGER size,
SQLSMALLINT decimal_digits, db_state *state);
static Boolean decode_params(db_state *state, byte *buffer, int *index, param_array **params,
- int i, int j);
+ int i, int j, int num_param_values);
/*------------- Erlang port communication functions ----------------------*/
@@ -212,6 +212,7 @@ static db_column * alloc_column_buffer(int n);
static void free_column_buffer(db_column **columns, int n);
static void free_params(param_array **params, int cols);
static void clean_state(db_state *state);
+static SQLLEN* alloc_strlen_indptr(int n, int val);
/* ------------- Init/map/bind/retrive functions -------------------------*/
@@ -1157,7 +1158,7 @@ static db_result_msg encode_out_params(db_state *state,
break;
case SQL_C_BIT:
ei_x_encode_atom(&dynamic_buffer(state),
- ((Boolean*)values)[j]==TRUE?"true":"false");
+ ((byte*)values)[j]==TRUE?"true":"false");
break;
default:
ei_x_encode_atom(&dynamic_buffer(state), "error");
@@ -1579,37 +1580,48 @@ static void encode_data_type(SQLSMALLINT sql_type, SQLINTEGER size,
}
static Boolean decode_params(db_state *state, byte *buffer, int *index, param_array **params,
- int i, int j)
+ int i, int j, int num_param_values)
{
int erl_type, size;
long bin_size, l64;
long val;
param_array* param;
TIMESTAMP_STRUCT* ts;
+ char atomarray[MAXATOMLEN+1];
ei_get_type(buffer, index, &erl_type, &size);
param = &(*params)[i];
+ if(erl_type == ERL_ATOM_EXT) {
+ ei_decode_atom(buffer, index, atomarray);
+ if(strncmp(atomarray, "null", 4) == 0 ) {
+ param->offset += param->type.len;
+
+ if(!param->type.strlen_or_indptr_array)
+ param->type.strlen_or_indptr_array = alloc_strlen_indptr(num_param_values, param->type.len);
+
+ param->type.strlen_or_indptr_array[j] = SQL_NULL_DATA;
+ return TRUE;
+ }
+ }
+
switch (param->type.c) {
case SQL_C_CHAR:
if (binary_strings(state)) {
ei_decode_binary(buffer, index,
&(param->values.string[param->offset]), &bin_size);
param->offset += param->type.len;
- param->type.strlen_or_indptr_array[j] = SQL_NTS;
} else {
if(erl_type != ERL_STRING_EXT) {
return FALSE;
}
ei_decode_string(buffer, index, &(param->values.string[param->offset]));
param->offset += param->type.len;
- param->type.strlen_or_indptr_array[j] = SQL_NTS;
}
break;
case SQL_C_WCHAR:
ei_decode_binary(buffer, index, &(param->values.string[param->offset]), &bin_size);
param->offset += param->type.len;
- param->type.strlen_or_indptr_array[j] = SQL_NTS;
break;
case SQL_C_TYPE_TIMESTAMP:
ts = (TIMESTAMP_STRUCT*) param->values.string;
@@ -1661,9 +1673,13 @@ static Boolean decode_params(db_state *state, byte *buffer, int *index, param_ar
if((erl_type != ERL_ATOM_EXT)) {
return FALSE;
}
- ei_decode_boolean(buffer, index, &(param->values.bool[j]));
+ if (strncmp((char*)atomarray,"true",4) == 0)
+ param->values.bool[j] = TRUE;
+ else if (strncmp((char*)atomarray,"false",5) == 0)
+ param->values.bool[j] = FALSE;
+ else
+ return -1;
break;
-
default:
return FALSE;
}
@@ -2014,6 +2030,18 @@ static void clean_state(db_state *state)
nr_of_columns(state) = 0;
}
+/* Allocates and fill with default value StrLen_or_IndPtr array */
+static SQLLEN* alloc_strlen_indptr(int n, int val)
+{
+ int i;
+ SQLLEN* arr = (SQLLEN*)safe_malloc(n * sizeof(SQLLEN));
+
+ for( i=0; i < n; ++i )
+ arr[i] = val;
+
+ return arr;
+}
+
/* ------------- Init/map/bind/retrive functions ------------------------*/
/* Prepare the state for a connection */
@@ -2118,7 +2146,7 @@ static void init_param_column(param_array *params, byte *buffer, int *index,
(double *)safe_malloc(num_param_values * params->type.len);
} else if(params->type.c == SQL_C_CHAR) {
params->type.strlen_or_indptr_array
- = (SQLLEN*)safe_malloc(num_param_values * sizeof(SQLINTEGER));
+ = alloc_strlen_indptr(num_param_values, SQL_NTS);
params->values.string =
(byte *)safe_malloc(num_param_values *
sizeof(byte)* params->type.len);
@@ -2136,8 +2164,8 @@ static void init_param_column(param_array *params, byte *buffer, int *index,
params->type.len = length+1;
params->type.c = SQL_C_CHAR;
params->type.col_size = (SQLUINTEGER)length;
- params->type.strlen_or_indptr_array =
- (SQLLEN*)safe_malloc(num_param_values * sizeof(SQLINTEGER));
+ params->type.strlen_or_indptr_array
+ = alloc_strlen_indptr(num_param_values, SQL_NTS);
params->values.string =
(byte *)safe_malloc(num_param_values *
sizeof(byte)* params->type.len);
@@ -2159,8 +2187,8 @@ static void init_param_column(param_array *params, byte *buffer, int *index,
params->type.len = (length+1)*sizeof(SQLWCHAR);
params->type.c = SQL_C_WCHAR;
params->type.col_size = (SQLUINTEGER)length;
- params->type.strlen_or_indptr_array =
- (SQLLEN*)safe_malloc(num_param_values * sizeof(SQLINTEGER));
+ params->type.strlen_or_indptr_array
+ = alloc_strlen_indptr(num_param_values, SQL_NTS);
params->values.string =
(byte *)safe_malloc(num_param_values * sizeof(byte) * params->type.len);
@@ -2201,10 +2229,10 @@ static void init_param_column(param_array *params, byte *buffer, int *index,
case USER_BOOLEAN:
params->type.sql = SQL_BIT;
params->type.c = SQL_C_BIT;
- params->type.len = sizeof(Boolean);
+ params->type.len = sizeof(byte);
params->type.col_size = params->type.len;
params->values.bool =
- (Boolean *)safe_malloc(num_param_values * params->type.len);
+ (byte *)safe_malloc(num_param_values * params->type.len);
break;
}
params->offset = 0;
@@ -2411,7 +2439,7 @@ static param_array * bind_parameter_arrays(byte *buffer, int *index,
}
for (j = 0; j < num_param_values; j++) {
- if(!decode_params(state, buffer, index, &params, i, j)) {
+ if(!decode_params(state, buffer, index, &params, i, j, num_param_values)) {
/* An input parameter was not of the expected type */
free_params(&params, i);
return params;
diff --git a/lib/odbc/c_src/odbcserver.h b/lib/odbc/c_src/odbcserver.h
index 56b6148777..a76cedf1af 100644
--- a/lib/odbc/c_src/odbcserver.h
+++ b/lib/odbc/c_src/odbcserver.h
@@ -156,7 +156,7 @@ typedef struct {
byte *string;
SQLINTEGER *integer;
double *floating;
- Boolean *bool;
+ byte *bool;
}values;
} param_array;
diff --git a/lib/odbc/doc/src/Makefile b/lib/odbc/doc/src/Makefile
index cd9bca0a55..53a855f182 100644
--- a/lib/odbc/doc/src/Makefile
+++ b/lib/odbc/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/odbc/doc/src/notes.xml b/lib/odbc/doc/src/notes.xml
index 08763163b8..5f6cf91961 100644
--- a/lib/odbc/doc/src/notes.xml
+++ b/lib/odbc/doc/src/notes.xml
@@ -31,7 +31,27 @@
<p>This document describes the changes made to the odbc application.
</p>
- <section><title>ODBC 2.10.12</title>
+ <section><title>ODBC 2.10.13</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add support for NULL value in odbc:param_query</p>
+ <p>
+ Support atom 'null' in odbc:param_query as database NULL
+ value Fix "ODBC: received unexpected info:{tcp_closed,
+ ...}" when connection is terminated. Fix possible access
+ violation with 64bit ODBC. Thanks to Maxim Zrazhevskiy</p>
+ <p>
+ Own Id: OTP-10206</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>ODBC 2.10.12</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/odbc/src/Makefile b/lib/odbc/src/Makefile
index 7ca82d784b..2af65cc757 100644
--- a/lib/odbc/src/Makefile
+++ b/lib/odbc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/odbc/src/odbc.appup.src b/lib/odbc/src/odbc.appup.src
index 853323da09..c7c83ea079 100644
--- a/lib/odbc/src/odbc.appup.src
+++ b/lib/odbc/src/odbc.appup.src
@@ -1,12 +1,8 @@
%% -*- erlang -*-
{"%VSN%",
[
- {"2.10.11", [{restart_application, odbc}]},
- {"2.10.10", [{restart_application, odbc}]},
- {"2.10.9", [{restart_application, odbc}]}
+ {<<"2\\.*">>, [{restart_application, odbc}]}
],
[
- {"2.10.11", [{restart_application, odbc}]},
- {"2.10.10", [{restart_application, odbc}]},
- {"2.10.9", [{restart_application, odbc}]}
+ {<<"2\\.*">>, [{restart_application, odbc}]}
]}.
diff --git a/lib/odbc/src/odbc.erl b/lib/odbc/src/odbc.erl
index 9f7b06dcf1..16fdb4aabd 100644
--- a/lib/odbc/src/odbc.erl
+++ b/lib/odbc/src/odbc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. 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
@@ -755,7 +755,10 @@ handle_info({'DOWN', _Ref, _Type, _Process, shutdown}, State) ->
handle_info({'DOWN', _Ref, _Type, Process, Reason}, State) ->
{stop, {stopped, {'EXIT', Process, Reason}},
State#state{reply_to = undefined}};
-
+
+handle_info({tcp_closed, Socket}, State = #state{odbc_socket=Socket,
+ state = disconnecting}) ->
+ {stop, normal, State};
%---------------------------------------------------------------------------
%% Catch all - throws away unknown messages (This could happen by "accident"
%% so we do not want to crash, but we make a log entry as it is an
@@ -942,9 +945,11 @@ fix_params({sql_bit, InOut, Values}) ->
fix_params({'sql_timestamp', InOut, Values}) ->
NewValues =
case (catch
- lists:map(fun({{Year,Month,Day},{Hour,Minute,Second}}) ->
- {Year,Month,Day,Hour,Minute,Second}
- end, Values)) of
+ lists:map(
+ fun({{Year,Month,Day},{Hour,Minute,Second}}) ->
+ {Year,Month,Day,Hour,Minute,Second};
+ (null) -> null
+ end, Values)) of
Result ->
Result
end,
@@ -960,15 +965,15 @@ fix_inout(out) ->
fix_inout(inout) ->
?INOUT.
-string_terminate([Value| _ ] = Values) when is_list(Value)->
- case (catch
- lists:map(fun(Str) -> Str ++ [?STR_TERMINATOR] end, Values)) of
- Result ->
- Result
- end;
-string_terminate([Value| _ ] = Values) when is_binary(Value)->
- case (catch
- lists:map(fun(B) -> <<B/binary,0:16>> end, Values)) of
+string_terminate(Values) ->
+ case (catch lists:map(fun string_terminate_value/1, Values)) of
Result ->
Result
end.
+
+string_terminate_value(String) when is_list(String) ->
+ String ++ [?STR_TERMINATOR];
+string_terminate_value(Binary) when is_binary(Binary) ->
+ <<Binary/binary,0:16>>;
+string_terminate_value(null) ->
+ null.
diff --git a/lib/odbc/test/Makefile b/lib/odbc/test/Makefile
index 84551f0eb1..d45073281b 100644
--- a/lib/odbc/test/Makefile
+++ b/lib/odbc/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/odbc/test/odbc_test_lib.erl b/lib/odbc/test/odbc_test_lib.erl
index a8439d5fb6..e814cd2aca 100644
--- a/lib/odbc/test/odbc_test_lib.erl
+++ b/lib/odbc/test/odbc_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2012. 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
@@ -29,6 +29,7 @@
unique_table_name() ->
lists:reverse(lists:foldl(fun($@, Acc) -> [$t, $A |Acc] ;
+ ($-,Acc) -> Acc;
(X, Acc) -> [X |Acc] end,
[], atom_to_list(node()))).
diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk
index fb6e208a52..3bb2fe5bce 100644
--- a/lib/odbc/vsn.mk
+++ b/lib/odbc/vsn.mk
@@ -1 +1 @@
-ODBC_VSN = 2.10.12
+ODBC_VSN = 2.10.13
diff --git a/lib/orber/COSS/CosNaming/Makefile b/lib/orber/COSS/CosNaming/Makefile
index a61fcd08a6..769c08a9e9 100644
--- a/lib/orber/COSS/CosNaming/Makefile
+++ b/lib/orber/COSS/CosNaming/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/orber/c_src/Makefile.in b/lib/orber/c_src/Makefile.in
index 5953d41ff3..9970642a9f 100644
--- a/lib/orber/c_src/Makefile.in
+++ b/lib/orber/c_src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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
@@ -58,15 +58,9 @@ ifeq ($(findstring win32,$(TARGET)),win32)
orber:
echo "Nothing to build on NT"
else
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-orber:
- echo "Nothing to build for VxWorks"
-
-else
orber:
echo "Nothing to build"
endif
-endif
clean:
@@ -94,20 +88,12 @@ release_spec: opt
$(INSTALL_PROGRAM) $(CC_FILES) "$(RELSYSDIR)/priv/src"
$(INSTALL_PROGRAM) $(HH_FILES) "$(RELSYSDIR)/priv/include"
else
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-release_spec:
- $(INSTALL_DIR) "$(RELSYSDIR)/priv/src"
- $(INSTALL_DIR) "$(RELSYSDIR)/priv/include"
- $(INSTALL_PROGRAM) $(CC_FILES) "$(RELSYSDIR)/priv/src"
- $(INSTALL_PROGRAM) $(HH_FILES) "$(RELSYSDIR)/priv/include"
-else
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/priv/src"
$(INSTALL_DIR) "$(RELSYSDIR)/priv/include"
$(INSTALL_DATA) $(CC_FILES) "$(RELSYSDIR)/priv/src"
$(INSTALL_DATA) $(HH_FILES) "$(RELSYSDIR)/priv/include"
endif
-endif
release_docs_spec:
diff --git a/lib/orber/doc/src/Makefile b/lib/orber/doc/src/Makefile
index c429c1d80b..4cd7c45437 100644
--- a/lib/orber/doc/src/Makefile
+++ b/lib/orber/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/orber/doc/src/notes.xml b/lib/orber/doc/src/notes.xml
index 72cd808d20..d561199998 100644
--- a/lib/orber/doc/src/notes.xml
+++ b/lib/orber/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2011</year>
+ <year>1997</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/orber/examples/Stack/Makefile b/lib/orber/examples/Stack/Makefile
index b03d7c3b86..f1a5106a7b 100644
--- a/lib/orber/examples/Stack/Makefile
+++ b/lib/orber/examples/Stack/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/orber/java_src/Orber/Makefile b/lib/orber/java_src/Orber/Makefile
index 4e52ed3635..f7f7336622 100644
--- a/lib/orber/java_src/Orber/Makefile
+++ b/lib/orber/java_src/Orber/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/orber/priv/Makefile b/lib/orber/priv/Makefile
index 7957f7bbf0..cd361e3f0a 100644
--- a/lib/orber/priv/Makefile
+++ b/lib/orber/priv/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. 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/lib/orber/src/Makefile b/lib/orber/src/Makefile
index a57d6d948b..72610def2b 100644
--- a/lib/orber/src/Makefile
+++ b/lib/orber/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/orber/test/Makefile b/lib/orber/test/Makefile
index 8eef1934a4..2f8777c773 100644
--- a/lib/orber/test/Makefile
+++ b/lib/orber/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/orber/test/csiv2_SUITE.erl b/lib/orber/test/csiv2_SUITE.erl
index 60ffa1eb09..b89bf0a56c 100644
--- a/lib/orber/test/csiv2_SUITE.erl
+++ b/lib/orber/test/csiv2_SUITE.erl
@@ -668,67 +668,57 @@ code_OpenSSL509_api(_Config) ->
ssl_server_peercert_api(doc) -> ["Test ssl:peercert (server side)"];
ssl_server_peercert_api(suite) -> [];
ssl_server_peercert_api(_Config) ->
- case os:type() of
- vxworks ->
- {skipped, "No SSL-support for VxWorks."};
- _ ->
- Options = orber_test_lib:get_options(iiop_ssl, server,
- 2, [{iiop_ssl_port, 0}]),
- {ok, ServerNode, ServerHost} =
- ?match({ok,_,_}, orber_test_lib:js_node(Options)),
- ServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_ssl_port, []),
- SSLOptions = orber_test_lib:get_options(ssl, client),
- {ok, Socket} =
- ?match({ok, _}, fake_client_ORB(ssl, ServerHost, ServerPort, SSLOptions)),
- {ok, _PeerCert} = ?match({ok, _}, orber_socket:peercert(ssl, Socket)),
- %% ?match({ok, {rdnSequence, _}}, orber_socket:peercert(ssl, Socket, [pkix, subject])),
- %% ?match({ok, {rdnSequence, _}}, orber_socket:peercert(ssl, Socket, [ssl, subject])),
- % ?match({ok, #'Certificate'{}},
- % 'OrberCSIv2':decode('Certificate', PeerCert)),
- destroy_fake_ORB(ssl, Socket),
- ok
- end.
+ Options = orber_test_lib:get_options(iiop_ssl, server,
+ 2, [{iiop_ssl_port, 0}]),
+ {ok, ServerNode, ServerHost} =
+ ?match({ok,_,_}, orber_test_lib:js_node(Options)),
+ ServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_ssl_port, []),
+ SSLOptions = orber_test_lib:get_options(ssl, client),
+ {ok, Socket} =
+ ?match({ok, _}, fake_client_ORB(ssl, ServerHost, ServerPort, SSLOptions)),
+ {ok, _PeerCert} = ?match({ok, _}, orber_socket:peercert(ssl, Socket)),
+ %% ?match({ok, {rdnSequence, _}}, orber_socket:peercert(ssl, Socket, [pkix, subject])),
+ %% ?match({ok, {rdnSequence, _}}, orber_socket:peercert(ssl, Socket, [ssl, subject])),
+ % ?match({ok, #'Certificate'{}},
+ % 'OrberCSIv2':decode('Certificate', PeerCert)),
+ destroy_fake_ORB(ssl, Socket),
+ ok.
ssl_client_peercert_api(doc) -> ["Test ssl:peercert (client side)"];
ssl_client_peercert_api(suite) -> [];
ssl_client_peercert_api(_Config) ->
- case os:type() of
- vxworks ->
- {skipped, "No SSL-support for VxWorks."};
- _ ->
- Options = orber_test_lib:get_options(iiop_ssl, client,
- 2, [{iiop_ssl_port, 0}]),
- {ok, ClientNode, _ClientHost} =
- ?match({ok,_,_}, orber_test_lib:js_node(Options)),
- crypto:start(),
- ssl:start(),
- SSLOptions = orber_test_lib:get_options(ssl, server),
- {ok, LSock} = ?match({ok, _}, ssl:listen(0, SSLOptions)),
- {ok, {_Address, LPort}} = ?match({ok, {_, _}}, ssl:sockname(LSock)),
- IOR = ?match({'IOP_IOR',_,_},
- iop_ior:create_external({1, 2}, "IDL:FAKE:1.0",
- "localhost", 6004, "FAKE",
- [#'IOP_TaggedComponent'
- {tag=?TAG_SSL_SEC_TRANS,
- component_data=#'SSLIOP_SSL'
- {target_supports = 2,
- target_requires = 2,
- port = LPort}}])),
- spawn(orber_test_lib, remote_apply,
- [ClientNode, corba_object, non_existent, [IOR]]),
- {ok, Socket} = ?match({ok, _}, ssl:transport_accept(LSock)),
- ?match(ok, ssl:ssl_accept(Socket)),
-
- {ok, _PeerCert} = ?match({ok, _}, orber_socket:peercert(ssl, Socket)),
- %% ?match({ok, {rdnSequence, _}}, orber_socket:peercert(ssl, Socket, [pkix, subject])),
- %% ?match({ok, {rdnSequence, _}}, orber_socket:peercert(ssl, Socket, [ssl, subject])),
- % ?match({ok, #'Certificate'{}},
- % 'OrberCSIv2':decode('Certificate', PeerCert)),
- ssl:close(Socket),
- ssl:close(LSock),
- ssl:stop(),
- ok
- end.
+ Options = orber_test_lib:get_options(iiop_ssl, client,
+ 2, [{iiop_ssl_port, 0}]),
+ {ok, ClientNode, _ClientHost} =
+ ?match({ok,_,_}, orber_test_lib:js_node(Options)),
+ crypto:start(),
+ ssl:start(),
+ SSLOptions = orber_test_lib:get_options(ssl, server),
+ {ok, LSock} = ?match({ok, _}, ssl:listen(0, SSLOptions)),
+ {ok, {_Address, LPort}} = ?match({ok, {_, _}}, ssl:sockname(LSock)),
+ IOR = ?match({'IOP_IOR',_,_},
+ iop_ior:create_external({1, 2}, "IDL:FAKE:1.0",
+ "localhost", 6004, "FAKE",
+ [#'IOP_TaggedComponent'
+ {tag=?TAG_SSL_SEC_TRANS,
+ component_data=#'SSLIOP_SSL'
+ {target_supports = 2,
+ target_requires = 2,
+ port = LPort}}])),
+ spawn(orber_test_lib, remote_apply,
+ [ClientNode, corba_object, non_existent, [IOR]]),
+ {ok, Socket} = ?match({ok, _}, ssl:transport_accept(LSock)),
+ ?match(ok, ssl:ssl_accept(Socket)),
+
+ {ok, _PeerCert} = ?match({ok, _}, orber_socket:peercert(ssl, Socket)),
+ %% ?match({ok, {rdnSequence, _}}, orber_socket:peercert(ssl, Socket, [pkix, subject])),
+ %% ?match({ok, {rdnSequence, _}}, orber_socket:peercert(ssl, Socket, [ssl, subject])),
+ % ?match({ok, #'Certificate'{}},
+ % 'OrberCSIv2':decode('Certificate', PeerCert)),
+ ssl:close(Socket),
+ ssl:close(LSock),
+ ssl:stop(),
+ ok.
%%-----------------------------------------------------------------
%% Local functions.
diff --git a/lib/orber/test/orber_test_lib.erl b/lib/orber/test/orber_test_lib.erl
index 2bfa1ef871..6824d25aef 100644
--- a/lib/orber/test/orber_test_lib.erl
+++ b/lib/orber/test/orber_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. 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
@@ -278,24 +278,13 @@ check_options(Options) ->
end.
starter(Host, Name, Args) ->
- case os:type() of
- vxworks ->
- test_server:start_node(Name, slave, [{args,Args}]);
- _ ->
- io:format("slave:start_link(~p,~p,~p).~n",[Host,Name,Args]),
- slave:start_link(Host, Name, Args)
- end.
+ io:format("slave:start_link(~p,~p,~p).~n",[Host,Name,Args]),
+ slave:start_link(Host, Name, Args).
slave_sup() ->
process_flag(trap_exit, true),
receive
- {'EXIT', _, _} ->
- case os:type() of
- vxworks ->
- erlang:halt();
- _ ->
- ignore
- end
+ {'EXIT', _, _} -> ignore
end.
start_ssl(true, Node) ->
@@ -419,12 +408,7 @@ destroy_node(Node, Type) ->
stopper(Node, Type).
stopper(Node, _Type) ->
- case os:type() of
- vxworks ->
- test_server:stop_node(Node);
- _ ->
- slave:stop(Node)
- end.
+ slave:stop(Node).
%%------------------------------------------------------------
diff --git a/lib/os_mon/c_src/Makefile.in b/lib/os_mon/c_src/Makefile.in
index 45dc01e993..e728e43a09 100644
--- a/lib/os_mon/c_src/Makefile.in
+++ b/lib/os_mon/c_src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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
@@ -63,14 +63,10 @@ ENTRY_OBJ=$(ERL_TOP)/erts/obj/$(TARGET)/port_entry.o
PORT_ENTRY_POINT=erl_port_entry
ENTRY_LDFLAGS=-entry:$(PORT_ENTRY_POINT)
else
-ifeq ($(findstring vxworks_simso,$(TARGET)),vxworks_simso)
-PROGRAMS =
-else
PROGRAMS = \
memsup @os_mon_programs@
C_FILES= $(PROGRAMS:%=%.c)
endif
-endif
TARGET_FILES= $(PROGRAMS:%=$(BINDIR)/%)
@@ -127,14 +123,10 @@ $(OBJDIR)/memsup.o: memsup.h
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
-ifeq ($(findstring vxworks_simso,$(TARGET)),vxworks_simso)
-release_spec:
-else
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/src"
$(INSTALL_DATA) $(C_FILES) "$(RELSYSDIR)/src"
$(INSTALL_DIR) "$(RELSYSDIR)/priv/bin"
$(INSTALL_PROGRAM) $(TARGET_FILES) "$(RELSYSDIR)/priv/bin"
-endif
release_docs_spec:
diff --git a/lib/os_mon/c_src/cpu_sup.c b/lib/os_mon/c_src/cpu_sup.c
index 9c5f9a6aa5..7372d5b0e8 100644
--- a/lib/os_mon/c_src/cpu_sup.c
+++ b/lib/os_mon/c_src/cpu_sup.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2011. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2012. 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
@@ -458,8 +458,18 @@ static void error(char* err_msg) {
* if we get error here we have trouble,
* silence unnecessary warnings
*/
- if(write(FD_ERR, err_msg, strlen(err_msg)));
- if(write(FD_ERR, "\n", 1));
+ char buffer[256] = "[os_mon] cpu supervisor port (cpu_sup): ";
+ int i = strlen(buffer), j = 0;
+ int n = strlen(err_msg);
+
+ while(i < 253 && j < n) {
+ buffer[i++] = err_msg[j++];
+ }
+ buffer[i++] = '\r';
+ buffer[i++] = '\n';
+
+ /* try to use one write only */
+ if(write(FD_ERR, buffer, i));
exit(-1);
}
diff --git a/lib/os_mon/c_src/memsup.c b/lib/os_mon/c_src/memsup.c
index 078f20ff98..3a1a8e9444 100644
--- a/lib/os_mon/c_src/memsup.c
+++ b/lib/os_mon/c_src/memsup.c
@@ -31,7 +31,7 @@
*
* This program is started from Erlang as follows,
*
- * Port = open_port({spawn, 'memsup'}, [{packet,1}]) for UNIX and VxWorks
+ * Port = open_port({spawn, 'memsup'}, [{packet,1}]) for UNIX
*
* Erlang sends one of the request condes defined in memsup.h and this program
* answers in one of two ways:
@@ -75,10 +75,6 @@
* that there is no process at the other end of the connection
* having the connection open for writing (end-of-file).
*
- * COMPILING
- *
- * When the target is VxWorks the identifier VXWORKS must be defined for
- * the preprocessor (usually by a -D option).
*/
#if defined(sgi) || defined(__sgi) || defined(__sgi__)
@@ -90,9 +86,7 @@
#include <stddef.h>
#include <stdlib.h>
-#ifndef VXWORKS
#include <unistd.h>
-#endif
#if (defined(__unix__) || defined(unix)) && !defined(USG)
#include <sys/param.h>
@@ -104,12 +98,6 @@
#include <time.h>
#include <errno.h>
-#ifdef VXWORKS
-#include <vxWorks.h>
-#include <ioLib.h>
-#include <memLib.h>
-#endif
-
#ifdef BSD4_4
#include <sys/types.h>
#include <sys/sysctl.h>
@@ -143,20 +131,8 @@
/* prototypes */
static void print_error(const char *,...);
-#ifdef VXWORKS
-extern int erl_mem_info_get(MEM_PART_STATS *);
-#endif
-
-#ifdef VXWORKS
-#define MAIN memsup
-static MEM_PART_STATS latest;
-static unsigned long latest_system_total; /* does not fit in the struct */
-
-#else
#define MAIN main
-#endif
-
/*
* example, we want procfs information, now give them something equivalent:
@@ -282,16 +258,6 @@ send_tag(int value){
}
}
-
-#ifdef VXWORKS
-static void load_statistics(void){
- if(memPartInfoGet(memSysPartId,&latest) != OK)
- memset(&latest,0,sizeof(latest));
- latest_system_total = latest.numBytesFree + latest.numBytesAlloc;
- erl_mem_info_get(&latest); /* if it fails, latest is untouched */
-}
-#endif
-
#ifdef BSD4_4
static int
get_vmtotal(struct vmtotal *vt) {
@@ -358,19 +324,6 @@ get_mem_procfs(memory_ext *me){
/* arch specific functions */
-#if defined(VXWORKS)
-static int
-get_extended_mem_vxwork(memory_ext *me) {
- load_statistics();
- me->total = (latest.numBytesFree + latest.numBytesAlloc);
- me->free = latest.numBytesFree;
- me->pagesize = 1;
- me->flag = F_MEM_TOTAL | F_MEM_FREE;
- return 1;
-}
-#endif
-
-
#if defined(__linux__) /* ifdef SYSINFO */
/* sysinfo does not include cached memory which is a problem. */
static int
@@ -442,12 +395,8 @@ get_extended_mem_sgi(memory_ext *me) {
static void
get_extended_mem(memory_ext *me) {
-/* vxworks */
-#if defined(VXWORKS)
- if (get_extended_mem_vxworks(me)) return;
-
/* linux */
-#elif defined(__linux__)
+#if defined(__linux__)
if (get_mem_procfs(me)) return;
if (get_extended_mem_sysinfo(me)) return;
@@ -477,12 +426,7 @@ get_extended_mem(memory_ext *me) {
static void
get_basic_mem(unsigned long *tot, unsigned long *used, unsigned long *pagesize){
-#if defined(VXWORKS)
- load_statistics();
- *tot = (latest.numBytesFree + latest.numBytesAlloc);
- *used = latest.numBytesAlloc;
- *pagesize = 1;
-#elif defined(_SC_AVPHYS_PAGES) /* Does this exist on others than Solaris2? */
+#if defined(_SC_AVPHYS_PAGES) /* Does this exist on others than Solaris2? */
unsigned long avPhys, phys, pgSz;
phys = sysconf(_SC_PHYS_PAGES);
@@ -493,7 +437,7 @@ get_basic_mem(unsigned long *tot, unsigned long *used, unsigned long *pagesize){
#elif defined(__linux__) && !defined(_SC_AVPHYS_PAGES)
memory_ext me;
if (get_mem_procfs(&me) < 0) {
- print_error("ProcFS read error.");
+ print_error("ProcFS read error");
exit(1);
}
*tot = me.total;
@@ -557,17 +501,8 @@ extended_show_mem(void){
if (me.flag & F_SWAP_TOTAL) { send_tag(SWAP_TOTAL); send(me.total_swap, ps); }
if (me.flag & F_SWAP_FREE) { send_tag(SWAP_FREE); send(me.free_swap, ps); }
-#ifdef VXWORKS
- send_tag(SM_SYSTEM_TOTAL);
- send(latest_system_total, 1);
- send_tag(SM_LARGEST_FREE);
- send(latest.maxBlockSizeFree, 1);
- send_tag(SM_NUMBER_OF_FREE);
- send(latest.numBlocksFree, 1);
-#else
/* total is system total*/
if (me.flag & F_MEM_TOTAL) { send_tag(MEM_SYSTEM_TOTAL); send(me.total, ps); }
-#endif
send_tag(SHOW_SYSTEM_MEM_END);
}
@@ -582,7 +517,7 @@ message_loop(int erlin_fd)
* Wait for command from Erlang
*/
if ((res = read(erlin_fd, &cmdLen, 1)) < 0) {
- print_error("Error reading from Erlang.");
+ print_error("Error reading from Erlang");
return;
}
@@ -603,19 +538,19 @@ message_loop(int erlin_fd)
break;
case 0:
- print_error("Erlang has closed.");
+ print_error("Erlang has closed");
return;
default:
- print_error("Error reading from Erlang.");
+ print_error("Error reading from Erlang");
return;
} /* switch() */
} else { /* cmdLen != 1 */
- print_error("Invalid command length (%d) received.", cmdLen);
+ print_error("Invalid command length (%d) received", cmdLen);
return;
}
} else { /* Erlang end closed */
- print_error("Erlang has closed.");
+ print_error("Erlang has closed");
return;
}
}
@@ -641,15 +576,12 @@ static void
print_error(const char *format,...)
{
va_list args;
+ char buffer[256];
va_start(args, format);
- fprintf(stderr, "%s: ", program_name);
- vfprintf(stderr, format, args);
+ vsnprintf(buffer, 256, format, args);
va_end(args);
- fprintf(stderr, " \n");
+ /* try to use one write only */
+ fprintf(stderr, "[os_mon] memory supervisor port (memsup): %s\r\n", buffer);
+ fflush(stderr);
}
-
-
-
-
-
diff --git a/lib/os_mon/c_src/win32sysinfo.c b/lib/os_mon/c_src/win32sysinfo.c
index 2a155aae87..f02ae637b2 100644
--- a/lib/os_mon/c_src/win32sysinfo.c
+++ b/lib/os_mon/c_src/win32sysinfo.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2012. 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
@@ -89,6 +89,7 @@ typedef BOOL (WINAPI *tfpGetDiskFreeSpaceEx)(LPCTSTR, PULARGE_INTEGER,PULARGE_IN
static tfpGetDiskFreeSpaceEx fpGetDiskFreeSpaceEx;
+static void print_error(const char *msg);
static void
return_answer(char* value)
{
@@ -98,7 +99,7 @@ return_answer(char* value)
res = write(1,(char*) &bytes,1);
if (res != 1) {
- fprintf(stderr,"win32sysinfo:Error writing to pipe");
+ print_error("Error writing to pipe");
exit(1);
}
@@ -107,9 +108,8 @@ return_answer(char* value)
while (left > 0)
{
res = write(1, value+bytes-left, left);
- if (res <= 0)
- {
- fprintf(stderr,"win32sysinfo:Error writing to pipe");
+ if (res <= 0) {
+ print_error("Error writing to pipe");
exit(1);
}
left -= res;
@@ -248,7 +248,6 @@ message_loop()
char cmd[512];
int res;
- fprintf(stderr,"in message_loop\n");
/* Startup ACK. */
return_answer(OK);
while (1)
@@ -257,12 +256,12 @@ message_loop()
* Wait for command from Erlang
*/
if ((res = read(0, &cmdLen, 1)) < 0) {
- fprintf(stderr,"win32sysinfo:Error reading from Erlang.");
+ print_error("Error reading from Erlang");
return;
}
if (res != 1){ /* Exactly one byte read ? */
- fprintf(stderr,"win32sysinfo:Erlang has closed.");
+ print_error("Erlang has closed");
return;
}
if ((res = read(0, &cmd, cmdLen)) == cmdLen){
@@ -291,11 +290,11 @@ message_loop()
return_answer("xEND");
}
else if (res == 0) {
- fprintf(stderr,"win32sysinfo:Erlang has closed.");
+ print_error("Erlang has closed");
return;
}
else {
- fprintf(stderr,"win32sysinfo:Error reading from Erlang.");
+ print_error("Error reading from Erlang");
return;
}
}
@@ -309,10 +308,9 @@ int main(int argc, char ** argv){
message_loop();
return 0;
}
-
-
-
-
-
-
-
+static void
+print_error(const char *msg) {
+ /* try to use one write only */
+ fprintf(stderr, "[os_mon] win32 supervisor port (win32sysinfo): %s\r\n", msg);
+ fflush(stderr);
+}
diff --git a/lib/os_mon/doc/src/Makefile b/lib/os_mon/doc/src/Makefile
index aa8fb4b60c..08fd23ec92 100644
--- a/lib/os_mon/doc/src/Makefile
+++ b/lib/os_mon/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/os_mon/doc/src/notes.xml b/lib/os_mon/doc/src/notes.xml
index 6674d7b722..061183e51c 100644
--- a/lib/os_mon/doc/src/notes.xml
+++ b/lib/os_mon/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,27 @@
</header>
<p>This document describes the changes made to the OS_Mon application.</p>
+<section><title>Os_Mon 2.2.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Infinity timeout added to internal calls in disksup to
+ allow it to work properly under very heavy load.</p>
+ <p>
+ Own Id: OTP-10100</p>
+ </item>
+ <item>
+ <p>Clarify error messages from os_mon port programs</p>
+ <p>
+ Own Id: OTP-10161</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Os_Mon 2.2.9</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/os_mon/mibs/Makefile b/lib/os_mon/mibs/Makefile
index a6d8ab5042..3e24c3d373 100644
--- a/lib/os_mon/mibs/Makefile
+++ b/lib/os_mon/mibs/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/os_mon/src/Makefile b/lib/os_mon/src/Makefile
index a8ad26f6a1..864d7a09d4 100644
--- a/lib/os_mon/src/Makefile
+++ b/lib/os_mon/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/os_mon/src/disksup.erl b/lib/os_mon/src/disksup.erl
index c7abb7a386..f5f76f1488 100644
--- a/lib/os_mon/src/disksup.erl
+++ b/lib/os_mon/src/disksup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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/lib/os_mon/src/memsup.erl b/lib/os_mon/src/memsup.erl
index 54771b4703..a1b8591c8c 100644
--- a/lib/os_mon/src/memsup.erl
+++ b/lib/os_mon/src/memsup.erl
@@ -185,7 +185,6 @@ init([]) ->
{unix, irix} -> true;
{unix, sunos} -> true;
{win32, _OSname} -> false;
- vxworks -> true;
_ ->
exit({unsupported_os, OS})
end,
@@ -617,8 +616,7 @@ code_change(Vsn, PrevState, "1.8") ->
{unix, openbsd} -> true;
{unix, netbsd} -> true;
{unix, sunos} -> true;
- {win32, _OSname} -> false;
- vxworks -> true
+ {win32, _OSname} -> false
end,
Pid = if
PortMode -> spawn_link(fun() -> port_init() end);
diff --git a/lib/os_mon/src/os_mon.erl b/lib/os_mon/src/os_mon.erl
index 2b6cd7c498..df1eccb064 100644
--- a/lib/os_mon/src/os_mon.erl
+++ b/lib/os_mon/src/os_mon.erl
@@ -177,8 +177,6 @@ services({unix, _}) -> % Other unix.
[cpu_sup, disksup, memsup];
services({win32, _}) ->
[disksup, memsup, os_sup, sysinfo];
-services(vxworks) ->
- [memsup];
services(_) ->
[].
diff --git a/lib/os_mon/test/Makefile b/lib/os_mon/test/Makefile
index 954cfef7fb..9c5f2c1820 100644
--- a/lib/os_mon/test/Makefile
+++ b/lib/os_mon/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/os_mon/vsn.mk b/lib/os_mon/vsn.mk
index b92bdf3c1a..180906127c 100644
--- a/lib/os_mon/vsn.mk
+++ b/lib/os_mon/vsn.mk
@@ -1 +1 @@
-OS_MON_VSN = 2.2.9
+OS_MON_VSN = 2.2.10
diff --git a/lib/otp_mibs/doc/src/Makefile b/lib/otp_mibs/doc/src/Makefile
index 16a5bb39b4..6b8610bc23 100644
--- a/lib/otp_mibs/doc/src/Makefile
+++ b/lib/otp_mibs/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. 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/lib/otp_mibs/mibs/Makefile b/lib/otp_mibs/mibs/Makefile
index 7ef8480b3e..703c4b3ed4 100644
--- a/lib/otp_mibs/mibs/Makefile
+++ b/lib/otp_mibs/mibs/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/otp_mibs/src/Makefile b/lib/otp_mibs/src/Makefile
index c9c40fec3a..03298d39d5 100644
--- a/lib/otp_mibs/src/Makefile
+++ b/lib/otp_mibs/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. 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/lib/parsetools/doc/src/Makefile b/lib/parsetools/doc/src/Makefile
index 464ea66af0..96879084d8 100644
--- a/lib/parsetools/doc/src/Makefile
+++ b/lib/parsetools/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/parsetools/src/Makefile b/lib/parsetools/src/Makefile
index e228ee88ef..4f199da8ee 100644
--- a/lib/parsetools/src/Makefile
+++ b/lib/parsetools/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/parsetools/test/Makefile b/lib/parsetools/test/Makefile
index 430386bfe5..6455f6ade7 100644
--- a/lib/parsetools/test/Makefile
+++ b/lib/parsetools/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2011. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. 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/lib/percept/doc/src/Makefile b/lib/percept/doc/src/Makefile
index e749103783..3182dc2954 100644
--- a/lib/percept/doc/src/Makefile
+++ b/lib/percept/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2007-2010. All Rights Reserved.
+# Copyright Ericsson AB 2007-2012. 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/lib/percept/priv/Makefile b/lib/percept/priv/Makefile
index 4d7942c49c..b82b51ed4b 100644
--- a/lib/percept/priv/Makefile
+++ b/lib/percept/priv/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2007-2009. All Rights Reserved.
+# Copyright Ericsson AB 2007-2012. 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/lib/percept/src/Makefile b/lib/percept/src/Makefile
index 4b9a10cdb8..253a8c2da3 100644
--- a/lib/percept/src/Makefile
+++ b/lib/percept/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2007-2009. All Rights Reserved.
+# Copyright Ericsson AB 2007-2012. 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/lib/percept/src/percept.app.src b/lib/percept/src/percept.app.src
index c70fede721..7b20093ece 100644
--- a/lib/percept/src/percept.app.src
+++ b/lib/percept/src/percept.app.src
@@ -17,14 +17,26 @@
%% %CopyrightEnd%
%%
-{application,percept,
- [{description, "PERCEPT Erlang Concurrency Profiling Tool"},
- {vsn, "%VSN%"},
- {modules, [percept,percept_db,percept_html,percept_graph,percept_analyzer]},
- {registered, [percept_db,percept_port]},
- {applications, [kernel,stdlib]},
- {env, []}
- ]}.
-
+{application,percept, [
+ {description, "PERCEPT Erlang Concurrency Profiling Tool"},
+ {vsn, "%VSN%"},
+ {modules, [
+ egd,
+ egd_font,
+ egd_png,
+ egd_primitives,
+ egd_render,
+ percept,
+ percept_analyzer,
+ percept_db,
+ percept_graph,
+ percept_html,
+ percept_image
+ ]},
+ {registered, [percept_db,percept_port]},
+ {applications, [kernel,stdlib]},
+ {env,[]}
+]}.
+%% vim: syntax=erlang
diff --git a/lib/percept/test/Makefile b/lib/percept/test/Makefile
index ee70966b01..156ef5dd0a 100644
--- a/lib/percept/test/Makefile
+++ b/lib/percept/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2007-2011. All Rights Reserved.
+# Copyright Ericsson AB 2007-2012. 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/lib/pman/doc/src/Makefile b/lib/pman/doc/src/Makefile
index ec759091ab..d9e0317f05 100644
--- a/lib/pman/doc/src/Makefile
+++ b/lib/pman/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/pman/src/Makefile b/lib/pman/src/Makefile
index 2cd1c64c49..fa01fe7238 100644
--- a/lib/pman/src/Makefile
+++ b/lib/pman/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/public_key/asn1/Makefile b/lib/public_key/asn1/Makefile
index d48f48a5d5..4bd043ee5d 100644
--- a/lib/public_key/asn1/Makefile
+++ b/lib/public_key/asn1/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2011. All Rights Reserved.
+# Copyright Ericsson AB 2008-2012. 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/lib/public_key/asn1/OTP-PKIX.asn1 b/lib/public_key/asn1/OTP-PKIX.asn1
index fbf531df40..4f20208bce 100644
--- a/lib/public_key/asn1/OTP-PKIX.asn1
+++ b/lib/public_key/asn1/OTP-PKIX.asn1
@@ -119,6 +119,7 @@ IMPORTS
md2WithRSAEncryption,
md5WithRSAEncryption,
sha1WithRSAEncryption,
+ sha224WithRSAEncryption,
sha256WithRSAEncryption,
sha384WithRSAEncryption,
sha512WithRSAEncryption
@@ -225,7 +226,17 @@ dnQualifier ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
countryName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
ID id-at-countryName
- TYPE X520countryName }
+ TYPE X520countryName } -- this is currently not used when decoding
+ -- The decoding and mapping between ID and Type is done in the code
+ -- in module publickey_cert_records via the function attribute_type
+ -- To be more forgiving and compatible with other SSL implementations
+ -- regarding how to handle and sometimes accept incorrect certificates
+ -- we define and use the type below instead of X520countryName
+
+ OTP-X520countryname ::= CHOICE {
+ printableString PrintableString (SIZE (2)),
+ utf8String UTF8String (SIZE (2))
+}
serialNumber ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
ID id-at-serialNumber
@@ -307,6 +318,7 @@ PublicKeyAlgorithm ::= SEQUENCE {
SupportedSignatureAlgorithms SIGNATURE-ALGORITHM-CLASS ::= {
dsa-with-sha1 | md2-with-rsa-encryption |
md5-with-rsa-encryption | sha1-with-rsa-encryption |
+ sha224-with-rsa-encryption |
sha256-with-rsa-encryption |
sha384-with-rsa-encryption |
sha512-with-rsa-encryption |
@@ -355,6 +367,10 @@ SupportedPublicKeyAlgorithms PUBLIC-KEY-ALGORITHM-CLASS ::= {
ID sha1WithRSAEncryption
TYPE NULL }
+ sha224-with-rsa-encryption SIGNATURE-ALGORITHM-CLASS ::= {
+ ID sha224WithRSAEncryption
+ TYPE NULL }
+
sha256-with-rsa-encryption SIGNATURE-ALGORITHM-CLASS ::= {
ID sha256WithRSAEncryption
TYPE NULL }
diff --git a/lib/public_key/asn1/PKCS-1.asn1 b/lib/public_key/asn1/PKCS-1.asn1
index b06f5efa9d..c83289e779 100644
--- a/lib/public_key/asn1/PKCS-1.asn1
+++ b/lib/public_key/asn1/PKCS-1.asn1
@@ -33,6 +33,9 @@ sha1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 }
sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 }
sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 }
sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 }
+sha224WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 14 }
+
+
id-sha1 OBJECT IDENTIFIER ::= {
iso(1) identified-organization(3) oiw(14) secsig(3)
diff --git a/lib/public_key/doc/src/Makefile b/lib/public_key/doc/src/Makefile
index 928aa62c1b..17fb67e95c 100644
--- a/lib/public_key/doc/src/Makefile
+++ b/lib/public_key/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2011. All Rights Reserved.
+# Copyright Ericsson AB 2008-2012. 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/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml
index 4d3a9856eb..d895042570 100644
--- a/lib/public_key/doc/src/notes.xml
+++ b/lib/public_key/doc/src/notes.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -34,6 +34,31 @@
<file>notes.xml</file>
</header>
+<section><title>Public_Key 0.16</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add crypto and public_key support for the hash functions
+ SHA224, SHA256, SHA384 and SHA512 and also hmac and
+ rsa_sign/verify support using these hash functions.
+ Thanks to Andreas Schultz for making a prototype.</p>
+ <p>
+ Own Id: OTP-9908</p>
+ </item>
+ <item>
+ <p>
+ Optimize RSA private key handling in <c>crypto</c> and
+ <c>public_key</c>.</p>
+ <p>
+ Own Id: OTP-10065</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Public_Key 0.15</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index 0b6673e826..5c227557f2 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -82,9 +82,9 @@
<p><code> rsa_padding() = 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding'
| 'rsa_no_padding'</code></p>
- <p><code> rsa_digest_type() = 'md5' | 'sha' </code></p>
+ <p><code> rsa_digest_type() = 'md5' | 'sha' | 'sha224' | 'sha256' | 'sha384' | 'sha512' </code></p>
- <p><code> dss_digest_type() = 'none' | 'sha' </code></p>
+ <p><code> dss_digest_type() = 'sha' </code></p>
<p><code> ssh_file() = openssh_public_key | rfc4716_public_key |
known_hosts | auth_keys </code></p>
@@ -396,14 +396,14 @@
<name>sign(Msg, DigestType, Key) -> binary()</name>
<fsummary> Create digital signature.</fsummary>
<type>
- <v>Msg = binary()</v>
+ <v>Msg = binary() | {digest,binary()}</v>
<d>The msg is either the binary "plain text" data to be
- signed or in the case that digest type is <c>none</c>
- it is the hashed value of "plain text" i.e. the digest.</d>
- <v>DigestType = rsa_digest_type() | dsa_digest_type()</v>
+ signed or it is the hashed value of "plain text" i.e. the
+ digest.</d>
+ <v>DigestType = rsa_digest_type() | dss_digest_type()</v>
<v>Key = rsa_private_key() | dsa_private_key()</v>
- </type>
- <desc>
+ </type>
+ <desc>
<p> Creates a digital signature.</p>
</desc>
</func>
@@ -461,11 +461,10 @@
<name>verify(Msg, DigestType, Signature, Key) -> boolean()</name>
<fsummary>Verifies a digital signature.</fsummary>
<type>
- <v>Msg = binary()</v>
+ <v>Msg = binary() | {digest,binary()}</v>
<d>The msg is either the binary "plain text" data
- or in the case that digest type is <c>none</c>
- it is the hashed value of "plain text" i.e. the digest.</d>
- <v>DigestType = rsa_digest_type() | dsa_digest_type()</v>
+ or it is the hashed value of "plain text" i.e. the digest.</d>
+ <v>DigestType = rsa_digest_type() | dss_digest_type()</v>
<v>Signature = binary()</v>
<v>Key = rsa_public_key() | dsa_public_key()</v>
</type>
diff --git a/lib/public_key/src/Makefile b/lib/public_key/src/Makefile
index 8a72da477f..d5cd13d81a 100644
--- a/lib/public_key/src/Makefile
+++ b/lib/public_key/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2011. All Rights Reserved.
+# Copyright Ericsson AB 2008-2012. 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/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl
index b76e32a2a0..f9e2025479 100644
--- a/lib/public_key/src/pubkey_cert.erl
+++ b/lib/public_key/src/pubkey_cert.erl
@@ -376,8 +376,12 @@ encoded_tbs_cert(Cert) ->
digest_type(?sha1WithRSAEncryption) ->
sha;
+digest_type(?sha224WithRSAEncryption) ->
+ sha224;
digest_type(?sha256WithRSAEncryption) ->
sha256;
+digest_type(?sha384WithRSAEncryption) ->
+ sha384;
digest_type(?sha512WithRSAEncryption) ->
sha512;
digest_type(?md5WithRSAEncryption) ->
diff --git a/lib/public_key/src/pubkey_cert_records.erl b/lib/public_key/src/pubkey_cert_records.erl
index b86d7a1f0c..33fe940ea2 100644
--- a/lib/public_key/src/pubkey_cert_records.erl
+++ b/lib/public_key/src/pubkey_cert_records.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. 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
@@ -57,6 +57,15 @@ transform(#'OTPTBSCertificate'{}= TBS, decode) ->
transform(#'AttributeTypeAndValue'{type=Id,value=Value0} = ATAV, Func) ->
{ok, Value} =
case attribute_type(Id) of
+ 'X520countryName'when Func == decode ->
+ %% Workaround that some certificates break the ASN-1 spec
+ %% and encode countryname as utf8
+ case 'OTP-PUB-KEY':Func('OTP-X520countryname', Value0) of
+ {ok, {utf8String, Utf8Value}} ->
+ {ok, unicode:characters_to_list(Utf8Value)};
+ {ok, {printableString, ASCCI}} ->
+ {ok, ASCCI}
+ end;
Type when is_atom(Type) -> 'OTP-PUB-KEY':Func(Type, Value0);
_UnknownType -> {ok, Value0}
end,
diff --git a/lib/public_key/src/pubkey_ssh.erl b/lib/public_key/src/pubkey_ssh.erl
index f0c94e29a5..008ea96dd3 100644
--- a/lib/public_key/src/pubkey_ssh.erl
+++ b/lib/public_key/src/pubkey_ssh.erl
@@ -47,7 +47,7 @@ decode(Bin, public_key)->
rfc4716_decode(Bin)
end;
decode(Bin, rfc4716_public_key) ->
- rfc4716_decode(Bin);
+ rfc4716_decode(Bin);
decode(Bin, Type) ->
openssh_decode(Bin, Type).
@@ -58,7 +58,7 @@ decode(Bin, Type) ->
%% Description: Encodes a list of ssh file entries.
%%--------------------------------------------------------------------
encode(Entries, Type) ->
- erlang:iolist_to_binary(lists:map(fun({Key, Attributes}) ->
+ iolist_to_binary(lists:map(fun({Key, Attributes}) ->
do_encode(Type, Key, Attributes)
end, Entries)).
@@ -106,7 +106,7 @@ rfc4716_decode_line(Line, Lines, Acc) ->
_ ->
{Body, Rest} = join_entry([Line | Lines], []),
{lists:reverse(Acc), rfc4716_pubkey_decode(base64:mime_decode(Body)), Rest}
- end.
+ end.
join_entry([<<"---- END SSH2 PUBLIC KEY ----", _/binary>>| Lines], Entry) ->
{lists:reverse(Entry), Lines};
@@ -115,16 +115,16 @@ join_entry([Line | Lines], Entry) ->
rfc4716_pubkey_decode(<<?UINT32(Len), Type:Len/binary,
- ?UINT32(SizeE), E:SizeE/binary,
- ?UINT32(SizeN), N:SizeN/binary>>) when Type == <<"ssh-rsa">> ->
+ ?UINT32(SizeE), E:SizeE/binary,
+ ?UINT32(SizeN), N:SizeN/binary>>) when Type == <<"ssh-rsa">> ->
#'RSAPublicKey'{modulus = erlint(SizeN, N),
publicExponent = erlint(SizeE, E)};
rfc4716_pubkey_decode(<<?UINT32(Len), Type:Len/binary,
- ?UINT32(SizeP), P:SizeP/binary,
- ?UINT32(SizeQ), Q:SizeQ/binary,
- ?UINT32(SizeG), G:SizeG/binary,
- ?UINT32(SizeY), Y:SizeY/binary>>) when Type == <<"ssh-dss">> ->
+ ?UINT32(SizeP), P:SizeP/binary,
+ ?UINT32(SizeQ), Q:SizeQ/binary,
+ ?UINT32(SizeG), G:SizeG/binary,
+ ?UINT32(SizeY), Y:SizeY/binary>>) when Type == <<"ssh-dss">> ->
{erlint(SizeY, Y),
#'Dss-Parms'{p = erlint(SizeP, P),
q = erlint(SizeQ, Q),
@@ -143,94 +143,63 @@ do_openssh_decode(FileType, [<<>> | Lines], Acc) ->
do_openssh_decode(FileType,[<<"#", _/binary>> | Lines], Acc) ->
do_openssh_decode(FileType, Lines, Acc);
do_openssh_decode(auth_keys = FileType, [Line | Lines], Acc) ->
- Split = binary:split(Line, <<" ">>, [global]),
- case mend_split(Split, []) of
- %% ssh2
- [KeyType, Base64Enc, Comment] ->
+ case decode_auth_keys(Line) of
+ {ssh2, {options, [Options, KeyType, Base64Enc| Comment]}} ->
do_openssh_decode(FileType, Lines,
- [{openssh_pubkey_decode(KeyType, Base64Enc),
- [{comment, string_decode(Comment)}]} | Acc]);
- %% ssh1
- [Options, Bits, Exponent, Modulus, Comment] ->
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ decode_comment(Comment) ++ [{options, comma_list_decode(Options)}]} | Acc]);
+ {ssh2, {no_options, [KeyType, Base64Enc| Comment]}} ->
+ do_openssh_decode(FileType, Lines,
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ decode_comment(Comment)} | Acc]);
+ {ssh1, {options, [Options, Bits, Exponent, Modulus | Comment]}} ->
do_openssh_decode(FileType, Lines,
[{ssh1_rsa_pubkey_decode(Modulus, Exponent),
- [{comment, string_decode(Comment)},
- {options, comma_list_decode(Options)},
- {bits, integer_decode(Bits)}]} | Acc]);
- [A, B, C, D] ->
- ssh_2_or_1(FileType, Lines, Acc, A,B,C,D)
+ decode_comment(Comment) ++ [{options, comma_list_decode(Options)},
+ {bits, integer_decode(Bits)}]
+ } | Acc]);
+ {ssh1, {no_options, [Bits, Exponent, Modulus | Comment]}} ->
+ do_openssh_decode(FileType, Lines,
+ [{ssh1_rsa_pubkey_decode(Modulus, Exponent),
+ decode_comment(Comment) ++ [{bits, integer_decode(Bits)}]
+ } | Acc])
end;
do_openssh_decode(known_hosts = FileType, [Line | Lines], Acc) ->
- Split = binary:split(Line, <<" ">>, [global]),
- case mend_split(Split, []) of
- %% ssh 2
- [HostNames, KeyType, Base64Enc] ->
+ case decode_known_hosts(Line) of
+ {ssh2, [HostNames, KeyType, Base64Enc| Comment]} ->
do_openssh_decode(FileType, Lines,
- [{openssh_pubkey_decode(KeyType, Base64Enc),
- [{hostnames, comma_list_decode(HostNames)}]}| Acc]);
- [A, B, C, D] ->
- ssh_2_or_1(FileType, Lines, Acc, A, B, C, D);
- %% ssh 1
- [HostNames, Bits, Exponent, Modulus, Comment] ->
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ decode_comment(Comment) ++
+ [{hostnames, comma_list_decode(HostNames)}]}| Acc]);
+ {ssh1, [HostNames, Bits, Exponent, Modulus | Comment]} ->
do_openssh_decode(FileType, Lines,
- [{ssh1_rsa_pubkey_decode(Modulus, Exponent),
- [{comment, string_decode(Comment)},
- {hostnames, comma_list_decode(HostNames)},
- {bits, integer_decode(Bits)}]} | Acc])
- end;
+ [{ssh1_rsa_pubkey_decode(Modulus, Exponent),
+ decode_comment(Comment) ++
+ [{hostnames, comma_list_decode(HostNames)},
+ {bits, integer_decode(Bits)}]}
+ | Acc])
+ end;
do_openssh_decode(openssh_public_key = FileType, [Line | Lines], Acc) ->
- Split = binary:split(Line, <<" ">>, [global]),
- case mend_split(Split, []) of
- [KeyType, Base64Enc, Comment0] when KeyType == <<"ssh-rsa">>;
- KeyType == <<"ssh-dss">> ->
- Comment = string:strip(binary_to_list(Comment0), right, $\n),
+ case split_n(2, Line, []) of
+ [KeyType, Base64Enc] when KeyType == <<"ssh-rsa">>;
+ KeyType == <<"ssh-dss">> ->
+ do_openssh_decode(FileType, Lines,
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ []} | Acc]);
+ [KeyType, Base64Enc | Comment0] when KeyType == <<"ssh-rsa">>;
+ KeyType == <<"ssh-dss">> ->
+ Comment = string:strip(string_decode(iolist_to_binary(Comment0)), right, $\n),
do_openssh_decode(FileType, Lines,
[{openssh_pubkey_decode(KeyType, Base64Enc),
[{comment, Comment}]} | Acc])
end.
-ssh_2_or_1(known_hosts = FileType, Lines, Acc, A, B, C, D) ->
- try integer_decode(B) of
- Int ->
- file_type_decode_ssh1(FileType, Lines, Acc, A, Int, C,D)
- catch
- error:badarg ->
- file_type_decode_ssh2(FileType, Lines, Acc, A,B,C,D)
- end;
-ssh_2_or_1(auth_keys = FileType, Lines, Acc, A, B, C, D) ->
- try integer_decode(A) of
- Int ->
- file_type_decode_ssh1(FileType, Lines, Acc, Int, B, C,D)
- catch
- error:badarg ->
- file_type_decode_ssh2(FileType, Lines, Acc, A,B,C,D)
- end.
-
-file_type_decode_ssh1(known_hosts = FileType, Lines, Acc, HostNames, Bits, Exponent, Modulus) ->
- do_openssh_decode(FileType, Lines,
- [{ssh1_rsa_pubkey_decode(Modulus, Exponent),
- [{comment, []},
- {hostnames, comma_list_decode(HostNames)},
- {bits, Bits}]} | Acc]);
-file_type_decode_ssh1(auth_keys = FileType, Lines, Acc, Bits, Exponent, Modulus, Comment) ->
- do_openssh_decode(FileType, Lines,
- [{ssh1_rsa_pubkey_decode(Modulus, Exponent),
- [{comment, string_decode(Comment)},
- {bits, Bits}]} | Acc]).
-
-file_type_decode_ssh2(known_hosts = FileType, Lines, Acc, HostNames, KeyType, Base64Enc, Comment) ->
- do_openssh_decode(FileType, Lines,
- [{openssh_pubkey_decode(KeyType, Base64Enc),
- [{comment, string_decode(Comment)},
- {hostnames, comma_list_decode(HostNames)}]} | Acc]);
-file_type_decode_ssh2(auth_keys = FileType, Lines, Acc, Options, KeyType, Base64Enc, Comment) ->
- do_openssh_decode(FileType, Lines,
- [{openssh_pubkey_decode(KeyType, Base64Enc),
- [{comment, string_decode(Comment)},
- {options, comma_list_decode(Options)}]}
- | Acc]).
+decode_comment([]) ->
+ [];
+decode_comment(Comment) ->
+ [{comment, string_decode(iolist_to_binary(Comment))}].
openssh_pubkey_decode(<<"ssh-rsa">>, Base64Enc) ->
<<?UINT32(StrLen), _:StrLen/binary,
@@ -267,7 +236,7 @@ integer_decode(BinStr) ->
list_to_integer(binary_to_list(BinStr)).
string_decode(BinStr) ->
- binary_to_list(BinStr).
+ unicode_decode(BinStr).
unicode_decode(BinStr) ->
unicode:characters_to_list(BinStr).
@@ -285,11 +254,11 @@ do_encode(Type, Key, Attributes) ->
openssh_encode(Type, Key, Attributes).
rfc4716_encode(Key, [],[]) ->
- erlang:iolist_to_binary([begin_marker(),"\n",
+ iolist_to_binary([begin_marker(),"\n",
split_lines(base64:encode(ssh2_pubkey_encode(Key))),
"\n", end_marker(), "\n"]);
rfc4716_encode(Key, [], [_|_] = Acc) ->
- erlang:iolist_to_binary([begin_marker(), "\n",
+ iolist_to_binary([begin_marker(), "\n",
lists:reverse(Acc),
split_lines(base64:encode(ssh2_pubkey_encode(Key))),
"\n", end_marker(), "\n"]);
@@ -319,9 +288,9 @@ rfc4716_encode_value(Value) ->
end.
openssh_encode(openssh_public_key, Key, Attributes) ->
- Comment = proplists:get_value(comment, Attributes),
+ Comment = proplists:get_value(comment, Attributes, ""),
Enc = base64:encode(ssh2_pubkey_encode(Key)),
- erlang:iolist_to_binary([key_type(Key), " ", Enc, " ", Comment, "\n"]);
+ iolist_to_binary([key_type(Key), " ", Enc, " ", Comment, "\n"]);
openssh_encode(auth_keys, Key, Attributes) ->
Comment = proplists:get_value(comment, Attributes, ""),
@@ -345,30 +314,30 @@ openssh_encode(known_hosts, Key, Attributes) ->
end.
openssh_ssh2_auth_keys_encode(undefined, Key, Comment) ->
- erlang:iolist_to_binary([key_type(Key)," ", base64:encode(ssh2_pubkey_encode(Key)), line_end(Comment)]);
+ iolist_to_binary([key_type(Key)," ", base64:encode(ssh2_pubkey_encode(Key)), line_end(Comment)]);
openssh_ssh2_auth_keys_encode(Options, Key, Comment) ->
- erlang:iolist_to_binary([comma_list_encode(Options, []), " ",
+ iolist_to_binary([comma_list_encode(Options, []), " ",
key_type(Key)," ", base64:encode(ssh2_pubkey_encode(Key)), line_end(Comment)]).
openssh_ssh1_auth_keys_encode(undefined, Bits,
#'RSAPublicKey'{modulus = N, publicExponent = E},
Comment) ->
- erlang:iolist_to_binary([integer_to_list(Bits), " ", integer_to_list(E), " ", integer_to_list(N),
+ iolist_to_binary([integer_to_list(Bits), " ", integer_to_list(E), " ", integer_to_list(N),
line_end(Comment)]);
openssh_ssh1_auth_keys_encode(Options, Bits,
#'RSAPublicKey'{modulus = N, publicExponent = E},
Comment) ->
- erlang:iolist_to_binary([comma_list_encode(Options, []), " ", integer_to_list(Bits),
+ iolist_to_binary([comma_list_encode(Options, []), " ", integer_to_list(Bits),
" ", integer_to_list(E), " ", integer_to_list(N), line_end(Comment)]).
openssh_ssh2_know_hosts_encode(Hostnames, Key, Comment) ->
- erlang:iolist_to_binary([comma_list_encode(Hostnames, []), " ",
+ iolist_to_binary([comma_list_encode(Hostnames, []), " ",
key_type(Key)," ", base64:encode(ssh2_pubkey_encode(Key)), line_end(Comment)]).
openssh_ssh1_known_hosts_encode(Hostnames, Bits,
- #'RSAPublicKey'{modulus = N, publicExponent = E},
- Comment) ->
- erlang:iolist_to_binary([comma_list_encode(Hostnames, [])," ", integer_to_list(Bits)," ",
+ #'RSAPublicKey'{modulus = N, publicExponent = E},
+ Comment) ->
+ iolist_to_binary([comma_list_encode(Hostnames, [])," ", integer_to_list(Bits)," ",
integer_to_list(E)," ", integer_to_list(N), line_end(Comment)]).
line_end("") ->
@@ -411,24 +380,6 @@ ssh2_pubkey_encode({Y, #'Dss-Parms'{p = P, q = Q, g = G}}) ->
GBin/binary,
YBin/binary>>.
-mend_split([Part1, Part2 | Rest] = List, Acc) ->
- case option_end(Part1, Part2) of
- true ->
- lists:reverse(Acc) ++ List;
- false ->
- case length(binary:matches(Part1, <<"\"">>)) of
- N when N rem 2 == 0 ->
- mend_split(Rest, [Part1 | Acc]);
- _ ->
- mend_split([<<Part1/binary, Part2/binary>> | Rest], Acc)
- end
- end.
-
-option_end(Part1, Part2) ->
- (is_key_field(Part1) orelse is_bits_field(Part1))
- orelse
- (is_key_field(Part2) orelse is_bits_field(Part2)).
-
is_key_field(<<"ssh-dss">>) ->
true;
is_key_field(<<"ssh-rsa">>) ->
@@ -456,3 +407,72 @@ split_lines(<<Text:?ENCODED_LINE_LENGTH/binary, Rest/binary>>) ->
[Text, $\n | split_lines(Rest)];
split_lines(Bin) ->
[Bin].
+
+decode_auth_keys(Line) ->
+ [First, Rest] = binary:split(Line, <<" ">>, []),
+ case is_key_field(First) of
+ true ->
+ {ssh2, decode_auth_keys_ssh2(First, Rest)};
+ false ->
+ case is_bits_field(First) of
+ true ->
+ {ssh1, decode_auth_keys_ssh1(First, Rest)};
+ false ->
+ decode_auth_keys(First, Rest)
+ end
+ end.
+
+decode_auth_keys(First, Line) ->
+ [Second, Rest] = binary:split(Line, <<" ">>, []),
+ case is_key_field(Second) of
+ true ->
+ {ssh2, decode_auth_keys_ssh2(First, Second, Rest)};
+ false ->
+ case is_bits_field(Second) of
+ true ->
+ {ssh1, decode_auth_keys_ssh1(First, Second, Rest)};
+ false ->
+ decode_auth_keys(<<First/binary, Second/binary>>, Rest)
+ end
+ end.
+
+decode_auth_keys_ssh2(KeyType, Rest) ->
+ {no_options, [KeyType | split_n(1, Rest, [])]}.
+
+decode_auth_keys_ssh2(Options, Next, Rest) ->
+ {options, [Options, Next | split_n(1, Rest, [])]}.
+
+decode_auth_keys_ssh1(Options, Next, Rest) ->
+ {options, [Options, Next | split_n(2, Rest, [])]}.
+
+decode_auth_keys_ssh1(First, Rest) ->
+ {no_options, [First | split_n(2, Rest, [])]}.
+
+decode_known_hosts(Line) ->
+ [First, Rest] = binary:split(Line, <<" ">>, []),
+ [Second, Rest1] = binary:split(Rest, <<" ">>, []),
+
+ case is_bits_field(Second) of
+ true ->
+ {ssh1, decode_known_hosts_ssh1(First, Second, Rest1)};
+ false ->
+ {ssh2, decode_known_hosts_ssh2(First, Second, Rest1)}
+ end.
+
+decode_known_hosts_ssh1(Hostnames, Bits, Rest) ->
+ [Hostnames, Bits | split_n(2, Rest, [])].
+
+decode_known_hosts_ssh2(Hostnames, KeyType, Rest) ->
+ [Hostnames, KeyType | split_n(1, Rest, [])].
+
+split_n(0, <<>>, Acc) ->
+ lists:reverse(Acc);
+split_n(0, Bin, Acc) ->
+ lists:reverse([Bin | Acc]);
+split_n(N, Bin, Acc) ->
+ case binary:split(Bin, <<" ">>, []) of
+ [First, Rest] ->
+ split_n(N-1, Rest, [First | Acc]);
+ [Last] ->
+ split_n(0, <<>>, [Last | Acc])
+ end.
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index 9f1a0b3af5..d5df53e848 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. 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
@@ -48,8 +48,8 @@
-type rsa_padding() :: 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding'
| 'rsa_no_padding'.
-type public_crypt_options() :: [{rsa_pad, rsa_padding()}].
--type rsa_digest_type() :: 'md5' | 'sha'| 'sha256' | 'sha512'.
--type dss_digest_type() :: 'none' | 'sha'.
+-type rsa_digest_type() :: 'md5' | 'sha'| 'sha224' | 'sha256' | 'sha384' | 'sha512'.
+-type dss_digest_type() :: 'none' | 'sha'. %% None is for backwards compatibility
-define(UINT32(X), X:32/unsigned-big-integer).
-define(DER_NULL, <<5, 0>>).
@@ -332,60 +332,61 @@ format_rsa_private_key(#'RSAPrivateKey'{modulus = N, publicExponent = E,
[crypto:mpint(K) || K <- [E, N, D]].
%%--------------------------------------------------------------------
--spec sign(PlainTextOrDigest :: binary(), rsa_digest_type() | dss_digest_type(),
- rsa_private_key() |
+-spec sign(binary() | {digest, binary()}, rsa_digest_type() | dss_digest_type(),
+ rsa_private_key() |
dsa_private_key()) -> Signature :: binary().
-%%
%% Description: Create digital signature.
%%--------------------------------------------------------------------
-sign(PlainText, DigestType,
- #'RSAPrivateKey'{modulus = N, publicExponent = E, privateExponent = D} = Key)
- when is_binary(PlainText),
- (DigestType == md5 orelse DigestType == sha),
- is_integer(N), is_integer(E), is_integer(D) ->
- crypto:rsa_sign(DigestType, sized_binary(PlainText),
- format_rsa_private_key(Key));
-
-sign(Digest, none, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X})
- when is_binary(Digest)->
- crypto:dss_sign(none, Digest,
- [crypto:mpint(P), crypto:mpint(Q),
+sign({digest,_}=Digest, DigestType, Key = #'RSAPrivateKey'{}) ->
+ crypto:rsa_sign(DigestType, Digest, format_rsa_private_key(Key));
+
+sign(PlainText, DigestType, Key = #'RSAPrivateKey'{}) ->
+ crypto:rsa_sign(DigestType, sized_binary(PlainText), format_rsa_private_key(Key));
+
+sign({digest,_}=Digest, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) ->
+ crypto:dss_sign(Digest,
+ [crypto:mpint(P), crypto:mpint(Q),
crypto:mpint(G), crypto:mpint(X)]);
-
-sign(PlainText, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X})
- when is_binary(PlainText) ->
- crypto:dss_sign(sized_binary(PlainText),
- [crypto:mpint(P), crypto:mpint(Q),
- crypto:mpint(G), crypto:mpint(X)]).
+
+sign(PlainText, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) ->
+ crypto:dss_sign(sized_binary(PlainText),
+ [crypto:mpint(P), crypto:mpint(Q),
+ crypto:mpint(G), crypto:mpint(X)]);
+
+%% Backwards compatible
+sign(Digest, none, #'DSAPrivateKey'{} = Key) ->
+ sign({digest,Digest}, sha, Key).
%%--------------------------------------------------------------------
--spec verify(PlainTextOrDigest :: binary(), rsa_digest_type() | dss_digest_type(),
- Signature :: binary(), rsa_public_key()
+-spec verify(binary() | {digest, binary()}, rsa_digest_type() | dss_digest_type(),
+ Signature :: binary(), rsa_public_key()
| dsa_public_key()) -> boolean().
-%%
%% Description: Verifies a digital signature.
%%--------------------------------------------------------------------
-verify(PlainText, DigestType, Signature,
- #'RSAPublicKey'{modulus = Mod, publicExponent = Exp})
- when is_binary (PlainText) and (DigestType == sha orelse
- DigestType == sha256 orelse
- DigestType == sha512 orelse
- DigestType == md5) ->
+verify({digest,_}=Digest, DigestType, Signature,
+ #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}) ->
+ crypto:rsa_verify(DigestType, Digest,
+ sized_binary(Signature),
+ [crypto:mpint(Exp), crypto:mpint(Mod)]);
+
+verify(PlainText, DigestType, Signature,
+ #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}) ->
crypto:rsa_verify(DigestType,
sized_binary(PlainText),
sized_binary(Signature),
[crypto:mpint(Exp), crypto:mpint(Mod)]);
-verify(Digest, none, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}})
- when is_integer(Key), is_binary(Digest), is_binary(Signature) ->
- crypto:dss_verify(none,
- Digest,
- sized_binary(Signature),
+verify({digest,_}=Digest, sha, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}})
+ when is_integer(Key), is_binary(Signature) ->
+ crypto:dss_verify(Digest, sized_binary(Signature),
[crypto:mpint(P), crypto:mpint(Q),
crypto:mpint(G), crypto:mpint(Key)]);
-
+%% Backwards compatibility
+verify(Digest, none, Signature, {_, #'Dss-Parms'{}} = Key ) ->
+ verify({digest,Digest}, sha, Signature, Key);
+
verify(PlainText, sha, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}})
- when is_integer(Key), is_binary(PlainText), is_binary(Signature) ->
+ when is_integer(Key), is_binary(PlainText), is_binary(Signature) ->
crypto:dss_verify(sized_binary(PlainText),
sized_binary(Signature),
[crypto:mpint(P), crypto:mpint(Q),
diff --git a/lib/public_key/test/Makefile b/lib/public_key/test/Makefile
index 41d77f103b..929d321d69 100644
--- a/lib/public_key/test/Makefile
+++ b/lib/public_key/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2011. All Rights Reserved.
+# Copyright Ericsson AB 2008-2012. 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/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl
index a91dcfa029..f2f30dad6e 100644
--- a/lib/public_key/test/public_key_SUITE.erl
+++ b/lib/public_key/test/public_key_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. 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
@@ -107,7 +107,7 @@ all() ->
{group, ssh_public_key_decode_encode},
encrypt_decrypt,
{group, sign_verify},
- pkix, pkix_path_validation].
+ pkix, pkix_countryname, pkix_path_validation].
groups() ->
[{pem_decode_encode, [], [dsa_pem, rsa_pem, encrypted_pem,
@@ -405,9 +405,20 @@ ssh_known_hosts(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
{ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "known_hosts")),
- [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2}] = Decoded =
+ [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2},
+ {#'RSAPublicKey'{}, Attributes3}, {#'RSAPublicKey'{}, Attributes4}] = Decoded =
public_key:ssh_decode(SshKnownHosts, known_hosts),
+ Comment1 = undefined,
+ Comment2 = "[email protected]",
+ Comment3 = "Comment with whitespaces",
+ Comment4 = "[email protected] Comment with whitespaces",
+
+ Comment1 = proplists:get_value(comment, Attributes1, undefined),
+ Comment2 = proplists:get_value(comment, Attributes2),
+ Comment3 = proplists:get_value(comment, Attributes3),
+ Comment4 = proplists:get_value(comment, Attributes4),
+
Value1 = proplists:get_value(hostnames, Attributes1, undefined),
Value2 = proplists:get_value(hostnames, Attributes2, undefined),
true = (Value1 =/= undefined) and (Value2 =/= undefined),
@@ -425,13 +436,16 @@ ssh1_known_hosts(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
{ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "ssh1_known_hosts")),
- [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2}] = Decoded =
- public_key:ssh_decode(SshKnownHosts, known_hosts),
+ [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2},{#'RSAPublicKey'{}, Attributes3}]
+ = Decoded = public_key:ssh_decode(SshKnownHosts, known_hosts),
Value1 = proplists:get_value(hostnames, Attributes1, undefined),
Value2 = proplists:get_value(hostnames, Attributes2, undefined),
true = (Value1 =/= undefined) and (Value2 =/= undefined),
+ Comment ="dhopson@VMUbuntu-DSH comment with whitespaces",
+ Comment = proplists:get_value(comment, Attributes3),
+
Encoded = public_key:ssh_encode(Decoded, known_hosts),
Decoded = public_key:ssh_decode(Encoded, known_hosts).
@@ -444,12 +458,22 @@ ssh_auth_keys(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
{ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "auth_keys")),
- [{#'RSAPublicKey'{}, Attributes1}, {{_, #'Dss-Parms'{}}, _Attributes2}] = Decoded =
+ [{#'RSAPublicKey'{}, Attributes1}, {{_, #'Dss-Parms'{}}, Attributes2},
+ {#'RSAPublicKey'{}, Attributes3}, {{_, #'Dss-Parms'{}}, Attributes4}
+ ] = Decoded =
public_key:ssh_decode(SshAuthKeys, auth_keys),
Value1 = proplists:get_value(options, Attributes1, undefined),
true = Value1 =/= undefined,
+ Comment1 = Comment2 = "dhopson@VMUbuntu-DSH",
+ Comment3 = Comment4 ="dhopson@VMUbuntu-DSH comment with whitespaces",
+
+ Comment1 = proplists:get_value(comment, Attributes1),
+ Comment2 = proplists:get_value(comment, Attributes2),
+ Comment3 = proplists:get_value(comment, Attributes3),
+ Comment4 = proplists:get_value(comment, Attributes4),
+
Encoded = public_key:ssh_encode(Decoded, auth_keys),
Decoded = public_key:ssh_decode(Encoded, auth_keys).
@@ -462,13 +486,24 @@ ssh1_auth_keys(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
{ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "ssh1_auth_keys")),
- [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2}] = Decoded =
+ [{#'RSAPublicKey'{}, Attributes1},
+ {#'RSAPublicKey'{}, Attributes2}, {#'RSAPublicKey'{}, Attributes3},
+ {#'RSAPublicKey'{}, Attributes4}, {#'RSAPublicKey'{}, Attributes5}] = Decoded =
public_key:ssh_decode(SshAuthKeys, auth_keys),
- Value1 = proplists:get_value(bits, Attributes1, undefined),
- Value2 = proplists:get_value(bits, Attributes2, undefined),
+ Value1 = proplists:get_value(bits, Attributes2, undefined),
+ Value2 = proplists:get_value(bits, Attributes3, undefined),
true = (Value1 =/= undefined) and (Value2 =/= undefined),
+ Comment2 = Comment3 = "dhopson@VMUbuntu-DSH",
+ Comment4 = Comment5 ="dhopson@VMUbuntu-DSH comment with whitespaces",
+
+ undefined = proplists:get_value(comment, Attributes1, undefined),
+ Comment2 = proplists:get_value(comment, Attributes2),
+ Comment3 = proplists:get_value(comment, Attributes3),
+ Comment4 = proplists:get_value(comment, Attributes4),
+ Comment5 = proplists:get_value(comment, Attributes5),
+
Encoded = public_key:ssh_encode(Decoded, auth_keys),
Decoded = public_key:ssh_decode(Encoded, auth_keys).
@@ -626,6 +661,34 @@ pkix(Config) when is_list(Config) ->
VerifyStr = public_key:pkix_normalize_name(TestStr),
ok.
+
+%%--------------------------------------------------------------------
+pkix_countryname(doc) ->
+ "Test workaround for certs that code x509countryname as utf8";
+pkix_countryname(suite) ->
+ [];
+pkix_countryname(Config) when is_list(Config) ->
+ Cert = incorrect_pkix_cert(),
+ OTPCert = public_key:pkix_decode_cert(Cert, otp),
+ TBSCert = OTPCert#'OTPCertificate'.tbsCertificate,
+ Issuer = TBSCert#'OTPTBSCertificate'.issuer,
+ Subj = TBSCert#'OTPTBSCertificate'.subject,
+ check_countryname(Issuer),
+ check_countryname(Subj).
+
+check_countryname({rdnSequence,DirName}) ->
+ do_check_countryname(DirName).
+do_check_countryname([]) ->
+ ok;
+do_check_countryname([#'AttributeTypeAndValue'{type = ?'id-at-countryName',
+ value = "US"}|_]) ->
+ ok;
+do_check_countryname([#'AttributeTypeAndValue'{type = ?'id-at-countryName',
+ value = Value}|_]) ->
+ test_server:fail({incorrect_cuntry_name, Value});
+do_check_countryname([_| Rest]) ->
+ do_check_countryname(Rest).
+
%%--------------------------------------------------------------------
pkix_path_validation(doc) ->
"Misc pkix tests not covered elsewhere";
@@ -716,3 +779,6 @@ check_entry_type(_,_) ->
strip_ending_newlines(Bin) ->
string:strip(binary_to_list(Bin), right, 10).
+
+incorrect_pkix_cert() ->
+ <<48,130,5,186,48,130,4,162,160,3,2,1,2,2,7,7,250,61,63,6,140,137,48,13,6,9,42, 134,72,134,247,13,1,1,5,5,0,48,129,220,49,11,48,9,6,3,85,4,6,19,2,85,83,49, 16,48,14,6,3,85,4,8,19,7,65,114,105,122,111,110,97,49,19,48,17,6,3,85,4,7,19, 10,83,99,111,116,116,115,100,97,108,101,49,37,48,35,6,3,85,4,10,19,28,83,116, 97,114,102,105,101,108,100,32,84,101,99,104,110,111,108,111,103,105,101,115, 44,32,73,110,99,46,49,57,48,55,6,3,85,4,11,19,48,104,116,116,112,58,47,47,99, 101,114,116,105,102,105,99,97,116,101,115,46,115,116,97,114,102,105,101,108, 100,116,101,99,104,46,99,111,109,47,114,101,112,111,115,105,116,111,114,121, 49,49,48,47,6,3,85,4,3,19,40,83,116,97,114,102,105,101,108,100,32,83,101,99, 117,114,101,32,67,101,114,116,105,102,105,99,97,116,105,111,110,32,65,117, 116,104,111,114,105,116,121,49,17,48,15,6,3,85,4,5,19,8,49,48,54,56,56,52,51, 53,48,30,23,13,49,48,49,48,50,51,48,49,51,50,48,53,90,23,13,49,50,49,48,50, 51,48,49,51,50,48,53,90,48,122,49,11,48,9,6,3,85,4,6,12,2,85,83,49,11,48,9,6, 3,85,4,8,12,2,65,90,49,19,48,17,6,3,85,4,7,12,10,83,99,111,116,116,115,100, 97,108,101,49,38,48,36,6,3,85,4,10,12,29,83,112,101,99,105,97,108,32,68,111, 109,97,105,110,32,83,101,114,118,105,99,101,115,44,32,73,110,99,46,49,33,48, 31,6,3,85,4,3,12,24,42,46,108,111,103,105,110,46,115,101,99,117,114,101,115, 101,114,118,101,114,46,110,101,116,48,130,1,34,48,13,6,9,42,134,72,134,247, 13,1,1,1,5,0,3,130,1,15,0,48,130,1,10,2,130,1,1,0,185,136,240,80,141,36,124, 245,182,130,73,19,188,74,166,117,72,228,185,209,43,129,244,40,44,193,231,11, 209,12,234,88,43,142,1,162,48,122,17,95,230,105,171,131,12,147,46,204,36,80, 250,171,33,253,35,62,83,22,71,212,186,141,14,198,89,89,121,204,224,122,246, 127,110,188,229,162,67,95,6,74,231,127,99,131,7,240,85,102,203,251,50,58,58, 104,245,103,181,183,134,32,203,121,232,54,32,188,139,136,112,166,126,14,91, 223,153,172,164,14,61,38,163,208,215,186,210,136,213,143,70,147,173,109,217, 250,169,108,31,211,104,238,103,93,182,59,165,43,196,189,218,241,30,148,240, 109,90,69,176,194,52,116,173,151,135,239,10,209,179,129,192,102,75,11,25,168, 223,32,174,84,223,134,70,167,55,172,143,27,130,123,226,226,7,34,142,166,39, 48,246,96,231,150,84,220,106,133,193,55,95,159,227,24,249,64,36,1,142,171,16, 202,55,126,7,156,15,194,22,116,53,113,174,104,239,203,120,45,131,57,87,84, 163,184,27,83,57,199,91,200,34,43,98,61,180,144,76,65,170,177,2,3,1,0,1,163, 130,1,224,48,130,1,220,48,15,6,3,85,29,19,1,1,255,4,5,48,3,1,1,0,48,29,6,3, 85,29,37,4,22,48,20,6,8,43,6,1,5,5,7,3,1,6,8,43,6,1,5,5,7,3,2,48,14,6,3,85, 29,15,1,1,255,4,4,3,2,5,160,48,56,6,3,85,29,31,4,49,48,47,48,45,160,43,160, 41,134,39,104,116,116,112,58,47,47,99,114,108,46,115,116,97,114,102,105,101, 108,100,116,101,99,104,46,99,111,109,47,115,102,115,50,45,48,46,99,114,108, 48,83,6,3,85,29,32,4,76,48,74,48,72,6,11,96,134,72,1,134,253,110,1,7,23,2,48, 57,48,55,6,8,43,6,1,5,5,7,2,1,22,43,104,116,116,112,115,58,47,47,99,101,114, 116,115,46,115,116,97,114,102,105,101,108,100,116,101,99,104,46,99,111,109, 47,114,101,112,111,115,105,116,111,114,121,47,48,129,141,6,8,43,6,1,5,5,7,1, 1,4,129,128,48,126,48,42,6,8,43,6,1,5,5,7,48,1,134,30,104,116,116,112,58,47, 47,111,99,115,112,46,115,116,97,114,102,105,101,108,100,116,101,99,104,46,99, 111,109,47,48,80,6,8,43,6,1,5,5,7,48,2,134,68,104,116,116,112,58,47,47,99, 101,114,116,105,102,105,99,97,116,101,115,46,115,116,97,114,102,105,101,108, 100,116,101,99,104,46,99,111,109,47,114,101,112,111,115,105,116,111,114,121, 47,115,102,95,105,110,116,101,114,109,101,100,105,97,116,101,46,99,114,116, 48,31,6,3,85,29,35,4,24,48,22,128,20,73,75,82,39,209,27,188,242,161,33,106, 98,123,81,66,122,138,215,213,86,48,59,6,3,85,29,17,4,52,48,50,130,24,42,46, 108,111,103,105,110,46,115,101,99,117,114,101,115,101,114,118,101,114,46,110, 101,116,130,22,108,111,103,105,110,46,115,101,99,117,114,101,115,101,114,118, 101,114,46,110,101,116,48,29,6,3,85,29,14,4,22,4,20,138,233,191,208,157,203, 249,85,242,239,20,195,48,10,148,49,144,101,255,116,48,13,6,9,42,134,72,134, 247,13,1,1,5,5,0,3,130,1,1,0,82,31,121,162,49,50,143,26,167,202,143,61,71, 189,201,199,57,81,122,116,90,192,88,24,102,194,174,48,157,74,27,87,210,223, 253,93,3,91,150,109,120,1,110,27,11,200,198,141,222,246,14,200,71,105,41,138, 13,114,122,106,63,17,197,181,234,121,61,89,74,65,41,231,248,219,129,83,176, 219,55,107,55,211,112,98,38,49,69,77,96,221,108,123,152,12,210,159,157,141, 43,226,55,187,129,3,82,49,136,66,81,196,91,234,196,10,82,48,6,80,163,83,71, 127,102,177,93,209,129,26,104,2,84,24,255,248,161,3,244,169,234,92,122,110, 43,4,17,113,185,235,108,219,210,236,132,216,177,227,17,169,58,162,159,182, 162,93,160,229,200,9,163,229,110,121,240,168,232,14,91,214,188,196,109,210, 164,222,0,109,139,132,113,91,16,118,173,178,176,80,132,34,41,199,51,206,250, 224,132,60,115,192,94,107,163,219,212,226,225,65,169,148,108,213,46,174,173, 103,110,189,229,166,149,254,31,51,44,144,108,187,182,11,251,201,206,86,138, 208,59,51,86,132,235,81,225,88,34,190,8,184>>.
diff --git a/lib/public_key/test/public_key_SUITE_data/auth_keys b/lib/public_key/test/public_key_SUITE_data/auth_keys
index 0c4b47edde..8be7357a06 100644
--- a/lib/public_key/test/public_key_SUITE_data/auth_keys
+++ b/lib/public_key/test/public_key_SUITE_data/auth_keys
@@ -1,3 +1,7 @@
command="dump /home",no-pty,no-port-forwarding ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAgEAwrr66r8n6B8Y0zMF3dOpXEapIQD9DiYQ6D6/zwor9o39jSkHNiMMER/GETBbzP83LOcekm02aRjo55ArO7gPPVvCXbrirJu9pkm4AC4BBre5xSLS7soyzwbigFruM8G63jSXqpHqJ/ooi168sKMC2b0Ncsi+JlTfNYlDXJVLKEeZgZOInQyMmtisaDTUQWTIv1snAizf4iIYENuAkGYGNCL77u5Y5VOu5eQipvFajTnps9QvUx/zdSFYn9e2sulWM3Bxc/S4IJ67JWHVRpfJxGi3hinRBH8WQdXuUwdJJTiJHKPyYrrM7Q6Xq4TOMFtcRuLDC6u3BXM1L0gBvHPNOnD5l2Lp5EjUkQ9CBf2j4A4gfH+iWQZyk08esAG/iwArAVxkl368+dkbMWOXL8BN4x5zYgdzoeypQZZ2RKH780MCTSo4WQ19DP8pw+9q3bSFC9H3xYAxrKAJNWjeTUJOTrTe+mWXXU770gYyQTxa2ycnYrlZucn1S3vsvn6eq7NZZ8NRbyv1n15Ocg+nHK4fuKOrwPhU3NbKQwtjb0Wsxx1gAmQqIOLTpAdsrAauPxC7TPYA5qQVCphvimKuhQM/1gMV225JrnjspVlthCzuFYUjXOKC3wxz6FFEtwnXu3uC5bVVkmkNadJmD21gD23yk4BraGXVYpRMIB+X+OTUUI8= dhopson@VMUbuntu-DSH
ssh-dss AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbETW6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdHYI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5cvwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGfJ0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAAvioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACBAN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HSn24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV dhopson@VMUbuntu-DSH
+
+command="dump /home",no-pty,no-port-forwarding ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAgEAwrr66r8n6B8Y0zMF3dOpXEapIQD9DiYQ6D6/zwor9o39jSkHNiMMER/GETBbzP83LOcekm02aRjo55ArO7gPPVvCXbrirJu9pkm4AC4BBre5xSLS7soyzwbigFruM8G63jSXqpHqJ/ooi168sKMC2b0Ncsi+JlTfNYlDXJVLKEeZgZOInQyMmtisaDTUQWTIv1snAizf4iIYENuAkGYGNCL77u5Y5VOu5eQipvFajTnps9QvUx/zdSFYn9e2sulWM3Bxc/S4IJ67JWHVRpfJxGi3hinRBH8WQdXuUwdJJTiJHKPyYrrM7Q6Xq4TOMFtcRuLDC6u3BXM1L0gBvHPNOnD5l2Lp5EjUkQ9CBf2j4A4gfH+iWQZyk08esAG/iwArAVxkl368+dkbMWOXL8BN4x5zYgdzoeypQZZ2RKH780MCTSo4WQ19DP8pw+9q3bSFC9H3xYAxrKAJNWjeTUJOTrTe+mWXXU770gYyQTxa2ycnYrlZucn1S3vsvn6eq7NZZ8NRbyv1n15Ocg+nHK4fuKOrwPhU3NbKQwtjb0Wsxx1gAmQqIOLTpAdsrAauPxC7TPYA5qQVCphvimKuhQM/1gMV225JrnjspVlthCzuFYUjXOKC3wxz6FFEtwnXu3uC5bVVkmkNadJmD21gD23yk4BraGXVYpRMIB+X+OTUUI8= dhopson@VMUbuntu-DSH comment with whitespaces
+
+ssh-dss AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbETW6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdHYI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5cvwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGfJ0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAAvioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACBAN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HSn24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV dhopson@VMUbuntu-DSH comment with whitespaces
diff --git a/lib/public_key/test/public_key_SUITE_data/known_hosts b/lib/public_key/test/public_key_SUITE_data/known_hosts
index 30fc3b1fe8..3c3af68178 100644
--- a/lib/public_key/test/public_key_SUITE_data/known_hosts
+++ b/lib/public_key/test/public_key_SUITE_data/known_hosts
@@ -1,3 +1,8 @@
hostname.domain.com,192.168.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1XY18+zA8VNK2YkzygOkMqUxHSTfxT1Xxx8CgDZgcQH8HUhPssW5ttvG8nKetlPQZAVk1C4WkWS1y5b3ekBhZTIxocp9Joc6V1+f2EOfO2mSLRwB16RGrdw6q7msrBXTC/dl+hF45kMMzVNzqxnSMVOa0sEPK2zK6Sg3Vi9fCSM=
|1|BWO5qDxk/cFH0wa05JLdHn+j6xQ=|rXQvIxh5cDD3C43k5DPDamawVNA= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1XY18+zA8VNK2YkzygOkMqUxHSTfxT1Xxx8CgDZgcQH8HUhPssW5ttvG8nKetlPQZAVk1C4WkWS1y5b3ekBhZTIxocp9Joc6V1+f2EOfO2mSLRwB16RGrdw6q7msrBXTC/dl+hF45kMMzVNzqxnSMVOa0sEPK2zK6Sg3Vi9fCSM= [email protected]
+
+hostname.domain.com,192.168.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1XY18+zA8VNK2YkzygOkMqUxHSTfxT1Xxx8CgDZgcQH8HUhPssW5ttvG8nKetlPQZAVk1C4WkWS1y5b3ekBhZTIxocp9Joc6V1+f2EOfO2mSLRwB16RGrdw6q7msrBXTC/dl+hF45kMMzVNzqxnSMVOa0sEPK2zK6Sg3Vi9fCSM= Comment with whitespaces
+
+|1|BWO5qDxk/cFH0wa05JLdHn+j6xQ=|rXQvIxh5cDD3C43k5DPDamawVNA= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1XY18+zA8VNK2YkzygOkMqUxHSTfxT1Xxx8CgDZgcQH8HUhPssW5ttvG8nKetlPQZAVk1C4WkWS1y5b3ekBhZTIxocp9Joc6V1+f2EOfO2mSLRwB16RGrdw6q7msrBXTC/dl+hF45kMMzVNzqxnSMVOa0sEPK2zK6Sg3Vi9fCSM= [email protected] Comment with whitespaces
+
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys b/lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys
index c91f4e4679..ac3d61b4c7 100644
--- a/lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys
+++ b/lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys
@@ -1,3 +1,9 @@
+1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663
+
1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH
command="dump /home",no-pty,no-port-forwarding 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH
+
+1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH comment with whitespaces
+
+command="dump /home",no-pty,no-port-forwarding 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH comment with whitespaces
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts b/lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts
index ec668fe05b..835b16ab67 100644
--- a/lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts
+++ b/lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts
@@ -1,2 +1,3 @@
hostname.domain.com,192.168.0.1 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH
hostname2.domain.com,192.168.0.2 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663
+hostname3.domain.com,192.168.0.3 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH comment with whitespaces
diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk
index ab4ee8b0ff..c8165fa247 100644
--- a/lib/public_key/vsn.mk
+++ b/lib/public_key/vsn.mk
@@ -1 +1 @@
-PUBLIC_KEY_VSN = 0.15
+PUBLIC_KEY_VSN = 0.16
diff --git a/lib/reltool/doc/src/Makefile b/lib/reltool/doc/src/Makefile
index 0b85b7e02c..2373b5e507 100644
--- a/lib/reltool/doc/src/Makefile
+++ b/lib/reltool/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2010. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. 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/lib/reltool/doc/src/notes.xml b/lib/reltool/doc/src/notes.xml
index 0aae128c2b..66ec7e9035 100644
--- a/lib/reltool/doc/src/notes.xml
+++ b/lib/reltool/doc/src/notes.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2009</year>
- <year>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -37,7 +37,28 @@
thus constitutes one section in this document. The title of each
section is the version number of Reltool.</p>
- <section><title>Reltool 0.6</title>
+ <section><title>Reltool 0.6.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ A new sys level configuration parameter
+ <c>{excl_lib,otp_root}</c> is added. When this is set,
+ the target system will not contain anything from
+ <c>$OTP_ROOT</c>. It will only contain a releases
+ directory with rel, script and boot files, and a lib
+ directory with applications found outside of
+ <c>$OTP_ROOT</c> (i.e. "your own" applications).</p>
+ <p>
+ Own Id: OTP-9743</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Reltool 0.6</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml
index 9a4e2d130e..fbe29753be 100644
--- a/lib/reltool/doc/src/reltool.xml
+++ b/lib/reltool/doc/src/reltool.xml
@@ -51,8 +51,18 @@
defines library directories where additional applications
may reside and it defaults to the directories
listed by the operating system environment variable
- <c>ERL_LIBS</c>. See the module <c>code</c> for more info.
- Finally single modules and entire applications may be read from
+ <c>ERL_LIBS</c>. See the module <c>code</c> for more info.</p>
+
+ <p>An application directory <c>AppDir</c> under a library
+ directory is recognized by the existence of an <c>AppDir/ebin</c>
+ directory. If this does not exist, <c>reltool</c> will not
+ consider <c>AppDir</c> at all when looking for applications.</p>
+
+ <p>It is recommended that application directories are named as the
+ application, possibly followed by a dash and the version
+ number. For example <c>myapp</c> or <c>myapp-1.1</c>.</p>
+
+ <p>Finally single modules and entire applications may be read from
Escripts.</p>
<p>Some configuration parameters control the behavior of Reltool
@@ -144,7 +154,7 @@
value <c>include</c> implies that all applications and
escripts that do not have any explicit <c>incl_cond</c>
setting will be included. <c>exclude</c> implies that all
- applications and escripts) that do not have any explicit
+ applications and escripts that do not have any explicit
<c>incl_cond</c> setting will be excluded.</p>
</item>
@@ -221,6 +231,52 @@
system.</p>
</item>
+ <tag><c>excl_lib</c></tag>
+ <item>
+ <warning><p>This option is experimental.</p></warning>
+ <p>If the <c>excl_lib</c> option is set to <c>otp_root</c>
+ then reltool will not copy anything from the Erlang/OTP
+ installation ($OTP_ROOT) into the target structure. The goal
+ is to create a "slim" release which can be used together with
+ an existing Erlang/OTP installation. The target structure will
+ therefore only contain a <c>lib</c> directory with the
+ applications that were found outside of $OTP_ROOT (typically
+ your own applications), and a <c>releases</c> directory with
+ the generated <c>.rel,</c> <c>.script</c> and <c>.boot</c>
+ files.</p>
+
+ <p>When starting this release, three things must be specified:</p>
+ <taglist>
+ <tag><b>Which <c>releases</c> directory to use</b></tag>
+ <item>Tell the release handler to use the <c>releases</c>
+ directory in our target structure instead of
+ <c>$OTP_ROOT/releases</c>. This is done by setting the SASL
+ environment variable <c>releases_dir</c>, either from the
+ command line (<c>-sasl releases_dir
+ &lt;target-dir&gt;/releases</c>) or in
+ <c>sys.config</c>.</item>
+
+ <tag><b>Which boot file to use</b></tag>
+ <item>The default boot file is <c>$OTP_ROOT/bin/start</c>,
+ but in this case we need to specify a boot file from our
+ target structure, typically
+ <c>&lt;target-dir&gt;/releases/&lt;vsn&gt;/&lt;RelName&gt;</c>. This
+ is done with the <c>-boot</c> command line option to
+ <c>erl</c></item>
+
+ <tag><b>The location of our applications</b></tag>
+ <item>The generated .script (and .boot) file uses the
+ environment variable <c>$RELTOOL_EXT_LIB</c> as prefix for
+ the paths to all applications. The <c>-boot_var</c> option
+ to <c>erl</c> can be used for specifying the value of this
+ variable, typically <c>-boot_var RELTOOL_EXT_LIB
+ &lt;target-dir&gt;/lib</c>.</item>
+ </taglist>
+
+ <p>Example:</p>
+ <p><code>erl -sasl releases_dir \"mytarget/releases\" -boot mytarget/releases/1.0/myrel -boot_var RELTOOL_EXT_LIB mytarget/lib</code></p>
+ </item>
+
<tag><c>incl_sys_filters</c></tag>
<item>
<p>This parameter normally contains a list of regular
@@ -326,6 +382,11 @@
<p>This parameter is mutual exclusive with <c>lib_dir</c>. If
<c>vsn</c> and <c>lib_dir</c> are both omitted, the latest version
will be chosen.</p>
+ <p>Note that in order for reltool to sort application versions
+ and thereby be able to select the latest, it is required that
+ the version id for the application consits of integers and
+ dots only, for example <c>1</c>, <c>2.0</c> or
+ <c>3.17.1</c>.</p>
</item>
<tag><c>lib_dir</c></tag>
<item>
@@ -337,6 +398,11 @@
<p>This parameter is mutual exclusive with <c>vsn</c>. If
<c>vsn</c> and <c>lib_dir</c> are both omitted, the latest version
will be chosen.</p>
+ <p>Note that in order for reltool to sort application versions
+ and thereby be able to select the latest, it is required that
+ the version id for the application consits of integers and
+ dots only, for example <c>1</c>, <c>2.0</c> or
+ <c>3.17.1</c>.</p>
</item>
<tag><c>mod</c></tag>
<item>
@@ -400,7 +466,7 @@
<tag><c>incl_cond</c></tag>
<item>
<p>This parameter controls whether the module is included or not. By
- default the <c>mod_incl</c> parameter on application and system level
+ default the <c>mod_cond</c> parameter on application and system level
will be used to control whether the module is included or not. The
value of <c>incl_cond</c> overrides the module inclusion policy.
<c>include</c> implies that the module is included, while
@@ -452,6 +518,7 @@ app() = {vsn, app_vsn()}
| {incl_cond, incl_cond()}
| {debug_info, debug_info()}
| {app_file, app_file()}
+ | {excl_lib, excl_lib()}
| {incl_sys_filters, incl_sys_filters()}
| {excl_sys_filters, excl_sys_filters()}
| {incl_app_filters, incl_app_filters()}
@@ -477,6 +544,7 @@ escript() = {incl_cond, incl_cond()}
escript_file() = file()
excl_app_filters() = regexps()
excl_archive_filters() = regexps()
+excl_lib() = otp_root
excl_sys_filters() = regexps()
file() = string()
incl_app() = app_name()
diff --git a/lib/reltool/doc/src/reltool_usage.xml b/lib/reltool/doc/src/reltool_usage.xml
index d128e80a77..0041e60d8f 100644
--- a/lib/reltool/doc/src/reltool_usage.xml
+++ b/lib/reltool/doc/src/reltool_usage.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2009</year>
- <year>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -257,6 +257,11 @@
policy</c> part of the page. By default the latest version of the
application is selected, but it is possible to override this by
explicitly selecting another version.</p>
+
+ <p>Note that in order for reltool to sort application versions and
+ thereby be able to select the latest, it is required that the
+ version id for the application consits of integers and dots only,
+ for example <c>1</c>, <c>2.0</c> or <c>3.17.1</c>.</p>
<p>By default the <c>Application inclusion policy</c> on system
level is used for all applications. Set the value to
@@ -335,7 +340,7 @@
<p>There are two categories of modules on the <c>Module
dependencies</c> page. If the module is used by other modules,
- these are listed under <c>Modules used by others</c>. If the
+ these are listed under <c>Modules using this</c>. If the
module uses other modules, these are listed under <c>Used
modules</c>.</p>
@@ -365,7 +370,7 @@
<p>There are two categories of modules on the <c>Dependencies</c>
page. If the module is used by other modules, these are listed
- under <c>Modules used by others</c>. If the module uses other
+ under <c>Modules using this</c>. If the module uses other
modules, these are listed under <c>Used modules</c>.</p>
<p>Double click on an module name to launch a module window.</p>
diff --git a/lib/reltool/examples/Makefile b/lib/reltool/examples/Makefile
index afc47e1d12..0b495b14b7 100644
--- a/lib/reltool/examples/Makefile
+++ b/lib/reltool/examples/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. 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/lib/reltool/src/Makefile b/lib/reltool/src/Makefile
index 4730b4acff..a7e34053f1 100644
--- a/lib/reltool/src/Makefile
+++ b/lib/reltool/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2010. All Rights Reserved.
+# Copyright Ericsson AB 2009-2012. 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/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl
index 26dcd82447..f0d8b38519 100644
--- a/lib/reltool/src/reltool.hrl
+++ b/lib/reltool/src/reltool.hrl
@@ -79,6 +79,7 @@
| {debug_info, debug_info()}
| {app_file, app_file()}
| {profile, profile()}
+ | {excl_lib, excl_lib()}
| {incl_sys_filters, incl_sys_filters()}
| {excl_sys_filters, excl_sys_filters()}
| {incl_app_filters, incl_app_filters()}
@@ -123,6 +124,7 @@
-type incl_defaults() :: boolean().
-type incl_derived() :: boolean().
-type status() :: missing | ok.
+-type excl_lib() :: otp_root.
-record(common,
{
@@ -233,6 +235,7 @@
rels :: [#rel{}],
emu_name :: emu_name(),
profile :: profile(),
+ excl_lib :: excl_lib(),
incl_sys_filters :: [#regexp{}],
excl_sys_filters :: [#regexp{}],
incl_app_filters :: [#regexp{}],
diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl
index 034a42e1e2..c56e29152d 100644
--- a/lib/reltool/src/reltool_server.erl
+++ b/lib/reltool/src/reltool_server.erl
@@ -674,6 +674,8 @@ mod_init_is_included(ModTab, M, ModCond, AppCond, Default, Status) ->
true;
exclude ->
false;
+ derived ->
+ undefined;
undefined ->
%% print(M#mod.name, hipe, "mod_cond -> ~p\n",
%% [ModCond]),
@@ -693,6 +695,8 @@ mod_init_is_included(ModTab, M, ModCond, AppCond, Default, Status) ->
true;
exclude ->
false;
+ derived ->
+ undefined;
undefined ->
Default
end
@@ -783,9 +787,10 @@ mod_mark_is_included(#state{app_tab=AppTab, mod_tab=ModTab, sys=Sys} = S,
M#mod{is_pre_included = true,
is_included = true};
exclude ->
- M#mod{is_pre_included = true,
- is_included = true};
- undefined ->
+ M#mod{is_pre_included = false,
+ is_included = false};
+ ModInclCond when ModInclCond==undefined;
+ ModInclCond==derived ->
M#mod{is_included = true}
end,
ets:insert(ModTab, M2),
@@ -979,7 +984,7 @@ refresh_app(#app{name = AppName,
%% Add info from .app file
Base = get_base(AppName, ActiveDir),
- {_, DefaultVsn} = reltool_utils:split_app_name(Base),
+ DefaultVsn = get_vsn_from_dir(AppName,Base),
Ebin = filename:join([ActiveDir, "ebin"]),
AppFile =
filename:join([Ebin,
@@ -1408,6 +1413,8 @@ decode(#sys{} = Sys, [{Key, Val} | KeyVals]) ->
ExclApp,
Sys#sys.excl_app_filters),
embedded_app_type = AppType};
+ excl_lib when Val =:= otp_root ->
+ Sys#sys{excl_lib=Val};
incl_sys_filters ->
Sys#sys{incl_sys_filters =
dec_re(Key, Val, Sys#sys.incl_sys_filters)};
@@ -1678,8 +1685,7 @@ app_dirs2([Lib | Libs], Acc) ->
EbinDir = filename:join([AppDir, "ebin"]),
case filelib:is_dir(EbinDir, erl_prim_loader) of
true ->
- {Name, _Vsn} =
- reltool_utils:split_app_name(Base),
+ Name = find_app_name(Base,EbinDir),
case Name of
erts -> false;
_ -> {true, {Name, AppDir}}
@@ -1697,17 +1703,74 @@ app_dirs2([Lib | Libs], Acc) ->
app_dirs2([], Acc) ->
lists:sort(lists:append(Acc)).
+find_app_name(Base,EbinDir) ->
+ {ok,EbinFiles} = erl_prim_loader:list_dir(EbinDir),
+ AppFile =
+ case [F || F <- EbinFiles, filename:extension(F)=:=".app"] of
+ [AF] ->
+ AF;
+ _ ->
+ undefined
+ end,
+ find_app_name1(Base,AppFile).
+
+find_app_name1(Base,undefined) ->
+ {Name,_} = reltool_utils:split_app_name(Base),
+ Name;
+find_app_name1(_Base,AppFile) ->
+ list_to_atom(filename:rootname(AppFile)).
+
+get_vsn_from_dir(AppName,Base) ->
+ Prefix = atom_to_list(AppName) ++ "-",
+ case lists:prefix(Prefix,Base) of
+ true ->
+ lists:nthtail(length(Prefix),Base);
+ false ->
+ ""
+ end.
+
+
escripts_to_apps([Escript | Escripts], Apps, Status) ->
{EscriptAppName, _Label} = split_escript_name(Escript),
Ext = code:objfile_extension(),
+
+ %% First find all .app files and associate the app name to the app
+ %% label - this is in order to now which application a module
+ %% belongs to in the next round.
+ AppFun = fun(FullName, _GetInfo, _GetBin, AppFiles) ->
+ Components = filename:split(FullName),
+ case Components of
+ [AppLabel, "ebin", File] ->
+ case filename:extension(File) of
+ ".app" ->
+ [{AppLabel,File}|AppFiles];
+ _ ->
+ AppFiles
+ end;
+ _ ->
+ AppFiles
+ end
+ end,
+ AppFiles =
+ case reltool_utils:escript_foldl(AppFun, [], Escript) of
+ {ok, AF} ->
+ AF;
+ {error, Reason1} ->
+ reltool_utils:throw_error("Illegal escript ~p: ~p",
+ [Escript,Reason1])
+ end,
+
+ %% Next, traverse all files...
Fun = fun(FullName, _GetInfo, GetBin, {FileAcc, StatusAcc}) ->
Components = filename:split(FullName),
case Components of
[AppLabel, "ebin", File] ->
case filename:extension(File) of
".app" ->
- {AppName, DefaultVsn} =
- reltool_utils:split_app_name(AppLabel),
+ AppName =
+ list_to_atom(filename:rootname(File)),
+ DefaultVsn =
+ get_vsn_from_dir(AppName,AppLabel),
AppFileName =
filename:join([Escript, FullName]),
{Info, StatusAcc2} =
@@ -1720,8 +1783,9 @@ escripts_to_apps([Escript | Escripts], Apps, Status) ->
{[{AppName, app, Dir, Info} | FileAcc],
StatusAcc2};
E when E =:= Ext ->
- {AppName, _} =
- reltool_utils:split_app_name(AppLabel),
+ AppFile =
+ proplists:get_value(AppLabel,AppFiles),
+ AppName = find_app_name1(AppLabel,AppFile),
Mod = init_mod(AppName,
File,
{File, GetBin()},
@@ -1758,6 +1822,7 @@ escripts_to_apps([Escript | Escripts], Apps, Status) ->
{FileAcc, StatusAcc}
end
end,
+
case reltool_utils:escript_foldl(Fun, {[], Status}, Escript) of
{ok, {Files, Status2}} ->
EscriptApp =
@@ -1772,8 +1837,9 @@ escripts_to_apps([Escript | Escripts], Apps, Status) ->
Apps,
Status2),
escripts_to_apps(Escripts, Apps2, Status3);
- {error, Reason} ->
- reltool_utils:throw_error("Illegal escript ~p: ~p", [Escript,Reason])
+ {error, Reason2} ->
+ reltool_utils:throw_error("Illegal escript ~p: ~p",
+ [Escript,Reason2])
end;
escripts_to_apps([], Apps, Status) ->
{Apps, Status}.
@@ -1932,7 +1998,7 @@ ensure_app_info(#app{name = Name,
fun(Dir, StatusAcc) ->
Base = get_base(Name, Dir),
Ebin = filename:join([Dir, "ebin"]),
- {_, DefaultVsn} = reltool_utils:split_app_name(Base),
+ DefaultVsn = get_vsn_from_dir(Name,Base),
AppFile = filename:join([Ebin, atom_to_list(Name) ++ ".app"]),
read_app_info(AppFile, AppFile, Name, DefaultVsn, StatusAcc)
end,
diff --git a/lib/reltool/src/reltool_target.erl b/lib/reltool/src/reltool_target.erl
index e3a7b02143..6cb7ba0163 100644
--- a/lib/reltool/src/reltool_target.erl
+++ b/lib/reltool/src/reltool_target.erl
@@ -333,7 +333,9 @@ merge_apps(#rel{name = RelName,
A#app.name =/= ?MISSING_APP_NAME,
not lists:keymember(A#app.name, #app.name, MergedApps2)],
MergedApps3 = do_merge_apps(RelName, Embedded, Apps, EmbAppType, MergedApps2),
- sort_apps(lists:reverse(MergedApps3)).
+ RevMerged = lists:reverse(MergedApps3),
+ MergedSortedUsedAndIncs = sort_used_and_incl_apps(RevMerged,RevMerged),
+ sort_apps(MergedSortedUsedAndIncs).
do_merge_apps(RelName, [#rel_app{name = Name} = RA | RelApps], Apps, RelAppType, Acc) ->
case is_already_merged(Name, RelApps, Acc) of
@@ -342,9 +344,11 @@ do_merge_apps(RelName, [#rel_app{name = Name} = RA | RelApps], Apps, RelAppType,
false ->
{value, App} = lists:keysearch(Name, #app.name, Apps),
MergedApp = merge_app(RelName, RA, RelAppType, App),
- MoreNames = (MergedApp#app.info)#app_info.applications,
+ ReqNames = (MergedApp#app.info)#app_info.applications,
+ IncNames = (MergedApp#app.info)#app_info.incl_apps,
Acc2 = [MergedApp | Acc],
- do_merge_apps(RelName, MoreNames ++ RelApps, Apps, RelAppType, Acc2)
+ do_merge_apps(RelName, ReqNames ++ IncNames ++ RelApps,
+ Apps, RelAppType, Acc2)
end;
do_merge_apps(RelName, [Name | RelApps], Apps, RelAppType, Acc) ->
case is_already_merged(Name, RelApps, Acc) of
@@ -507,6 +511,56 @@ load_app_mods(#app{mods = Mods} = App, Mand, PathFlag, Variables) ->
SplitMods).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% sort_used_and_incl_apps(Apps, OrderedApps) -> Apps
+%% Apps = [#app{}]
+%% OrderedApps = [#app{}]
+%%
+%% OTP-4121, OTP-9984
+%% (Tickets are written for systools, but needs to be implemented here
+%% as well.)
+%% Make sure that used and included applications are given in the same
+%% order as in the release resource file (.rel). Otherwise load and
+%% start instructions in the boot script, and consequently release
+%% upgrade instructions in relup, may end up in the wrong order.
+
+sort_used_and_incl_apps([#app{info=Info} = App|Apps], OrderedApps) ->
+ Incls2 =
+ case Info#app_info.incl_apps of
+ Incls when length(Incls)>1 ->
+ sort_appl_list(Incls, OrderedApps);
+ Incls ->
+ Incls
+ end,
+ Uses2 =
+ case Info#app_info.applications of
+ Uses when length(Uses)>1 ->
+ sort_appl_list(Uses, OrderedApps);
+ Uses ->
+ Uses
+ end,
+ App2 = App#app{info=Info#app_info{incl_apps=Incls2, applications=Uses2}},
+ [App2|sort_used_and_incl_apps(Apps, OrderedApps)];
+sort_used_and_incl_apps([], _OrderedApps) ->
+ [].
+
+sort_appl_list(List, Order) ->
+ IndexedList = find_pos(List, Order),
+ SortedIndexedList = lists:keysort(1, IndexedList),
+ lists:map(fun({_Index,Name}) -> Name end, SortedIndexedList).
+
+find_pos([Name|Incs], OrderedApps) ->
+ [find_pos(1, Name, OrderedApps)|find_pos(Incs, OrderedApps)];
+find_pos([], _OrderedApps) ->
+ [].
+
+find_pos(N, Name, [#app{name=Name}|_OrderedApps]) ->
+ {N, Name};
+find_pos(N, Name, [_OtherAppl|OrderedApps]) ->
+ find_pos(N+1, Name, OrderedApps).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Function: sort_apps(Apps) -> {ok, Apps'} | throw({error, Error})
%% Types: Apps = {{Name, Vsn}, #application}]
%% Purpose: Sort applications according to dependencies among
@@ -687,6 +741,8 @@ strip_name_ebin(Dir, Name, Vsn) ->
case lists:reverse(Dir) of
["ebin", Name | D] -> {ok, lists:reverse(D)};
["ebin", FullName | D] -> {ok, lists:reverse(D)};
+ [Name | D] -> {ok, lists:reverse(D)};
+ [FullName | D] -> {ok, lists:reverse(D)};
_ -> false
end.
@@ -733,8 +789,20 @@ do_spec_rel_files(#rel{name = RelName} = Rel, Sys) ->
BootFile = RelName ++ ".boot",
MergedApps = merge_apps(Rel, Sys),
GenRel = do_gen_rel(Rel, Sys, MergedApps),
+ Variables =
+ case Sys#sys.excl_lib of
+ otp_root ->
+ %% All applications that are fetched from somewhere
+ %% other than $OTP_ROOT/lib will get $RELTOOL_EXT_LIB
+ %% as path prefix in the .script file.
+ [{"RELTOOL_EXT_LIB",LibDir} || LibDir <- Sys#sys.lib_dirs] ++
+ [{"RELTOOL_EXT_LIB",filename:dirname(AppLibDir)} ||
+ #app{active_dir=AppLibDir,use_selected_vsn=dir}
+ <- MergedApps];
+ _ ->
+ []
+ end,
PathFlag = true,
- Variables = [],
{ok, Script} = do_gen_script(Rel, Sys, MergedApps, PathFlag, Variables),
{ok, BootBin} = gen_boot(Script),
Date = date(),
@@ -771,29 +839,34 @@ gen_spec(Sys) ->
end.
do_gen_spec(#sys{root_dir = RootDir,
+ excl_lib = ExclLib,
incl_sys_filters = InclRegexps,
excl_sys_filters = ExclRegexps,
relocatable = Relocatable,
apps = Apps} = Sys) ->
- {create_dir, _, SysFiles} = spec_dir(RootDir),
- {ExclRegexps2, SysFiles2} =
- strip_sys_files(Relocatable, SysFiles, Apps, ExclRegexps),
RelFiles = spec_rel_files(Sys),
- {InclRegexps2, BinFiles} =
- spec_bin_files(Sys, SysFiles, SysFiles2, RelFiles, InclRegexps),
+ {SysFiles, InclRegexps2, ExclRegexps2, Mandatory} =
+ case ExclLib of
+ otp_root ->
+ {[],InclRegexps,ExclRegexps,["lib"]};
+ _ ->
+ {create_dir, _, SF} = spec_dir(RootDir),
+ {ER2, SF2} = strip_sys_files(Relocatable, SF, Apps, ExclRegexps),
+ {IR2, BinFiles} =
+ spec_bin_files(Sys, SF, SF2, RelFiles, InclRegexps),
+ SF3 = [{create_dir, "bin", BinFiles}] ++ SF2,
+ {SF3,IR2,ER2,["bin","erts","lib"]}
+ end,
LibFiles = spec_lib_files(Sys),
{BootVsn, StartFile} = spec_start_file(Sys),
- SysFiles3 =
- [
- {create_dir, "releases",
+ SysFiles2 =
+ [{create_dir, "releases",
[StartFile,
- {create_dir,BootVsn, RelFiles}]},
- {create_dir, "bin", BinFiles}
- ] ++ SysFiles2,
- SysFiles4 = filter_spec(SysFiles3, InclRegexps2, ExclRegexps2),
- SysFiles5 = SysFiles4 ++ [{create_dir, "lib", LibFiles}],
- check_sys(["bin", "erts", "lib"], SysFiles5),
- SysFiles5.
+ {create_dir,BootVsn, RelFiles}]}] ++ SysFiles,
+ SysFiles3 = filter_spec(SysFiles2, InclRegexps2, ExclRegexps2),
+ SysFiles4 = SysFiles3 ++ [{create_dir, "lib", LibFiles}],
+ check_sys(Mandatory, SysFiles4),
+ SysFiles4.
strip_sys_files(Relocatable, SysFiles, Apps, ExclRegexps) ->
ExclRegexps2 =
@@ -967,22 +1040,35 @@ safe_lookup_spec(Prefix, Specs) ->
%% Specify applications
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec_lib_files(#sys{apps = Apps} = Sys) ->
+spec_lib_files(#sys{root_dir = RootDir,
+ apps = Apps,
+ excl_lib = ExclLib} = Sys) ->
Filter = fun(#app{is_escript = IsEscript, is_included = IsIncl,
- is_pre_included = IsPre, name = Name}) ->
+ is_pre_included = IsPre, name = Name,
+ active_dir = ActiveDir}) ->
if
Name =:= ?MISSING_APP_NAME ->
false;
IsEscript =/= false ->
false;
IsIncl; IsPre ->
- true;
+ case ExclLib of
+ otp_root ->
+ not lists:prefix(RootDir,ActiveDir);
+ _ ->
+ true
+ end;
true ->
false
end
end,
SelectedApps = lists:filter(Filter, Apps),
- check_apps([kernel, stdlib], SelectedApps),
+ case ExclLib of
+ otp_root ->
+ ok;
+ _ ->
+ check_apps([kernel, stdlib], SelectedApps)
+ end,
lists:flatten([spec_app(App, Sys) || App <- SelectedApps]).
check_apps([Mandatory | Names], Apps) ->
@@ -1388,12 +1474,10 @@ do_install(RelName, TargetDir) ->
BinDir = filename:join([TargetDir2, "bin"]),
case os:type() of
{win32, _} ->
- NativeRootDir = filename:nativename(TargetDir2),
- %% NativeBinDir =
- %% filename:nativename(filename:join([BinDir, "win32"])),
- NativeBinDir = filename:nativename(BinDir),
+ NativeRootDir = nativename(TargetDir2),
+ NativeErtsBinDir = nativename(ErtsBinDir),
IniData = ["[erlang]\r\n",
- "Bindir=", NativeBinDir, "\r\n",
+ "Bindir=", NativeErtsBinDir, "\r\n",
"Progname=erl\r\n",
"Rootdir=", NativeRootDir, "\r\n"],
IniFile = filename:join([BinDir, "erl.ini"]),
@@ -1413,6 +1497,15 @@ do_install(RelName, TargetDir) ->
reltool_utils:throw_error("~s: Illegal data file syntax", [DataFile])
end.
+nativename(Dir) ->
+ escape_backslash(filename:nativename(Dir)).
+escape_backslash([$\\|T]) ->
+ [$\\,$\\|escape_backslash(T)];
+escape_backslash([H|T]) ->
+ [H|escape_backslash(T)];
+escape_backslash([]) ->
+ [].
+
subst_src_scripts(Scripts, SrcDir, DestDir, Vars, Opts) ->
Fun = fun(Script) ->
subst_src_script(Script, SrcDir, DestDir, Vars, Opts)
diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl
index 8a98dc36cf..8d71865508 100644
--- a/lib/reltool/test/reltool_server_SUITE.erl
+++ b/lib/reltool/test/reltool_server_SUITE.erl
@@ -67,6 +67,7 @@ all() ->
create_standalone_app_clash,
create_multiple_standalone,
create_old_target,
+ create_slim,
eval_target_spec,
otp_9135,
otp_9229_dupl_mod_exclude_app,
@@ -89,8 +90,10 @@ all() ->
gen_rel_files,
save_config,
dependencies,
+ mod_incl_cond_derived,
use_selected_vsn,
- use_selected_vsn_relative_path].
+ use_selected_vsn_relative_path,
+ non_standard_vsn_id].
groups() ->
[].
@@ -105,6 +108,15 @@ end_per_group(_GroupName, Config) ->
%% The test cases
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% A dummy break test case which is NOT in all(), but can be run
+%% directly from the command line with ct_run. It just does a
+%% test_server:break()...
+break(_Config) ->
+ test_server:break(""),
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Start a server process and check that it does not crash
start_server(_Config) ->
@@ -297,7 +309,6 @@ create_release(_Config) ->
%% started before the including application.
%% Circular dependencies shall also be detected and cause error.
-create_release_sort(_Config) -> {skip, "Two bugs related to sorting"};
create_release_sort(Config) ->
DataDir = ?config(data_dir,Config),
%% Configure the server
@@ -306,11 +317,12 @@ create_release_sort(Config) ->
RelName3 = "Include-both",
RelName4 = "Include-only-app",
RelName5 = "Include-only-rel",
- RelName6 = "Include-missing-app",
+ RelName6 = "Auto-add-missing-apps",
RelName7 = "Circular",
- RelName8 = "Include-both-missing-app",
- RelName9 = "Include-overwrite",
+ RelName8 = "Include-rel-alter-order",
+ RelName9 = "Include-none-overwrite",
RelName10= "Uses-order-as-rel",
+ RelName11= "Auto-add-dont-overwrite-load",
RelVsn = "1.0",
%% Application z (.app file):
%% includes [tools, mnesia]
@@ -325,11 +337,12 @@ create_release_sort(Config) ->
{rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools, mnesia]},
{rel, RelName4, RelVsn, [stdlib, kernel, z, mnesia, tools]},
{rel, RelName5, RelVsn, [stdlib, kernel, {sasl,[tools]}]},
- {rel, RelName6, RelVsn, [stdlib, kernel, z]},
+ {rel, RelName6, RelVsn, [z]},
{rel, RelName7, RelVsn, [stdlib, kernel, mnesia, y, sasl, x]},
- {rel, RelName8, RelVsn, [stdlib, kernel, {z,[tools]}]},
+ {rel, RelName8, RelVsn, [stdlib, kernel, {z,[mnesia,tools]}]},
{rel, RelName9, RelVsn, [stdlib, kernel, {z,[]}]},
{rel, RelName10, RelVsn, [stdlib, kernel, {z,[]}, inets, sasl]},
+ {rel, RelName11, RelVsn, [stdlib, kernel, z, {inets, load}]},
{incl_cond,exclude},
{mod_cond,app},
{app,kernel,[{incl_cond,include}]},
@@ -371,7 +384,6 @@ create_release_sort(Config) ->
{mnesia, _}]}},
reltool:get_rel([{config, Sys}], RelName3)),
- %%! BUG: same as OTP-4121, but for reltool???? Or revert tools and mnesia
?msym({ok, {release, {RelName4, RelVsn},
{erts, _},
[{kernel, _},
@@ -388,13 +400,29 @@ create_release_sort(Config) ->
"in the app file: [tools]"},
reltool:get_rel([{config, Sys}], RelName5)),
- ?m({error, "Undefined applications: [tools,mnesia]"},
+ ?msym({ok, {release, {RelName6, RelVsn},
+ {erts, _},
+ [{kernel, _},
+ {stdlib, _},
+ {sasl, _},
+ {inets, _},
+ {tools, _},
+ {mnesia, _},
+ {z, _}]}},
reltool:get_rel([{config, Sys}], RelName6)),
?m({error,"Circular dependencies: [x,y]"},
reltool:get_rel([{config, Sys}], RelName7)),
- ?m({error,"Undefined applications: [tools]"},
+ ?msym({ok, {release, {RelName8, RelVsn},
+ {erts, _},
+ [{kernel, _},
+ {stdlib, _},
+ {sasl, _},
+ {inets, _},
+ {mnesia, _},
+ {tools, _},
+ {z, _, [mnesia,tools]}]}},
reltool:get_rel([{config, Sys}], RelName8)),
?msym({ok,{release,{RelName9,RelVsn},
@@ -406,7 +434,6 @@ create_release_sort(Config) ->
{z,_,[]}]}},
reltool:get_rel([{config, Sys}], RelName9)),
- %%! BUG: same as OTP-9984, but for reltool???? Or revert inets and sasl?
?msym({ok,{release,{RelName10,RelVsn},
{erts,_},
[{kernel,_},
@@ -416,6 +443,17 @@ create_release_sort(Config) ->
{z,_,[]}]}},
reltool:get_rel([{config, Sys}], RelName10)),
+ ?msym({ok,{release,{RelName11,RelVsn},
+ {erts,_},
+ [{kernel,_},
+ {stdlib,_},
+ {sasl, _},
+ {inets, _, load},
+ {tools, _},
+ {mnesia, _},
+ {z,_}]}},
+ reltool:get_rel([{config, Sys}], RelName11)),
+
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -476,12 +514,16 @@ create_script_sort(Config) ->
RelName3 = "Include-both",
RelName4 = "Include-only-app",
RelName5 = "Include-only-rel",
- RelName6 = "Include-missing-app",
+ RelName6 = "Auto-add-missing-apps",
RelName7 = "Circular",
- RelName8 = "Include-both-missing-app",
- RelName9 = "Include-overwrite",
+ RelName8 = "Include-rel-alter-order",
+ RelName9 = "Include-none-overwrite",
+ RelName10= "Uses-order-as-rel",
RelVsn = "1.0",
LibDir = filename:join(DataDir,"sort_apps"),
+ %% Application z (.app file):
+ %% includes [tools, mnesia]
+ %% uses [kernel, stdlib, sasl, inets]
Sys =
{sys,
[
@@ -492,10 +534,11 @@ create_script_sort(Config) ->
{rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools, mnesia]},
{rel, RelName4, RelVsn, [stdlib, kernel, z, mnesia, tools]},
{rel, RelName5, RelVsn, [stdlib, kernel, {sasl,[tools]}]},
- {rel, RelName6, RelVsn, [stdlib, kernel, z]},
+ {rel, RelName6, RelVsn, [z]},
{rel, RelName7, RelVsn, [stdlib, kernel, mnesia, y, sasl, x]},
- {rel, RelName8, RelVsn, [stdlib, kernel, {z,[tools]}]},
+ {rel, RelName8, RelVsn, [stdlib, kernel, {z,[mnesia,tools]}]},
{rel, RelName9, RelVsn, [stdlib, kernel, {z,[]}]},
+ {rel, RelName10, RelVsn, [stdlib, kernel, {z,[]}, inets, sasl]},
{incl_cond,exclude},
{mod_cond,app},
{app,kernel,[{incl_cond,include}]},
@@ -552,8 +595,8 @@ create_script_sort(Config) ->
[{kernel,KernelVsn},
{stdlib,StdlibVsn},
{z,"1.0"},
- {tools,ToolsVsn},
{mnesia,MnesiaVsn},
+ {tools,ToolsVsn},
{sasl,SaslVsn},
{inets,InetsVsn}]},
FullName4 = filename:join(?WORK_DIR,RelName4),
@@ -568,6 +611,10 @@ create_script_sort(Config) ->
Rel6 = {release, {RelName6,RelVsn}, {erts,ErtsVsn},
[{kernel,KernelVsn},
{stdlib,StdlibVsn},
+ {sasl,SaslVsn},
+ {inets,InetsVsn},
+ {tools,ToolsVsn},
+ {mnesia,MnesiaVsn},
{z,"1.0"}]},
FullName6 = filename:join(?WORK_DIR,RelName6),
?m(ok, file:write_file(FullName6 ++ ".rel", io_lib:format("~p.\n", [Rel6]))),
@@ -583,7 +630,11 @@ create_script_sort(Config) ->
Rel8 = {release, {RelName8,RelVsn}, {erts,ErtsVsn},
[{kernel,KernelVsn},
{stdlib,StdlibVsn},
- {z,"1.0",[tools]}]},
+ {z,"1.0",[mnesia,tools]},
+ {sasl,SaslVsn},
+ {inets,InetsVsn},
+ {mnesia,MnesiaVsn},
+ {tools,ToolsVsn}]},
FullName8 = filename:join(?WORK_DIR,RelName8),
?m(ok, file:write_file(FullName8 ++ ".rel", io_lib:format("~p.\n", [Rel8]))),
Rel9 = {release, {RelName9,RelVsn}, {erts,ErtsVsn},
@@ -594,6 +645,14 @@ create_script_sort(Config) ->
{inets,InetsVsn}]},
FullName9 = filename:join(?WORK_DIR,RelName9),
?m(ok, file:write_file(FullName9 ++ ".rel", io_lib:format("~p.\n", [Rel9]))),
+ Rel10 = {release, {RelName10,RelVsn}, {erts,ErtsVsn},
+ [{kernel,KernelVsn},
+ {stdlib,StdlibVsn},
+ {z,"1.0",[]},
+ {inets,InetsVsn},
+ {sasl,SaslVsn}]},
+ FullName10 = filename:join(?WORK_DIR,RelName10),
+ ?m(ok, file:write_file(FullName10 ++ ".rel", io_lib:format("~p.\n", [Rel10]))),
%% Generate script files with systools and reltool and compare
ZPath = filename:join([LibDir,"*",ebin]),
@@ -625,26 +684,31 @@ create_script_sort(Config) ->
"in the app file: [tools]"},
reltool:get_script(Pid, RelName5)),
- ?msym({error,_,{undefined_applications,_}},
- systools_make_script(FullName6,ZPath)),
- ?m({error, "Undefined applications: [tools,mnesia]"},
- reltool:get_script(Pid, RelName6)),
+ ?msym({ok,_,_}, systools_make_script(FullName6,ZPath)),
+ {ok, [SystoolsScript6]} = ?msym({ok,[_]}, file:consult(FullName6++".script")),
+ {ok, Script6} = ?msym({ok, _}, reltool:get_script(Pid, RelName6)),
+ ?m(equal, diff_script(SystoolsScript6, Script6)),
?msym({error,_,{circular_dependencies,_}},
systools_make_script(FullName7,ZPath)),
?m({error,"Circular dependencies: [x,y]"},
reltool:get_script(Pid, RelName7)),
- ?msym({error,_,{undefined_applications,_}},
- systools_make_script(FullName8,ZPath)),
- ?m({error, "Undefined applications: [tools]"},
- reltool:get_script(Pid, RelName8)),
+ ?msym({ok,_,_}, systools_make_script(FullName8,ZPath)),
+ {ok, [SystoolsScript8]} = ?msym({ok,[_]}, file:consult(FullName8++".script")),
+ {ok, Script8} = ?msym({ok, _}, reltool:get_script(Pid, RelName8)),
+ ?m(equal, diff_script(SystoolsScript8, Script8)),
?msym({ok,_,_}, systools_make_script(FullName9,ZPath)),
{ok, [SystoolsScript9]} = ?msym({ok,[_]}, file:consult(FullName9++".script")),
{ok, Script9} = ?msym({ok, _}, reltool:get_script(Pid, RelName9)),
?m(equal, diff_script(SystoolsScript9, Script9)),
+ ?msym({ok,_,_}, systools_make_script(FullName10,ZPath)),
+ {ok, [SystoolsScript10]} = ?msym({ok,[_]}, file:consult(FullName10++".script")),
+ {ok, Script10} = ?msym({ok, _}, reltool:get_script(Pid, RelName10)),
+ ?m(equal, diff_script(SystoolsScript10, Script10)),
+
%% Stop server
?m(ok, reltool:stop(Pid)),
ok.
@@ -950,8 +1014,6 @@ create_multiple_standalone(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Generate old type of target system
-
-create_old_target(_Config) -> {skip, "Old style of target"};
create_old_target(_Config) ->
%% Configure the server
@@ -974,8 +1036,7 @@ create_old_target(_Config) ->
?m(ok, reltool_utils:recursive_delete(TargetDir)),
?m(ok, file:make_dir(TargetDir)),
ok = ?m(ok, reltool:create_target([{config, Config}], TargetDir)),
-
- %% io:format("Will fail on Windows (should patch erl.ini)\n", []),
+
ok = ?m(ok, reltool:install(RelName2, TargetDir)),
Erl = filename:join([TargetDir, "bin", "erl"]),
@@ -985,6 +1046,61 @@ create_old_target(_Config) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Generate target system
+
+create_slim(Config) ->
+ %% Configure the server
+ RelName = "slim",
+ RelVsn = "1.0",
+
+ DataDir = ?config(data_dir,Config),
+ LibDir = filename:join(DataDir,"slim"),
+
+ Sys =
+ {sys,
+ [
+ {root_dir, code:root_dir()},
+ {lib_dirs, []},
+ {boot_rel, RelName},
+ {rel, RelName, RelVsn, [sasl, stdlib, kernel, a]},
+ {app, sasl, [{incl_cond, include}]},
+ {app, a, [{incl_cond, include},
+ {lib_dir,filename:join(LibDir,"a-1.0")}]},
+ {excl_lib,otp_root}
+ ]},
+
+ %% Generate target file
+ TargetDir = filename:absname(filename:join([?WORK_DIR, "target_slim"])),
+ ?m(ok, reltool_utils:recursive_delete(TargetDir)),
+ ?m(ok, file:make_dir(TargetDir)),
+ ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, Sys}])]),
+ ok = ?m(ok, reltool:create_target([{config, Sys}], TargetDir)),
+
+ TargetLibDir = filename:join(TargetDir,"lib"),
+ TargetRelDir = filename:join(TargetDir,"releases"),
+ TargetRelVsnDir = filename:join(TargetRelDir,RelVsn),
+
+ {ok,["a-1.0.ez"]} = file:list_dir(TargetLibDir),
+
+ RootDir = code:root_dir(),
+ Erl = filename:join([RootDir, "bin", "erl"]),
+ EscapedQuote =
+ case os:type() of
+ {win32,_} -> "\\\"";
+ _ -> "\""
+ end,
+ Args = ["-boot_var", "RELTOOL_EXT_LIB", TargetLibDir,
+ "-boot", filename:join(TargetRelVsnDir,RelName),
+ "-sasl", "releases_dir", EscapedQuote++TargetRelDir++EscapedQuote],
+ {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl, Args)),
+ ?msym(RootDir, rpc:call(Node, code, root_dir, [])),
+ ?msym([{RelName,RelVsn,_,permanent}],
+ rpc:call(Node,release_handler,which_releases,[])),
+ ?msym(ok, stop_node(Node)),
+
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Generate target system with eval_target_spec/3
eval_target_spec(_Config) ->
@@ -1891,7 +2007,7 @@ save_config(Config) ->
%%
%% x-1.0: x1.erl x2.erl x3.erl
%% \ / (x2 calls y1, x3 calls y2)
-%% y-1.0: y1.erl y2.erl
+%% y-1.0: y0.erl y1.erl y2.erl
%% \ (y1 calls z1)
%% z-1.0 z1.erl
%%
@@ -2021,6 +2137,47 @@ dependencies(Config) ->
ok.
+%% Test that incl_cond on mod level overwrites mod_cond on app level
+%% Uses same test applications as dependencies/1 above
+mod_incl_cond_derived(Config) ->
+ %% In app y: mod_cond=none means no module shall be included
+ %% but mod_cond is overwritten by incl_cond on mod level
+ Sys = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]},
+ {incl_cond, exclude},
+ {app,kernel,[{incl_cond,include}]},
+ {app,sasl,[{incl_cond,include}]},
+ {app,stdlib,[{incl_cond,include}]},
+ {app,x,[{incl_cond,include}]},
+ {app,y,[{incl_cond,include},
+ {mod_cond,none},
+ {mod,y0,[{incl_cond,derived}]},
+ {mod,y2,[{incl_cond,derived}]}]}]},
+ {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])),
+
+ ?msym({ok,[#app{name=kernel},
+ #app{name=sasl},
+ #app{name=stdlib},
+ #app{name=x,uses_apps=[y]},
+ #app{name=y,uses_apps=[]}]},
+ reltool_server:get_apps(Pid,whitelist)),
+ {ok, Der} = ?msym({ok,_},reltool_server:get_apps(Pid,derived)),
+ ?msym([], rm_missing_app(Der)),
+ ?msym({ok,[]}, reltool_server:get_apps(Pid,source)),
+
+ %% 1. check that y0 is not included since it has
+ %% incl_cond=derived, but is not used by any other module.
+ ?msym({ok,#mod{is_included=undefined}}, reltool_server:get_mod(Pid,y0)),
+
+ %% 2. check that y1 is excluded since it has undefined incl_cond
+ %% on mod level, so mod_cond on app level shall be used.
+ ?msym({ok,#mod{is_included=false}}, reltool_server:get_mod(Pid,y1)),
+
+ %% 3. check that y2 is included since it has incl_cond=derived and
+ %% is used by x3.
+ ?msym({ok,#mod{is_included=true}}, reltool_server:get_mod(Pid,y2)),
+
+ ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
use_selected_vsn(Config) ->
LibDir1 = filename:join(datadir(Config),"use_selected_vsn"),
@@ -2145,6 +2302,39 @@ use_selected_vsn_relative_path(Config) ->
ok = file:set_cwd(Cwd),
ok.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Test that reltool recognizes an application with its real name even
+%% though it uses non standard format for its version number (in the
+%% directory name)
+non_standard_vsn_id(Config) ->
+ LibDir = filename:join(datadir(Config),"non_standard_vsn_id"),
+ B1Dir = filename:join(LibDir,"b-first"),
+ B2Dir = filename:join(LibDir,"b-second"),
+
+ %%-----------------------------------------------------------------
+ %% Default vsn of app b
+ Sys1 = {sys,[{lib_dirs,[LibDir]},
+ {incl_cond, exclude},
+ {app,kernel,[{incl_cond,include}]},
+ {app,sasl,[{incl_cond,include}]},
+ {app,stdlib,[{incl_cond,include}]},
+ {app,b,[{incl_cond,include}]}]},
+ {ok, Pid1} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])),
+ ?msym({ok,#app{vsn="first",active_dir=B1Dir,sorted_dirs=[B1Dir,B2Dir]}},
+ reltool_server:get_app(Pid1,b)),
+
+ %%-----------------------------------------------------------------
+ %% Pre-selected vsn of app b
+ Sys2 = {sys,[{lib_dirs,[LibDir]},
+ {incl_cond, exclude},
+ {app,kernel,[{incl_cond,include}]},
+ {app,sasl,[{incl_cond,include}]},
+ {app,stdlib,[{incl_cond,include}]},
+ {app,b,[{incl_cond,include},{vsn,"second"}]}]},
+ {ok, Pid2} = ?msym({ok, _}, reltool:start_server([{config, Sys2}])),
+ ?msym({ok,#app{vsn="second",active_dir=B2Dir,sorted_dirs=[B1Dir,B2Dir]}},
+ reltool_server:get_app(Pid2,b)),
+ ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2226,10 +2416,14 @@ mod_path(Node,Mod) ->
%% Node handling
start_node(Name, ErlPath) ->
+ start_node(Name, ErlPath, []).
+start_node(Name, ErlPath, Args0) ->
FullName = full_node_name(Name),
- CmdLine = mk_node_cmdline(Name, ErlPath),
- io:format("Starting node ~p: ~s~n", [FullName, CmdLine]),
- case open_port({spawn, CmdLine}, []) of
+ Args = mk_node_args(Name, Args0),
+ io:format("Starting node ~p: ~s~n",
+ [FullName, lists:flatten([[X," "] || X <- [ErlPath|Args]])]),
+ %io:format("open_port({spawn_executable, ~p}, [{args,~p}])~n",[ErlPath,Args]),
+ case open_port({spawn_executable, ErlPath}, [{args,Args}]) of
Port when is_port(Port) ->
unlink(Port),
erlang:port_close(Port),
@@ -2246,29 +2440,21 @@ stop_node(Node) ->
spawn(Node, fun () -> halt() end),
receive {nodedown, Node} -> ok end.
-mk_node_cmdline(Name) ->
- Prog = case catch init:get_argument(progname) of
- {ok,[[P]]} -> P;
- _ -> exit(no_progname_argument_found)
- end,
- mk_node_cmdline(Name, Prog).
-
-mk_node_cmdline(Name, Prog) ->
- Static = "-detached -noinput",
+mk_node_args(Name, Args) ->
Pa = filename:dirname(code:which(?MODULE)),
NameSw = case net_kernel:longnames() of
- false -> "-sname ";
- true -> "-name ";
+ false -> "-sname";
+ true -> "-name";
_ -> exit(not_distributed_node)
end,
{ok, Pwd} = file:get_cwd(),
NameStr = atom_to_list(Name),
- Prog ++ " "
- ++ Static ++ " "
- ++ NameSw ++ " " ++ NameStr ++ " "
- ++ "-pa " ++ Pa ++ " "
- ++ "-env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr ++ " "
- ++ "-setcookie " ++ atom_to_list(erlang:get_cookie()).
+ ["-detached", "-noinput",
+ NameSw, NameStr,
+ "-pa", Pa,
+ "-env", "ERL_CRASH_DUMP", Pwd ++ "/erl_crash_dump." ++ NameStr,
+ "-setcookie", atom_to_list(erlang:get_cookie())
+ | Args].
full_node_name(PreName) ->
HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end,
diff --git a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src
index 21410ceaa9..02846f42b6 100644
--- a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src
+++ b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src
@@ -22,7 +22,11 @@ SEL_VSN= \
use_selected_vsn/b-3.0/ebin/b.@EMULATOR@ \
use_selected_vsn/lib2/b-2.0/ebin/b.@EMULATOR@
-all: $(OTP9229) $(DEPENDENCIES) $(ESCRIPT) $(SEL_VSN)
+SLIM= \
+ slim/a-1.0/ebin/a_sup.@EMULATOR@ \
+ slim/a-1.0/ebin/a.@EMULATOR@
+
+all: $(OTP9229) $(DEPENDENCIES) $(ESCRIPT) $(SEL_VSN) $(SLIM)
otp_9229/x-1.0/ebin/x.@EMULATOR@: otp_9229/x-1.0/src/x.erl
erlc $(EFLAGS) -ootp_9229/x-1.0/ebin otp_9229/x-1.0/src/x.erl
@@ -55,3 +59,8 @@ use_selected_vsn/b-3.0/ebin/b.@EMULATOR@: use_selected_vsn/b-3.0/src/b.erl
erlc $(EFLAGS) -ouse_selected_vsn/b-3.0/ebin use_selected_vsn/b-3.0/src/b.erl
use_selected_vsn/lib2/b-2.0/ebin/b.@EMULATOR@: use_selected_vsn/lib2/b-2.0/src/b.erl
erlc $(EFLAGS) -ouse_selected_vsn/lib2/b-2.0/ebin use_selected_vsn/lib2/b-2.0/src/b.erl
+
+slim/a-1.0/ebin/a_sup.@EMULATOR@: slim/a-1.0/src/a_sup.erl
+ erlc $(EFLAGS) -oslim/a-1.0/ebin slim/a-1.0/src/a_sup.erl
+slim/a-1.0/ebin/a.@EMULATOR@: slim/a-1.0/src/a.erl
+ erlc $(EFLAGS) -oslim/a-1.0/ebin slim/a-1.0/src/a.erl
diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app
index d9dac371d7..39fdabeea4 100644
--- a/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app
+++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app
@@ -2,6 +2,6 @@
{application, y,
[{description, "Library application in reltool dependency test"},
{vsn, "1.0"},
- {modules, [y1,y2]},
+ {modules, [y0,y1,y2]},
{registered, []},
{applications, [kernel, stdlib]}]}.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y0.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y0.erl
new file mode 100644
index 0000000000..dc188ba7b6
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y0.erl
@@ -0,0 +1,5 @@
+-module(y0).
+-compile(export_all).
+
+f() ->
+ ok.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-first/ebin/b.app b/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-first/ebin/b.app
new file mode 100644
index 0000000000..55550a8190
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-first/ebin/b.app
@@ -0,0 +1,6 @@
+%% -*- erlang -*-
+{application, b,
+ [{description, "Reltool test app for using selected version of app"},
+ {vsn, "first"},
+ {modules, [b]},
+ {applications, [kernel, stdlib]}]}.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-first/src/b.erl b/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-first/src/b.erl
new file mode 100644
index 0000000000..a6b4ff1c05
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-first/src/b.erl
@@ -0,0 +1,4 @@
+-module(b).
+-compile(export_all).
+
+foo() -> ok.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-second/ebin/b.app b/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-second/ebin/b.app
new file mode 100644
index 0000000000..91e1365df7
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-second/ebin/b.app
@@ -0,0 +1,6 @@
+%% -*- erlang -*-
+{application, b,
+ [{description, "Reltool test app for using selected version of app"},
+ {vsn, "second"},
+ {modules, [b]},
+ {applications, [kernel, stdlib]}]}.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-second/src/b.erl b/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-second/src/b.erl
new file mode 100644
index 0000000000..a6b4ff1c05
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/non_standard_vsn_id/b-second/src/b.erl
@@ -0,0 +1,4 @@
+-module(b).
+-compile(export_all).
+
+foo() -> ok.
diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/ebin/a.app
index 200cfcfe47..3160e00da7 100644
--- a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app
+++ b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/ebin/a.app
@@ -1,8 +1,7 @@
{application, a,
[{description, "A CXC 138 11"},
- {vsn, "2.0"},
- {modules, [{a, 1}, {a_sup,1}]},
+ {vsn, "1.0"},
+ {modules, [a, a_sup]},
{registered, [a_sup]},
{applications, [kernel, stdlib]},
- {env, [{key1, val1}]},
{mod, {a_sup, []}}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a.erl
index cfe38b55ce..bb500bed69 100644
--- a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl
+++ b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a.erl
@@ -20,6 +20,8 @@
-behaviour(gen_server).
+-vsn(1).
+
%% External exports
-export([start_link/0, a/0]).
%% Internal exports
diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a_sup.erl
index a141c1767b..a141c1767b 100644
--- a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl
+++ b/lib/reltool/test/reltool_server_SUITE_data/slim/a-1.0/src/a_sup.erl
diff --git a/lib/reltool/vsn.mk b/lib/reltool/vsn.mk
index 3811d897c7..6872539b8c 100644
--- a/lib/reltool/vsn.mk
+++ b/lib/reltool/vsn.mk
@@ -1 +1 @@
-RELTOOL_VSN = 0.6
+RELTOOL_VSN = 0.6.1
diff --git a/lib/runtime_tools/c_src/Makefile.in b/lib/runtime_tools/c_src/Makefile.in
index 754e6ccd78..586f649924 100644
--- a/lib/runtime_tools/c_src/Makefile.in
+++ b/lib/runtime_tools/c_src/Makefile.in
@@ -46,9 +46,6 @@ LDFLAGS += $(DED_LDFLAGS)
DTRACE_LIBNAME = dyntrace
SYSINCLUDE = $(DED_SYS_INCLUDE)
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
- SYSINCLUDE += -I$(ERL_TOP)/erts/etc/vxworks
-endif
TRACE_DRV_INCLUDES = $(SYSINCLUDE)
@@ -101,12 +98,8 @@ ifeq ($(findstring win32,$(TARGET)), win32)
SOLIBS = $(LIBDIR)/trace_ip_drv.dll $(LIBDIR)/trace_file_drv.dll
LN=cp
else
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-SOLIBS = $(LIBDIR)/trace_ip_drv.eld $(LIBDIR)/trace_file_drv.eld
-else
SOLIBS = $(LIBDIR)/trace_ip_drv.so $(LIBDIR)/trace_file_drv.so
endif
-endif
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
@@ -165,15 +158,6 @@ $(LIBDIR)/trace_ip_drv.dll: $(TRACE_IP_DRV_OBJS)
$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
$(LIBDIR)/trace_file_drv.dll: $(TRACE_FILE_DRV_OBJS)
$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
-#
-# VxWorks is simply to different from Unix in this sense.
-# Here are the inference rules for VxWorks
-#
-$(LIBDIR)/trace_ip_drv.eld: $(TRACE_IP_DRV_OBJS)
- $(LD) $(LDFLAGS) -o $@ $^
-
-$(LIBDIR)/trace_file_drv.eld: $(TRACE_FILE_DRV_OBJS)
- $(LD) $(LDFLAGS) -o $@ $^
clean:
rm -f $(SOLIBS) $(TRACE_IP_DRV_OBJS) $(TRACE_FILE_DRV_OBJS)
diff --git a/lib/runtime_tools/c_src/dtrace_user.d b/lib/runtime_tools/c_src/dtrace_user.d
index 02150375af..9e180a3cb2 100644
--- a/lib/runtime_tools/c_src/dtrace_user.d
+++ b/lib/runtime_tools/c_src/dtrace_user.d
@@ -19,3118 +19,11 @@
*/
provider erlang {
- /**
- * Send a single string to a probe.
- *
- * @param NUL-terminated string
+ /*
+ * The set of probes for use by Erlang code ... moved from here to
+ * erts/emulator/beam/erlang_dtrace.d until a more portable solution is
+ * found; see erlang_dtrace.d for details.
*/
- probe user_trace__s1(char* message);
-
- /**
- * Multi-purpose probe: up to 4 NUL-terminated strings and 4
- * 64-bit integer arguments.
- *
- * @param proc, the PID (string form) of the sending process
- * @param user_tag, the user tag of the sender
- * @param i1, integer
- * @param i2, integer
- * @param i3, integer
- * @param i4, integer
- * @param s1, string/iolist. D's arg6 is NULL if not given by Erlang
- * @param s2, string/iolist. D's arg7 is NULL if not given by Erlang
- * @param s3, string/iolist. D's arg8 is NULL if not given by Erlang
- * @param s4, string/iolist. D's arg9 is NULL if not given by Erlang
- */
- probe user_trace__i4s4(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
-
- /**
- * Same args as user_trace__i4s4, but lots of different probes
- * to avoid the "one probe to rule them all and in the runtime
- * molasses bind them" problem.
- *
- * (I.e. If you use only a single probe, but you also embed that probe
- * in many different places in your code, if that probe fires 100K or
- * more times per second, then it *will* hurt when you have to enable
- * that probe. However, if you have any different probes, then you
- * can ensure that any probe on a hot code path will use separate
- * probe(s) than everyone else ... and you can then enable many non-
- * hot probes in production without worry about creating too much
- * measurement overhead.
- */
- probe user_trace__n0(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n2(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n3(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n4(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n5(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n6(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n7(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n8(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n9(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n10(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n11(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n12(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n13(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n14(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n15(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n16(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n17(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n18(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n19(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n20(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n21(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n22(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n23(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n24(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n25(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n26(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n27(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n28(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n29(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n30(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n31(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n32(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n33(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n34(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n35(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n36(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n37(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n38(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n39(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n40(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n41(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n42(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n43(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n44(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n45(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n46(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n47(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n48(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n49(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n50(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n51(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n52(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n53(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n54(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n55(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n56(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n57(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n58(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n59(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n60(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n61(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n62(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n63(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n64(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n65(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n66(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n67(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n68(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n69(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n70(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n71(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n72(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n73(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n74(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n75(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n76(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n77(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n78(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n79(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n80(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n81(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n82(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n83(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n84(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n85(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n86(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n87(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n88(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n89(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n90(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n91(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n92(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n93(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n94(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n95(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n96(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n97(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n98(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n99(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n100(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n101(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n102(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n103(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n104(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n105(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n106(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n107(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n108(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n109(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n110(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n111(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n112(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n113(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n114(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n115(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n116(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n117(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n118(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n119(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n120(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n121(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n122(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n123(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n124(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n125(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n126(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n127(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n128(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n129(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n130(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n131(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n132(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n133(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n134(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n135(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n136(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n137(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n138(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n139(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n140(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n141(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n142(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n143(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n144(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n145(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n146(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n147(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n148(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n149(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n150(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n151(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n152(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n153(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n154(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n155(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n156(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n157(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n158(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n159(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n160(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n161(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n162(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n163(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n164(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n165(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n166(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n167(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n168(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n169(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n170(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n171(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n172(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n173(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n174(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n175(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n176(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n177(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n178(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n179(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n180(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n181(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n182(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n183(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n184(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n185(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n186(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n187(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n188(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n189(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n190(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n191(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n192(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n193(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n194(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n195(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n196(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n197(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n198(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n199(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n200(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n201(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n202(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n203(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n204(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n205(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n206(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n207(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n208(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n209(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n210(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n211(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n212(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n213(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n214(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n215(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n216(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n217(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n218(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n219(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n220(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n221(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n222(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n223(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n224(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n225(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n226(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n227(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n228(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n229(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n230(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n231(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n232(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n233(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n234(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n235(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n236(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n237(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n238(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n239(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n240(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n241(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n242(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n243(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n244(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n245(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n246(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n247(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n248(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n249(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n250(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n251(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n252(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n253(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n254(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n255(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n256(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n257(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n258(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n259(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n260(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n261(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n262(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n263(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n264(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n265(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n266(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n267(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n268(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n269(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n270(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n271(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n272(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n273(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n274(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n275(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n276(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n277(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n278(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n279(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n280(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n281(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n282(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n283(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n284(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n285(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n286(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n287(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n288(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n289(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n290(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n291(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n292(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n293(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n294(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n295(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n296(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n297(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n298(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n299(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n300(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n301(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n302(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n303(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n304(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n305(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n306(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n307(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n308(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n309(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n310(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n311(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n312(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n313(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n314(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n315(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n316(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n317(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n318(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n319(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n320(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n321(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n322(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n323(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n324(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n325(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n326(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n327(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n328(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n329(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n330(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n331(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n332(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n333(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n334(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n335(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n336(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n337(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n338(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n339(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n340(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n341(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n342(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n343(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n344(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n345(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n346(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n347(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n348(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n349(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n350(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n351(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n352(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n353(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n354(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n355(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n356(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n357(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n358(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n359(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n360(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n361(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n362(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n363(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n364(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n365(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n366(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n367(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n368(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n369(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n370(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n371(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n372(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n373(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n374(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n375(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n376(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n377(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n378(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n379(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n380(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n381(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n382(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n383(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n384(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n385(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n386(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n387(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n388(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n389(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n390(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n391(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n392(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n393(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n394(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n395(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n396(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n397(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n398(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n399(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n400(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n401(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n402(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n403(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n404(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n405(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n406(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n407(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n408(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n409(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n410(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n411(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n412(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n413(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n414(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n415(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n416(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n417(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n418(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n419(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n420(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n421(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n422(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n423(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n424(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n425(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n426(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n427(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n428(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n429(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n430(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n431(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n432(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n433(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n434(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n435(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n436(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n437(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n438(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n439(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n440(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n441(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n442(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n443(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n444(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n445(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n446(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n447(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n448(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n449(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n450(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n451(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n452(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n453(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n454(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n455(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n456(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n457(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n458(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n459(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n460(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n461(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n462(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n463(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n464(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n465(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n466(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n467(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n468(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n469(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n470(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n471(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n472(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n473(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n474(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n475(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n476(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n477(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n478(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n479(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n480(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n481(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n482(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n483(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n484(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n485(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n486(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n487(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n488(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n489(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n490(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n491(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n492(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n493(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n494(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n495(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n496(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n497(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n498(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n499(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n500(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n501(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n502(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n503(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n504(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n505(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n506(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n507(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n508(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n509(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n510(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n511(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n512(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n513(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n514(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n515(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n516(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n517(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n518(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n519(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n520(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n521(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n522(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n523(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n524(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n525(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n526(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n527(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n528(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n529(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n530(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n531(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n532(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n533(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n534(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n535(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n536(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n537(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n538(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n539(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n540(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n541(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n542(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n543(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n544(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n545(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n546(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n547(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n548(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n549(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n550(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n551(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n552(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n553(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n554(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n555(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n556(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n557(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n558(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n559(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n560(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n561(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n562(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n563(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n564(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n565(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n566(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n567(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n568(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n569(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n570(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n571(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n572(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n573(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n574(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n575(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n576(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n577(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n578(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n579(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n580(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n581(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n582(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n583(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n584(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n585(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n586(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n587(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n588(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n589(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n590(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n591(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n592(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n593(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n594(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n595(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n596(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n597(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n598(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n599(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n600(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n601(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n602(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n603(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n604(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n605(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n606(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n607(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n608(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n609(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n610(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n611(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n612(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n613(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n614(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n615(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n616(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n617(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n618(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n619(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n620(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n621(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n622(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n623(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n624(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n625(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n626(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n627(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n628(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n629(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n630(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n631(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n632(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n633(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n634(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n635(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n636(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n637(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n638(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n639(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n640(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n641(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n642(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n643(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n644(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n645(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n646(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n647(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n648(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n649(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n650(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n651(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n652(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n653(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n654(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n655(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n656(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n657(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n658(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n659(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n660(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n661(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n662(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n663(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n664(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n665(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n666(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n667(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n668(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n669(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n670(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n671(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n672(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n673(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n674(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n675(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n676(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n677(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n678(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n679(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n680(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n681(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n682(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n683(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n684(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n685(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n686(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n687(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n688(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n689(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n690(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n691(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n692(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n693(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n694(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n695(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n696(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n697(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n698(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n699(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n700(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n701(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n702(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n703(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n704(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n705(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n706(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n707(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n708(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n709(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n710(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n711(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n712(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n713(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n714(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n715(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n716(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n717(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n718(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n719(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n720(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n721(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n722(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n723(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n724(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n725(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n726(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n727(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n728(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n729(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n730(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n731(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n732(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n733(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n734(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n735(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n736(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n737(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n738(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n739(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n740(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n741(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n742(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n743(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n744(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n745(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n746(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n747(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n748(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n749(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n750(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n751(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n752(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n753(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n754(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n755(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n756(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n757(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n758(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n759(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n760(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n761(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n762(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n763(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n764(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n765(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n766(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n767(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n768(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n769(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n770(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n771(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n772(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n773(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n774(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n775(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n776(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n777(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n778(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n779(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n780(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n781(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n782(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n783(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n784(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n785(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n786(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n787(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n788(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n789(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n790(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n791(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n792(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n793(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n794(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n795(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n796(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n797(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n798(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n799(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n800(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n801(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n802(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n803(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n804(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n805(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n806(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n807(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n808(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n809(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n810(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n811(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n812(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n813(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n814(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n815(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n816(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n817(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n818(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n819(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n820(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n821(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n822(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n823(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n824(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n825(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n826(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n827(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n828(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n829(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n830(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n831(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n832(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n833(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n834(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n835(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n836(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n837(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n838(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n839(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n840(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n841(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n842(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n843(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n844(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n845(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n846(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n847(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n848(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n849(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n850(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n851(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n852(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n853(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n854(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n855(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n856(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n857(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n858(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n859(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n860(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n861(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n862(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n863(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n864(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n865(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n866(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n867(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n868(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n869(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n870(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n871(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n872(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n873(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n874(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n875(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n876(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n877(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n878(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n879(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n880(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n881(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n882(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n883(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n884(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n885(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n886(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n887(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n888(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n889(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n890(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n891(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n892(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n893(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n894(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n895(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n896(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n897(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n898(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n899(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n900(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n901(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n902(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n903(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n904(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n905(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n906(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n907(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n908(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n909(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n910(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n911(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n912(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n913(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n914(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n915(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n916(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n917(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n918(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n919(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n920(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n921(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n922(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n923(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n924(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n925(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n926(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n927(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n928(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n929(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n930(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n931(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n932(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n933(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n934(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n935(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n936(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n937(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n938(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n939(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n940(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n941(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n942(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n943(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n944(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n945(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n946(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n947(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n948(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n949(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n950(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n951(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n952(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n953(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n954(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n955(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n956(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n957(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n958(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n959(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n960(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n961(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n962(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n963(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n964(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n965(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n966(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n967(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n968(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n969(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n970(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n971(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n972(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n973(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n974(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n975(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n976(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n977(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n978(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n979(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n980(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n981(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n982(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n983(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n984(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n985(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n986(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n987(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n988(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n989(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n990(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n991(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n992(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n993(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n994(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n995(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n996(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n997(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n998(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n999(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1000(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1001(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1002(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1003(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1004(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1005(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1006(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1007(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1008(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1009(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1010(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1011(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1012(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1013(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1014(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1015(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1016(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1017(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1018(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1019(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1020(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1021(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1022(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
- probe user_trace__n1023(char *proc, char *user_tag,
- int i1, int i2, int i3, int i4,
- char *s1, char *s2, char *s3, char *s4);
};
#pragma D attributes Evolving/Evolving/Common provider erlang provider
diff --git a/lib/runtime_tools/c_src/dyntrace.c b/lib/runtime_tools/c_src/dyntrace.c
index 62a392fe56..eef03afd1c 100644
--- a/lib/runtime_tools/c_src/dyntrace.c
+++ b/lib/runtime_tools/c_src/dyntrace.c
@@ -36,6 +36,10 @@
void dtrace_nifenv_str(ErlNifEnv *env, char *process_buf);
void get_string_maybe(ErlNifEnv *env, const ERL_NIF_TERM term, char **ptr, char *buf, int bufsiz);
+#ifdef HAVE_USE_DTRACE
+ERL_NIF_TERM erl_nif_user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM erl_nif_user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+#endif
#ifdef VALGRIND
# include <valgrind/memcheck.h>
@@ -98,1151 +102,25 @@ static ERL_NIF_TERM available(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
static ERL_NIF_TERM user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
#ifdef HAVE_USE_DTRACE
- ErlNifBinary message_bin;
- DTRACE_CHARBUF(messagebuf, MESSAGE_BUFSIZ + 1);
-
- if (DTRACE_ENABLED(user_trace_s1)) {
- if (!enif_inspect_iolist_as_binary(env, argv[0], &message_bin) ||
- message_bin.size > MESSAGE_BUFSIZ) {
- return atom_badarg;
- }
- memcpy(messagebuf, (char *) message_bin.data, message_bin.size);
- messagebuf[message_bin.size] = '\0';
- DTRACE1(user_trace_s1, messagebuf);
- return atom_true;
- } else {
- return atom_false;
- }
+ return erl_nif_user_trace_s1(env, argc, argv);
#else
return atom_error;
#endif
}
-void
-get_string_maybe(ErlNifEnv *env,
- const ERL_NIF_TERM term, char **ptr, char *buf, int bufsiz)
-{
- ErlNifBinary str_bin;
-
- if (!enif_inspect_iolist_as_binary(env, term, &str_bin) ||
- str_bin.size > bufsiz) {
- *ptr = NULL;
- } else {
- memcpy(buf, (char *) str_bin.data, str_bin.size);
- buf[str_bin.size] = '\0';
- *ptr = buf;
- }
-}
-
static ERL_NIF_TERM user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
#ifdef HAVE_USE_DTRACE
- DTRACE_CHARBUF(procbuf, 32 + 1);
- DTRACE_CHARBUF(user_tagbuf, MESSAGE_BUFSIZ + 1);
- char *utbuf = NULL;
- ErlNifSInt64 i1, i2, i3, i4;
- DTRACE_CHARBUF(messagebuf1, MESSAGE_BUFSIZ + 1);
- DTRACE_CHARBUF(messagebuf2, MESSAGE_BUFSIZ + 1);
- DTRACE_CHARBUF(messagebuf3, MESSAGE_BUFSIZ + 1);
- DTRACE_CHARBUF(messagebuf4, MESSAGE_BUFSIZ + 1);
- char *mbuf1 = NULL, *mbuf2 = NULL, *mbuf3 = NULL, *mbuf4 = NULL;
-
- if (DTRACE_ENABLED(user_trace_i4s4)) {
- dtrace_nifenv_str(env, procbuf);
- get_string_maybe(env, argv[0], &utbuf, user_tagbuf, MESSAGE_BUFSIZ);
- if (! enif_get_int64(env, argv[1], &i1))
- i1 = 0;
- if (! enif_get_int64(env, argv[2], &i2))
- i2 = 0;
- if (! enif_get_int64(env, argv[3], &i3))
- i3 = 0;
- if (! enif_get_int64(env, argv[4], &i4))
- i4 = 0;
- get_string_maybe(env, argv[5], &mbuf1, messagebuf1, MESSAGE_BUFSIZ);
- get_string_maybe(env, argv[6], &mbuf2, messagebuf2, MESSAGE_BUFSIZ);
- get_string_maybe(env, argv[7], &mbuf3, messagebuf3, MESSAGE_BUFSIZ);
- get_string_maybe(env, argv[8], &mbuf4, messagebuf4, MESSAGE_BUFSIZ);
- DTRACE10(user_trace_i4s4, procbuf, utbuf,
- i1, i2, i3, i4, mbuf1, mbuf2, mbuf3, mbuf4);
- return atom_true;
- } else {
- return atom_false;
- }
+ return erl_nif_user_trace_i4s4(env, argc, argv);
#else
return atom_error;
#endif
}
-#define DTRACE10_LABEL(name, label, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) \
- erlang_##name##label((a0), (a1), (a2), (a3), (a4), (a5), (a6), (a7), (a8), (a9))
-#define N_STATEMENT(the_label) \
- case the_label: \
- if (DTRACE_ENABLED(user_trace_n##the_label)) { \
- dtrace_nifenv_str(env, procbuf); \
- get_string_maybe(env, argv[1], &utbuf, user_tagbuf, MESSAGE_BUFSIZ); \
- if (! enif_get_int64(env, argv[2], &i1)) \
- i1 = 0; \
- if (! enif_get_int64(env, argv[3], &i2)) \
- i2 = 0; \
- if (! enif_get_int64(env, argv[4], &i3)) \
- i3 = 0; \
- if (! enif_get_int64(env, argv[5], &i4)) \
- i4 = 0; \
- get_string_maybe(env, argv[6], &mbuf1, messagebuf1, MESSAGE_BUFSIZ); \
- get_string_maybe(env, argv[7], &mbuf2, messagebuf2, MESSAGE_BUFSIZ); \
- get_string_maybe(env, argv[8], &mbuf3, messagebuf3, MESSAGE_BUFSIZ); \
- get_string_maybe(env, argv[9], &mbuf4, messagebuf4, MESSAGE_BUFSIZ); \
- DTRACE10_LABEL(user_trace_n, the_label, procbuf, utbuf, \
- i1, i2, i3, i4, mbuf1, mbuf2, mbuf3, mbuf4); \
- return atom_true; \
- } else { \
- return atom_false; \
- } \
- break
-
static ERL_NIF_TERM user_trace_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
#ifdef HAVE_USE_DTRACE
- DTRACE_CHARBUF(procbuf, 32 + 1);
- DTRACE_CHARBUF(user_tagbuf, MESSAGE_BUFSIZ + 1);
- char *utbuf = NULL;
- ErlNifSInt64 i1, i2, i3, i4;
- DTRACE_CHARBUF(messagebuf1, MESSAGE_BUFSIZ + 1);
- DTRACE_CHARBUF(messagebuf2, MESSAGE_BUFSIZ + 1);
- DTRACE_CHARBUF(messagebuf3, MESSAGE_BUFSIZ + 1);
- DTRACE_CHARBUF(messagebuf4, MESSAGE_BUFSIZ + 1);
- char *mbuf1 = NULL, *mbuf2 = NULL, *mbuf3 = NULL, *mbuf4 = NULL;
- ErlNifSInt64 label = 0;
-
- if (! enif_get_int64(env, argv[0], &label) || label < 0 || label > 1023)
- return atom_badarg;
- switch (label) {
- N_STATEMENT(0);
- N_STATEMENT(1);
- N_STATEMENT(2);
- N_STATEMENT(3);
- N_STATEMENT(4);
- N_STATEMENT(5);
- N_STATEMENT(6);
- N_STATEMENT(7);
- N_STATEMENT(8);
- N_STATEMENT(9);
- N_STATEMENT(10);
- N_STATEMENT(11);
- N_STATEMENT(12);
- N_STATEMENT(13);
- N_STATEMENT(14);
- N_STATEMENT(15);
- N_STATEMENT(16);
- N_STATEMENT(17);
- N_STATEMENT(18);
- N_STATEMENT(19);
- N_STATEMENT(20);
- N_STATEMENT(21);
- N_STATEMENT(22);
- N_STATEMENT(23);
- N_STATEMENT(24);
- N_STATEMENT(25);
- N_STATEMENT(26);
- N_STATEMENT(27);
- N_STATEMENT(28);
- N_STATEMENT(29);
- N_STATEMENT(30);
- N_STATEMENT(31);
- N_STATEMENT(32);
- N_STATEMENT(33);
- N_STATEMENT(34);
- N_STATEMENT(35);
- N_STATEMENT(36);
- N_STATEMENT(37);
- N_STATEMENT(38);
- N_STATEMENT(39);
- N_STATEMENT(40);
- N_STATEMENT(41);
- N_STATEMENT(42);
- N_STATEMENT(43);
- N_STATEMENT(44);
- N_STATEMENT(45);
- N_STATEMENT(46);
- N_STATEMENT(47);
- N_STATEMENT(48);
- N_STATEMENT(49);
- N_STATEMENT(50);
- N_STATEMENT(51);
- N_STATEMENT(52);
- N_STATEMENT(53);
- N_STATEMENT(54);
- N_STATEMENT(55);
- N_STATEMENT(56);
- N_STATEMENT(57);
- N_STATEMENT(58);
- N_STATEMENT(59);
- N_STATEMENT(60);
- N_STATEMENT(61);
- N_STATEMENT(62);
- N_STATEMENT(63);
- N_STATEMENT(64);
- N_STATEMENT(65);
- N_STATEMENT(66);
- N_STATEMENT(67);
- N_STATEMENT(68);
- N_STATEMENT(69);
- N_STATEMENT(70);
- N_STATEMENT(71);
- N_STATEMENT(72);
- N_STATEMENT(73);
- N_STATEMENT(74);
- N_STATEMENT(75);
- N_STATEMENT(76);
- N_STATEMENT(77);
- N_STATEMENT(78);
- N_STATEMENT(79);
- N_STATEMENT(80);
- N_STATEMENT(81);
- N_STATEMENT(82);
- N_STATEMENT(83);
- N_STATEMENT(84);
- N_STATEMENT(85);
- N_STATEMENT(86);
- N_STATEMENT(87);
- N_STATEMENT(88);
- N_STATEMENT(89);
- N_STATEMENT(90);
- N_STATEMENT(91);
- N_STATEMENT(92);
- N_STATEMENT(93);
- N_STATEMENT(94);
- N_STATEMENT(95);
- N_STATEMENT(96);
- N_STATEMENT(97);
- N_STATEMENT(98);
- N_STATEMENT(99);
- N_STATEMENT(100);
- N_STATEMENT(101);
- N_STATEMENT(102);
- N_STATEMENT(103);
- N_STATEMENT(104);
- N_STATEMENT(105);
- N_STATEMENT(106);
- N_STATEMENT(107);
- N_STATEMENT(108);
- N_STATEMENT(109);
- N_STATEMENT(110);
- N_STATEMENT(111);
- N_STATEMENT(112);
- N_STATEMENT(113);
- N_STATEMENT(114);
- N_STATEMENT(115);
- N_STATEMENT(116);
- N_STATEMENT(117);
- N_STATEMENT(118);
- N_STATEMENT(119);
- N_STATEMENT(120);
- N_STATEMENT(121);
- N_STATEMENT(122);
- N_STATEMENT(123);
- N_STATEMENT(124);
- N_STATEMENT(125);
- N_STATEMENT(126);
- N_STATEMENT(127);
- N_STATEMENT(128);
- N_STATEMENT(129);
- N_STATEMENT(130);
- N_STATEMENT(131);
- N_STATEMENT(132);
- N_STATEMENT(133);
- N_STATEMENT(134);
- N_STATEMENT(135);
- N_STATEMENT(136);
- N_STATEMENT(137);
- N_STATEMENT(138);
- N_STATEMENT(139);
- N_STATEMENT(140);
- N_STATEMENT(141);
- N_STATEMENT(142);
- N_STATEMENT(143);
- N_STATEMENT(144);
- N_STATEMENT(145);
- N_STATEMENT(146);
- N_STATEMENT(147);
- N_STATEMENT(148);
- N_STATEMENT(149);
- N_STATEMENT(150);
- N_STATEMENT(151);
- N_STATEMENT(152);
- N_STATEMENT(153);
- N_STATEMENT(154);
- N_STATEMENT(155);
- N_STATEMENT(156);
- N_STATEMENT(157);
- N_STATEMENT(158);
- N_STATEMENT(159);
- N_STATEMENT(160);
- N_STATEMENT(161);
- N_STATEMENT(162);
- N_STATEMENT(163);
- N_STATEMENT(164);
- N_STATEMENT(165);
- N_STATEMENT(166);
- N_STATEMENT(167);
- N_STATEMENT(168);
- N_STATEMENT(169);
- N_STATEMENT(170);
- N_STATEMENT(171);
- N_STATEMENT(172);
- N_STATEMENT(173);
- N_STATEMENT(174);
- N_STATEMENT(175);
- N_STATEMENT(176);
- N_STATEMENT(177);
- N_STATEMENT(178);
- N_STATEMENT(179);
- N_STATEMENT(180);
- N_STATEMENT(181);
- N_STATEMENT(182);
- N_STATEMENT(183);
- N_STATEMENT(184);
- N_STATEMENT(185);
- N_STATEMENT(186);
- N_STATEMENT(187);
- N_STATEMENT(188);
- N_STATEMENT(189);
- N_STATEMENT(190);
- N_STATEMENT(191);
- N_STATEMENT(192);
- N_STATEMENT(193);
- N_STATEMENT(194);
- N_STATEMENT(195);
- N_STATEMENT(196);
- N_STATEMENT(197);
- N_STATEMENT(198);
- N_STATEMENT(199);
- N_STATEMENT(200);
- N_STATEMENT(201);
- N_STATEMENT(202);
- N_STATEMENT(203);
- N_STATEMENT(204);
- N_STATEMENT(205);
- N_STATEMENT(206);
- N_STATEMENT(207);
- N_STATEMENT(208);
- N_STATEMENT(209);
- N_STATEMENT(210);
- N_STATEMENT(211);
- N_STATEMENT(212);
- N_STATEMENT(213);
- N_STATEMENT(214);
- N_STATEMENT(215);
- N_STATEMENT(216);
- N_STATEMENT(217);
- N_STATEMENT(218);
- N_STATEMENT(219);
- N_STATEMENT(220);
- N_STATEMENT(221);
- N_STATEMENT(222);
- N_STATEMENT(223);
- N_STATEMENT(224);
- N_STATEMENT(225);
- N_STATEMENT(226);
- N_STATEMENT(227);
- N_STATEMENT(228);
- N_STATEMENT(229);
- N_STATEMENT(230);
- N_STATEMENT(231);
- N_STATEMENT(232);
- N_STATEMENT(233);
- N_STATEMENT(234);
- N_STATEMENT(235);
- N_STATEMENT(236);
- N_STATEMENT(237);
- N_STATEMENT(238);
- N_STATEMENT(239);
- N_STATEMENT(240);
- N_STATEMENT(241);
- N_STATEMENT(242);
- N_STATEMENT(243);
- N_STATEMENT(244);
- N_STATEMENT(245);
- N_STATEMENT(246);
- N_STATEMENT(247);
- N_STATEMENT(248);
- N_STATEMENT(249);
- N_STATEMENT(250);
- N_STATEMENT(251);
- N_STATEMENT(252);
- N_STATEMENT(253);
- N_STATEMENT(254);
- N_STATEMENT(255);
- N_STATEMENT(256);
- N_STATEMENT(257);
- N_STATEMENT(258);
- N_STATEMENT(259);
- N_STATEMENT(260);
- N_STATEMENT(261);
- N_STATEMENT(262);
- N_STATEMENT(263);
- N_STATEMENT(264);
- N_STATEMENT(265);
- N_STATEMENT(266);
- N_STATEMENT(267);
- N_STATEMENT(268);
- N_STATEMENT(269);
- N_STATEMENT(270);
- N_STATEMENT(271);
- N_STATEMENT(272);
- N_STATEMENT(273);
- N_STATEMENT(274);
- N_STATEMENT(275);
- N_STATEMENT(276);
- N_STATEMENT(277);
- N_STATEMENT(278);
- N_STATEMENT(279);
- N_STATEMENT(280);
- N_STATEMENT(281);
- N_STATEMENT(282);
- N_STATEMENT(283);
- N_STATEMENT(284);
- N_STATEMENT(285);
- N_STATEMENT(286);
- N_STATEMENT(287);
- N_STATEMENT(288);
- N_STATEMENT(289);
- N_STATEMENT(290);
- N_STATEMENT(291);
- N_STATEMENT(292);
- N_STATEMENT(293);
- N_STATEMENT(294);
- N_STATEMENT(295);
- N_STATEMENT(296);
- N_STATEMENT(297);
- N_STATEMENT(298);
- N_STATEMENT(299);
- N_STATEMENT(300);
- N_STATEMENT(301);
- N_STATEMENT(302);
- N_STATEMENT(303);
- N_STATEMENT(304);
- N_STATEMENT(305);
- N_STATEMENT(306);
- N_STATEMENT(307);
- N_STATEMENT(308);
- N_STATEMENT(309);
- N_STATEMENT(310);
- N_STATEMENT(311);
- N_STATEMENT(312);
- N_STATEMENT(313);
- N_STATEMENT(314);
- N_STATEMENT(315);
- N_STATEMENT(316);
- N_STATEMENT(317);
- N_STATEMENT(318);
- N_STATEMENT(319);
- N_STATEMENT(320);
- N_STATEMENT(321);
- N_STATEMENT(322);
- N_STATEMENT(323);
- N_STATEMENT(324);
- N_STATEMENT(325);
- N_STATEMENT(326);
- N_STATEMENT(327);
- N_STATEMENT(328);
- N_STATEMENT(329);
- N_STATEMENT(330);
- N_STATEMENT(331);
- N_STATEMENT(332);
- N_STATEMENT(333);
- N_STATEMENT(334);
- N_STATEMENT(335);
- N_STATEMENT(336);
- N_STATEMENT(337);
- N_STATEMENT(338);
- N_STATEMENT(339);
- N_STATEMENT(340);
- N_STATEMENT(341);
- N_STATEMENT(342);
- N_STATEMENT(343);
- N_STATEMENT(344);
- N_STATEMENT(345);
- N_STATEMENT(346);
- N_STATEMENT(347);
- N_STATEMENT(348);
- N_STATEMENT(349);
- N_STATEMENT(350);
- N_STATEMENT(351);
- N_STATEMENT(352);
- N_STATEMENT(353);
- N_STATEMENT(354);
- N_STATEMENT(355);
- N_STATEMENT(356);
- N_STATEMENT(357);
- N_STATEMENT(358);
- N_STATEMENT(359);
- N_STATEMENT(360);
- N_STATEMENT(361);
- N_STATEMENT(362);
- N_STATEMENT(363);
- N_STATEMENT(364);
- N_STATEMENT(365);
- N_STATEMENT(366);
- N_STATEMENT(367);
- N_STATEMENT(368);
- N_STATEMENT(369);
- N_STATEMENT(370);
- N_STATEMENT(371);
- N_STATEMENT(372);
- N_STATEMENT(373);
- N_STATEMENT(374);
- N_STATEMENT(375);
- N_STATEMENT(376);
- N_STATEMENT(377);
- N_STATEMENT(378);
- N_STATEMENT(379);
- N_STATEMENT(380);
- N_STATEMENT(381);
- N_STATEMENT(382);
- N_STATEMENT(383);
- N_STATEMENT(384);
- N_STATEMENT(385);
- N_STATEMENT(386);
- N_STATEMENT(387);
- N_STATEMENT(388);
- N_STATEMENT(389);
- N_STATEMENT(390);
- N_STATEMENT(391);
- N_STATEMENT(392);
- N_STATEMENT(393);
- N_STATEMENT(394);
- N_STATEMENT(395);
- N_STATEMENT(396);
- N_STATEMENT(397);
- N_STATEMENT(398);
- N_STATEMENT(399);
- N_STATEMENT(400);
- N_STATEMENT(401);
- N_STATEMENT(402);
- N_STATEMENT(403);
- N_STATEMENT(404);
- N_STATEMENT(405);
- N_STATEMENT(406);
- N_STATEMENT(407);
- N_STATEMENT(408);
- N_STATEMENT(409);
- N_STATEMENT(410);
- N_STATEMENT(411);
- N_STATEMENT(412);
- N_STATEMENT(413);
- N_STATEMENT(414);
- N_STATEMENT(415);
- N_STATEMENT(416);
- N_STATEMENT(417);
- N_STATEMENT(418);
- N_STATEMENT(419);
- N_STATEMENT(420);
- N_STATEMENT(421);
- N_STATEMENT(422);
- N_STATEMENT(423);
- N_STATEMENT(424);
- N_STATEMENT(425);
- N_STATEMENT(426);
- N_STATEMENT(427);
- N_STATEMENT(428);
- N_STATEMENT(429);
- N_STATEMENT(430);
- N_STATEMENT(431);
- N_STATEMENT(432);
- N_STATEMENT(433);
- N_STATEMENT(434);
- N_STATEMENT(435);
- N_STATEMENT(436);
- N_STATEMENT(437);
- N_STATEMENT(438);
- N_STATEMENT(439);
- N_STATEMENT(440);
- N_STATEMENT(441);
- N_STATEMENT(442);
- N_STATEMENT(443);
- N_STATEMENT(444);
- N_STATEMENT(445);
- N_STATEMENT(446);
- N_STATEMENT(447);
- N_STATEMENT(448);
- N_STATEMENT(449);
- N_STATEMENT(450);
- N_STATEMENT(451);
- N_STATEMENT(452);
- N_STATEMENT(453);
- N_STATEMENT(454);
- N_STATEMENT(455);
- N_STATEMENT(456);
- N_STATEMENT(457);
- N_STATEMENT(458);
- N_STATEMENT(459);
- N_STATEMENT(460);
- N_STATEMENT(461);
- N_STATEMENT(462);
- N_STATEMENT(463);
- N_STATEMENT(464);
- N_STATEMENT(465);
- N_STATEMENT(466);
- N_STATEMENT(467);
- N_STATEMENT(468);
- N_STATEMENT(469);
- N_STATEMENT(470);
- N_STATEMENT(471);
- N_STATEMENT(472);
- N_STATEMENT(473);
- N_STATEMENT(474);
- N_STATEMENT(475);
- N_STATEMENT(476);
- N_STATEMENT(477);
- N_STATEMENT(478);
- N_STATEMENT(479);
- N_STATEMENT(480);
- N_STATEMENT(481);
- N_STATEMENT(482);
- N_STATEMENT(483);
- N_STATEMENT(484);
- N_STATEMENT(485);
- N_STATEMENT(486);
- N_STATEMENT(487);
- N_STATEMENT(488);
- N_STATEMENT(489);
- N_STATEMENT(490);
- N_STATEMENT(491);
- N_STATEMENT(492);
- N_STATEMENT(493);
- N_STATEMENT(494);
- N_STATEMENT(495);
- N_STATEMENT(496);
- N_STATEMENT(497);
- N_STATEMENT(498);
- N_STATEMENT(499);
- N_STATEMENT(500);
- N_STATEMENT(501);
- N_STATEMENT(502);
- N_STATEMENT(503);
- N_STATEMENT(504);
- N_STATEMENT(505);
- N_STATEMENT(506);
- N_STATEMENT(507);
- N_STATEMENT(508);
- N_STATEMENT(509);
- N_STATEMENT(510);
- N_STATEMENT(511);
- N_STATEMENT(512);
- N_STATEMENT(513);
- N_STATEMENT(514);
- N_STATEMENT(515);
- N_STATEMENT(516);
- N_STATEMENT(517);
- N_STATEMENT(518);
- N_STATEMENT(519);
- N_STATEMENT(520);
- N_STATEMENT(521);
- N_STATEMENT(522);
- N_STATEMENT(523);
- N_STATEMENT(524);
- N_STATEMENT(525);
- N_STATEMENT(526);
- N_STATEMENT(527);
- N_STATEMENT(528);
- N_STATEMENT(529);
- N_STATEMENT(530);
- N_STATEMENT(531);
- N_STATEMENT(532);
- N_STATEMENT(533);
- N_STATEMENT(534);
- N_STATEMENT(535);
- N_STATEMENT(536);
- N_STATEMENT(537);
- N_STATEMENT(538);
- N_STATEMENT(539);
- N_STATEMENT(540);
- N_STATEMENT(541);
- N_STATEMENT(542);
- N_STATEMENT(543);
- N_STATEMENT(544);
- N_STATEMENT(545);
- N_STATEMENT(546);
- N_STATEMENT(547);
- N_STATEMENT(548);
- N_STATEMENT(549);
- N_STATEMENT(550);
- N_STATEMENT(551);
- N_STATEMENT(552);
- N_STATEMENT(553);
- N_STATEMENT(554);
- N_STATEMENT(555);
- N_STATEMENT(556);
- N_STATEMENT(557);
- N_STATEMENT(558);
- N_STATEMENT(559);
- N_STATEMENT(560);
- N_STATEMENT(561);
- N_STATEMENT(562);
- N_STATEMENT(563);
- N_STATEMENT(564);
- N_STATEMENT(565);
- N_STATEMENT(566);
- N_STATEMENT(567);
- N_STATEMENT(568);
- N_STATEMENT(569);
- N_STATEMENT(570);
- N_STATEMENT(571);
- N_STATEMENT(572);
- N_STATEMENT(573);
- N_STATEMENT(574);
- N_STATEMENT(575);
- N_STATEMENT(576);
- N_STATEMENT(577);
- N_STATEMENT(578);
- N_STATEMENT(579);
- N_STATEMENT(580);
- N_STATEMENT(581);
- N_STATEMENT(582);
- N_STATEMENT(583);
- N_STATEMENT(584);
- N_STATEMENT(585);
- N_STATEMENT(586);
- N_STATEMENT(587);
- N_STATEMENT(588);
- N_STATEMENT(589);
- N_STATEMENT(590);
- N_STATEMENT(591);
- N_STATEMENT(592);
- N_STATEMENT(593);
- N_STATEMENT(594);
- N_STATEMENT(595);
- N_STATEMENT(596);
- N_STATEMENT(597);
- N_STATEMENT(598);
- N_STATEMENT(599);
- N_STATEMENT(600);
- N_STATEMENT(601);
- N_STATEMENT(602);
- N_STATEMENT(603);
- N_STATEMENT(604);
- N_STATEMENT(605);
- N_STATEMENT(606);
- N_STATEMENT(607);
- N_STATEMENT(608);
- N_STATEMENT(609);
- N_STATEMENT(610);
- N_STATEMENT(611);
- N_STATEMENT(612);
- N_STATEMENT(613);
- N_STATEMENT(614);
- N_STATEMENT(615);
- N_STATEMENT(616);
- N_STATEMENT(617);
- N_STATEMENT(618);
- N_STATEMENT(619);
- N_STATEMENT(620);
- N_STATEMENT(621);
- N_STATEMENT(622);
- N_STATEMENT(623);
- N_STATEMENT(624);
- N_STATEMENT(625);
- N_STATEMENT(626);
- N_STATEMENT(627);
- N_STATEMENT(628);
- N_STATEMENT(629);
- N_STATEMENT(630);
- N_STATEMENT(631);
- N_STATEMENT(632);
- N_STATEMENT(633);
- N_STATEMENT(634);
- N_STATEMENT(635);
- N_STATEMENT(636);
- N_STATEMENT(637);
- N_STATEMENT(638);
- N_STATEMENT(639);
- N_STATEMENT(640);
- N_STATEMENT(641);
- N_STATEMENT(642);
- N_STATEMENT(643);
- N_STATEMENT(644);
- N_STATEMENT(645);
- N_STATEMENT(646);
- N_STATEMENT(647);
- N_STATEMENT(648);
- N_STATEMENT(649);
- N_STATEMENT(650);
- N_STATEMENT(651);
- N_STATEMENT(652);
- N_STATEMENT(653);
- N_STATEMENT(654);
- N_STATEMENT(655);
- N_STATEMENT(656);
- N_STATEMENT(657);
- N_STATEMENT(658);
- N_STATEMENT(659);
- N_STATEMENT(660);
- N_STATEMENT(661);
- N_STATEMENT(662);
- N_STATEMENT(663);
- N_STATEMENT(664);
- N_STATEMENT(665);
- N_STATEMENT(666);
- N_STATEMENT(667);
- N_STATEMENT(668);
- N_STATEMENT(669);
- N_STATEMENT(670);
- N_STATEMENT(671);
- N_STATEMENT(672);
- N_STATEMENT(673);
- N_STATEMENT(674);
- N_STATEMENT(675);
- N_STATEMENT(676);
- N_STATEMENT(677);
- N_STATEMENT(678);
- N_STATEMENT(679);
- N_STATEMENT(680);
- N_STATEMENT(681);
- N_STATEMENT(682);
- N_STATEMENT(683);
- N_STATEMENT(684);
- N_STATEMENT(685);
- N_STATEMENT(686);
- N_STATEMENT(687);
- N_STATEMENT(688);
- N_STATEMENT(689);
- N_STATEMENT(690);
- N_STATEMENT(691);
- N_STATEMENT(692);
- N_STATEMENT(693);
- N_STATEMENT(694);
- N_STATEMENT(695);
- N_STATEMENT(696);
- N_STATEMENT(697);
- N_STATEMENT(698);
- N_STATEMENT(699);
- N_STATEMENT(700);
- N_STATEMENT(701);
- N_STATEMENT(702);
- N_STATEMENT(703);
- N_STATEMENT(704);
- N_STATEMENT(705);
- N_STATEMENT(706);
- N_STATEMENT(707);
- N_STATEMENT(708);
- N_STATEMENT(709);
- N_STATEMENT(710);
- N_STATEMENT(711);
- N_STATEMENT(712);
- N_STATEMENT(713);
- N_STATEMENT(714);
- N_STATEMENT(715);
- N_STATEMENT(716);
- N_STATEMENT(717);
- N_STATEMENT(718);
- N_STATEMENT(719);
- N_STATEMENT(720);
- N_STATEMENT(721);
- N_STATEMENT(722);
- N_STATEMENT(723);
- N_STATEMENT(724);
- N_STATEMENT(725);
- N_STATEMENT(726);
- N_STATEMENT(727);
- N_STATEMENT(728);
- N_STATEMENT(729);
- N_STATEMENT(730);
- N_STATEMENT(731);
- N_STATEMENT(732);
- N_STATEMENT(733);
- N_STATEMENT(734);
- N_STATEMENT(735);
- N_STATEMENT(736);
- N_STATEMENT(737);
- N_STATEMENT(738);
- N_STATEMENT(739);
- N_STATEMENT(740);
- N_STATEMENT(741);
- N_STATEMENT(742);
- N_STATEMENT(743);
- N_STATEMENT(744);
- N_STATEMENT(745);
- N_STATEMENT(746);
- N_STATEMENT(747);
- N_STATEMENT(748);
- N_STATEMENT(749);
- N_STATEMENT(750);
- N_STATEMENT(751);
- N_STATEMENT(752);
- N_STATEMENT(753);
- N_STATEMENT(754);
- N_STATEMENT(755);
- N_STATEMENT(756);
- N_STATEMENT(757);
- N_STATEMENT(758);
- N_STATEMENT(759);
- N_STATEMENT(760);
- N_STATEMENT(761);
- N_STATEMENT(762);
- N_STATEMENT(763);
- N_STATEMENT(764);
- N_STATEMENT(765);
- N_STATEMENT(766);
- N_STATEMENT(767);
- N_STATEMENT(768);
- N_STATEMENT(769);
- N_STATEMENT(770);
- N_STATEMENT(771);
- N_STATEMENT(772);
- N_STATEMENT(773);
- N_STATEMENT(774);
- N_STATEMENT(775);
- N_STATEMENT(776);
- N_STATEMENT(777);
- N_STATEMENT(778);
- N_STATEMENT(779);
- N_STATEMENT(780);
- N_STATEMENT(781);
- N_STATEMENT(782);
- N_STATEMENT(783);
- N_STATEMENT(784);
- N_STATEMENT(785);
- N_STATEMENT(786);
- N_STATEMENT(787);
- N_STATEMENT(788);
- N_STATEMENT(789);
- N_STATEMENT(790);
- N_STATEMENT(791);
- N_STATEMENT(792);
- N_STATEMENT(793);
- N_STATEMENT(794);
- N_STATEMENT(795);
- N_STATEMENT(796);
- N_STATEMENT(797);
- N_STATEMENT(798);
- N_STATEMENT(799);
- N_STATEMENT(800);
- N_STATEMENT(801);
- N_STATEMENT(802);
- N_STATEMENT(803);
- N_STATEMENT(804);
- N_STATEMENT(805);
- N_STATEMENT(806);
- N_STATEMENT(807);
- N_STATEMENT(808);
- N_STATEMENT(809);
- N_STATEMENT(810);
- N_STATEMENT(811);
- N_STATEMENT(812);
- N_STATEMENT(813);
- N_STATEMENT(814);
- N_STATEMENT(815);
- N_STATEMENT(816);
- N_STATEMENT(817);
- N_STATEMENT(818);
- N_STATEMENT(819);
- N_STATEMENT(820);
- N_STATEMENT(821);
- N_STATEMENT(822);
- N_STATEMENT(823);
- N_STATEMENT(824);
- N_STATEMENT(825);
- N_STATEMENT(826);
- N_STATEMENT(827);
- N_STATEMENT(828);
- N_STATEMENT(829);
- N_STATEMENT(830);
- N_STATEMENT(831);
- N_STATEMENT(832);
- N_STATEMENT(833);
- N_STATEMENT(834);
- N_STATEMENT(835);
- N_STATEMENT(836);
- N_STATEMENT(837);
- N_STATEMENT(838);
- N_STATEMENT(839);
- N_STATEMENT(840);
- N_STATEMENT(841);
- N_STATEMENT(842);
- N_STATEMENT(843);
- N_STATEMENT(844);
- N_STATEMENT(845);
- N_STATEMENT(846);
- N_STATEMENT(847);
- N_STATEMENT(848);
- N_STATEMENT(849);
- N_STATEMENT(850);
- N_STATEMENT(851);
- N_STATEMENT(852);
- N_STATEMENT(853);
- N_STATEMENT(854);
- N_STATEMENT(855);
- N_STATEMENT(856);
- N_STATEMENT(857);
- N_STATEMENT(858);
- N_STATEMENT(859);
- N_STATEMENT(860);
- N_STATEMENT(861);
- N_STATEMENT(862);
- N_STATEMENT(863);
- N_STATEMENT(864);
- N_STATEMENT(865);
- N_STATEMENT(866);
- N_STATEMENT(867);
- N_STATEMENT(868);
- N_STATEMENT(869);
- N_STATEMENT(870);
- N_STATEMENT(871);
- N_STATEMENT(872);
- N_STATEMENT(873);
- N_STATEMENT(874);
- N_STATEMENT(875);
- N_STATEMENT(876);
- N_STATEMENT(877);
- N_STATEMENT(878);
- N_STATEMENT(879);
- N_STATEMENT(880);
- N_STATEMENT(881);
- N_STATEMENT(882);
- N_STATEMENT(883);
- N_STATEMENT(884);
- N_STATEMENT(885);
- N_STATEMENT(886);
- N_STATEMENT(887);
- N_STATEMENT(888);
- N_STATEMENT(889);
- N_STATEMENT(890);
- N_STATEMENT(891);
- N_STATEMENT(892);
- N_STATEMENT(893);
- N_STATEMENT(894);
- N_STATEMENT(895);
- N_STATEMENT(896);
- N_STATEMENT(897);
- N_STATEMENT(898);
- N_STATEMENT(899);
- N_STATEMENT(900);
- N_STATEMENT(901);
- N_STATEMENT(902);
- N_STATEMENT(903);
- N_STATEMENT(904);
- N_STATEMENT(905);
- N_STATEMENT(906);
- N_STATEMENT(907);
- N_STATEMENT(908);
- N_STATEMENT(909);
- N_STATEMENT(910);
- N_STATEMENT(911);
- N_STATEMENT(912);
- N_STATEMENT(913);
- N_STATEMENT(914);
- N_STATEMENT(915);
- N_STATEMENT(916);
- N_STATEMENT(917);
- N_STATEMENT(918);
- N_STATEMENT(919);
- N_STATEMENT(920);
- N_STATEMENT(921);
- N_STATEMENT(922);
- N_STATEMENT(923);
- N_STATEMENT(924);
- N_STATEMENT(925);
- N_STATEMENT(926);
- N_STATEMENT(927);
- N_STATEMENT(928);
- N_STATEMENT(929);
- N_STATEMENT(930);
- N_STATEMENT(931);
- N_STATEMENT(932);
- N_STATEMENT(933);
- N_STATEMENT(934);
- N_STATEMENT(935);
- N_STATEMENT(936);
- N_STATEMENT(937);
- N_STATEMENT(938);
- N_STATEMENT(939);
- N_STATEMENT(940);
- N_STATEMENT(941);
- N_STATEMENT(942);
- N_STATEMENT(943);
- N_STATEMENT(944);
- N_STATEMENT(945);
- N_STATEMENT(946);
- N_STATEMENT(947);
- N_STATEMENT(948);
- N_STATEMENT(949);
- N_STATEMENT(950);
- N_STATEMENT(951);
- N_STATEMENT(952);
- N_STATEMENT(953);
- N_STATEMENT(954);
- N_STATEMENT(955);
- N_STATEMENT(956);
- N_STATEMENT(957);
- N_STATEMENT(958);
- N_STATEMENT(959);
- N_STATEMENT(960);
- N_STATEMENT(961);
- N_STATEMENT(962);
- N_STATEMENT(963);
- N_STATEMENT(964);
- N_STATEMENT(965);
- N_STATEMENT(966);
- N_STATEMENT(967);
- N_STATEMENT(968);
- N_STATEMENT(969);
- N_STATEMENT(970);
- N_STATEMENT(971);
- N_STATEMENT(972);
- N_STATEMENT(973);
- N_STATEMENT(974);
- N_STATEMENT(975);
- N_STATEMENT(976);
- N_STATEMENT(977);
- N_STATEMENT(978);
- N_STATEMENT(979);
- N_STATEMENT(980);
- N_STATEMENT(981);
- N_STATEMENT(982);
- N_STATEMENT(983);
- N_STATEMENT(984);
- N_STATEMENT(985);
- N_STATEMENT(986);
- N_STATEMENT(987);
- N_STATEMENT(988);
- N_STATEMENT(989);
- N_STATEMENT(990);
- N_STATEMENT(991);
- N_STATEMENT(992);
- N_STATEMENT(993);
- N_STATEMENT(994);
- N_STATEMENT(995);
- N_STATEMENT(996);
- N_STATEMENT(997);
- N_STATEMENT(998);
- N_STATEMENT(999);
- N_STATEMENT(1000);
- N_STATEMENT(1001);
- N_STATEMENT(1002);
- N_STATEMENT(1003);
- N_STATEMENT(1004);
- N_STATEMENT(1005);
- N_STATEMENT(1006);
- N_STATEMENT(1007);
- N_STATEMENT(1008);
- N_STATEMENT(1009);
- N_STATEMENT(1010);
- N_STATEMENT(1011);
- N_STATEMENT(1012);
- N_STATEMENT(1013);
- N_STATEMENT(1014);
- N_STATEMENT(1015);
- N_STATEMENT(1016);
- N_STATEMENT(1017);
- N_STATEMENT(1018);
- N_STATEMENT(1019);
- N_STATEMENT(1020);
- N_STATEMENT(1021);
- N_STATEMENT(1022);
- N_STATEMENT(1023);
- }
- return atom_error; /* NOTREACHED, shut up the compiler */
+ return erl_nif_user_trace_n(env, argc, argv);
#else
return atom_error;
#endif
diff --git a/lib/runtime_tools/c_src/trace_ip_drv.c b/lib/runtime_tools/c_src/trace_ip_drv.c
index 7f7ab8dd9d..a7d132ca6e 100644
--- a/lib/runtime_tools/c_src/trace_ip_drv.c
+++ b/lib/runtime_tools/c_src/trace_ip_drv.c
@@ -34,21 +34,12 @@
#include <stdlib.h>
#include <string.h>
#ifndef __WIN32__
-# ifdef VXWORKS
-# include <sockLib.h>
-# include <sys/times.h>
-# include <iosLib.h>
-# include <taskLib.h>
-# include <selectLib.h>
-# include <ioLib.h>
-# include "reclaim.h"
-# endif
-# include <unistd.h>
-# include <errno.h>
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <netinet/in.h>
-# include <fcntl.h>
+# include <unistd.h>
+# include <errno.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <fcntl.h>
#endif
#ifdef DEBUG
@@ -590,8 +581,8 @@ static void *my_alloc(size_t size)
void *ret;
if ((ret = driver_alloc(size)) == NULL) {
/* May or may not work... */
- fprintf(stderr, "Could not allocate %d bytes of memory in %s.",
- (int) size, __FILE__);
+ fprintf(stderr, "Could not allocate %lu bytes of memory in %s.",
+ (unsigned long) size, __FILE__);
exit(1);
}
return ret;
@@ -605,8 +596,8 @@ static ErlDrvBinary *my_alloc_binary(int size)
ErlDrvBinary *ret;
if ((ret = driver_alloc_binary(size)) == NULL) {
/* May or may not work... */
- fprintf(stderr, "Could not allocate a binary of %d bytes in %s.",
- (int) size, __FILE__);
+ fprintf(stderr, "Could not allocate a binary of %lu bytes in %s.",
+ (unsigned long) size, __FILE__);
exit(1);
}
return ret;
@@ -910,7 +901,7 @@ static void stop_select(ErlDrvEvent event, void* _)
WSACloseEvent((HANDLE)event);
}
-#else /* UNIX/VXWORKS */
+#else /* UNIX */
static int my_driver_select(TraceIpData *desc, SOCKET fd, int flags, enum MySelectOp op)
{
diff --git a/lib/runtime_tools/doc/src/Makefile b/lib/runtime_tools/doc/src/Makefile
index d240b287c3..51d93df418 100644
--- a/lib/runtime_tools/doc/src/Makefile
+++ b/lib/runtime_tools/doc/src/Makefile
@@ -43,9 +43,11 @@ XML_APPLICATION_FILES = ref_man.xml
XML_REF3_FILES = dbg.xml dyntrace.xml erts_alloc_config.xml
XML_REF6_FILES = runtime_tools_app.xml
-XML_PART_FILES = part_notes.xml part_notes_history.xml
+XML_PART_FILES = part_notes.xml part_notes_history.xml part.xml
XML_CHAPTER_FILES = notes.xml notes_history.xml
+GENERATED_XML_FILES = DTRACE.xml SYSTEMTAP.xml
+
BOOK_FILES = book.xml
XML_FILES = \
@@ -78,6 +80,11 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
+$(XML_FILES): $(GENERATED_XML_FILES)
+
+%.xml: $(ERL_TOP)/HOWTO/%.md $(ERL_TOP)/make/emd2exml
+ $(ERL_TOP)/make/emd2exml $< $@
+
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
diff --git a/lib/runtime_tools/doc/src/book.xml b/lib/runtime_tools/doc/src/book.xml
index 3f0dd7d55e..ad7d709644 100644
--- a/lib/runtime_tools/doc/src/book.xml
+++ b/lib/runtime_tools/doc/src/book.xml
@@ -34,6 +34,9 @@
<preamble>
<contents level="2"></contents>
</preamble>
+ <parts lift="no">
+ <xi:include href="part.xml"/>
+ </parts>
<applications>
<xi:include href="ref_man.xml"/>
</applications>
diff --git a/lib/runtime_tools/doc/src/dyntrace.xml b/lib/runtime_tools/doc/src/dyntrace.xml
index 5fc5530f25..f0149d0665 100644
--- a/lib/runtime_tools/doc/src/dyntrace.xml
+++ b/lib/runtime_tools/doc/src/dyntrace.xml
@@ -42,7 +42,7 @@
</list>
<p>Both building with dynamic trace probes and using them is experimental and unsupported by Erlang/OTP. It is included as an option for the developer to trace and debug performance issues in their systems.</p>
<p>The original implementation is mostly done by Scott Lystiger Fritchie as an Open Source Contribution and it should be viewed as such even though the source for dynamic tracing as well as this module is included in the main distribution. However, the ability to use dynamic tracing of the virtual machine is a very valuable contribution which OTP has every intention to maintain as a tool for the developer.</p>
- <p>How to write <c>d</c> programs or <c>systemtap</c> scripts can be learned from books and from a lot of pages on the Internet. This manual page does not include any documentation about using the dynamic trace tools of respective platform. The <c>examples</c> directory of the <c>runtime_tools</c> application however contains comprehensive examples of both <c>d</c> and <c>systemtap</c> programs that will help you get started. Another source of information is the <c>README.dtrace(.md)</c> and <c>README.systemtap(.md)</c> files in the Erlang source top directory.</p>
+ <p>How to write <c>d</c> programs or <c>systemtap</c> scripts can be learned from books and from a lot of pages on the Internet. This manual page does not include any documentation about using the dynamic trace tools of respective platform. The <c>examples</c> directory of the <c>runtime_tools</c> application however contains comprehensive examples of both <c>d</c> and <c>systemtap</c> programs that will help you get started. Another source of information is the <seealso marker="DTRACE">dtrace</seealso> and <seealso marker="SYSTEMTAP">systemtap</seealso> chapters in the Runtime Tools Users' Guide.</p>
</description>
<funcs>
<func>
diff --git a/lib/runtime_tools/doc/src/notes.xml b/lib/runtime_tools/doc/src/notes.xml
index 2d4a206e1c..90641719c5 100644
--- a/lib/runtime_tools/doc/src/notes.xml
+++ b/lib/runtime_tools/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,52 @@
<p>This document describes the changes made to the Runtime_Tools
application.</p>
+<section><title>Runtime_Tools 1.8.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Change the module-level docs to give complete
+ step-by-step instructions for using the `dyntrace:p()`
+ trace function. (Thanks to Scott Lystig Fritchie)</p>
+ <p>
+ Own Id: OTP-10141</p>
+ </item>
+ <item>
+ <p>
+ Add 1024 separate USDT probes to dyntrace.erl and
+ dyntrace.c (Thanks to Scott Lystig Fritchie)</p>
+ <p>
+ Own Id: OTP-10143</p>
+ </item>
+ <item>
+ <p>
+ Relocate bodies of DTrace probes to the statically-linked
+ VM.</p>
+ <p>
+ Due to various operating systems (in both the DTrace and
+ SystemTap worlds) not fully supporting DTrace probes (or
+ SystemTap-compatibility mode probes) in shared libraries,
+ we relocate those probes to the statically-linked virtual
+ machine. This could be seen as pollution of the pristine
+ VM by a (yet) experimental feature. However:</p>
+ <p>
+ 1. This code can be eliminated completely by the C
+ preprocessor. 2. Leaving the probes in the dyntrace NIF
+ shared library simply does not work correctly on too many
+ platforms. *Many* thanks to Macneil Shonle at Basho for
+ assisting when my RSI-injured fingers gave out. (note:
+ Solaris 10 and FreeBSD 9.0-RELEASE can take a long time
+ to compile)</p>
+ <p>
+ Own Id: OTP-10189</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Runtime_Tools 1.8.8</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/inviso/doc/src/part.xml b/lib/runtime_tools/doc/src/part.xml
index 08ced28f47..948d4a8020 100644
--- a/lib/inviso/doc/src/part.xml
+++ b/lib/runtime_tools/doc/src/part.xml
@@ -4,7 +4,7 @@
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>2006</year><year>2009</year>
+ <year>2012</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -21,15 +21,22 @@
</legalnotice>
- <title>Inviso User's Guide</title>
- <prepared></prepared>
+ <title>Runtime Tools User's Guide</title>
+ <prepared>Lukas Larsson</prepared>
<docno></docno>
- <date></date>
+ <date>2012-07-18</date>
<rev></rev>
+ <file>part.xml</file>
</header>
+
<description>
- <p><em>Inviso</em>, an Erlang trace tool.</p>
+ <p><em>Runtime Tools</em></p>
</description>
- <xi:include href="inviso_chapter.xml"/>
+
+ <xi:include href="DTRACE.xml"/>
+ <xi:include href="SYSTEMTAP.xml"/>
</part>
+
+
+
diff --git a/lib/runtime_tools/examples/user-probe-n.d b/lib/runtime_tools/examples/user-probe-n.d
new file mode 100644
index 0000000000..06a3e5c9b9
--- /dev/null
+++ b/lib/runtime_tools/examples/user-probe-n.d
@@ -0,0 +1,44 @@
+/* example usage: dtrace -q -s /path/to/user-probe.d */
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Scott Lystig Fritchie 2011-2012. 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
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+erlang*:::user_trace-n0
+{
+ printf("probe n0: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n",
+ copyinstr(arg0),
+ arg1 == NULL ? "" : copyinstr(arg1),
+ arg2, arg3, arg4, arg5,
+ arg6 == NULL ? "" : copyinstr(arg6),
+ arg7 == NULL ? "" : copyinstr(arg7),
+ arg8 == NULL ? "" : copyinstr(arg8),
+ arg9 == NULL ? "" : copyinstr(arg9));
+}
+
+erlang*:::user_trace-n1
+{
+ printf("probe n1: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n",
+ copyinstr(arg0),
+ arg1 == NULL ? "" : copyinstr(arg1),
+ arg2, arg3, arg4, arg5,
+ arg6 == NULL ? "" : copyinstr(arg6),
+ arg7 == NULL ? "" : copyinstr(arg7),
+ arg8 == NULL ? "" : copyinstr(arg8),
+ arg9 == NULL ? "" : copyinstr(arg9));
+}
+
diff --git a/lib/runtime_tools/examples/user-probe-n.systemtap b/lib/runtime_tools/examples/user-probe-n.systemtap
new file mode 100644
index 0000000000..6aa415bb67
--- /dev/null
+++ b/lib/runtime_tools/examples/user-probe-n.systemtap
@@ -0,0 +1,53 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. 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
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Note: This file assumes that you're using the non-SMP-enabled Erlang
+ * virtual machine, "beam". The SMP-enabled VM is called "beam.smp".
+ * Note that other variations of the virtual machine also have
+ * different names, e.g. the debug build of the SMP-enabled VM
+ * is "beam.debug.smp".
+ *
+ * To use a different virtual machine, replace each instance of
+ * "beam" with "beam.smp" or the VM name appropriate to your
+ * environment.
+ */
+
+probe process("beam").mark("user_trace-n0")
+{
+ printf("probe n0: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n",
+ user_string($arg1),
+ $arg2 == NULL ? "" : user_string($arg2),
+ $arg3, $arg4, $arg5, $arg6,
+ $arg7 == NULL ? "" : user_string($arg7),
+ $arg8 == NULL ? "" : user_string($arg8),
+ $arg9 == NULL ? "" : user_string($arg9),
+ $arg9 == NULL ? "" : user_string($arg9));
+}
+
+probe process("beam").mark("user_trace-n1")
+{
+ printf("probe n1: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n",
+ user_string($arg1),
+ $arg2 == NULL ? "" : user_string($arg2),
+ $arg3, $arg4, $arg5, $arg6,
+ $arg7 == NULL ? "" : user_string($arg7),
+ $arg8 == NULL ? "" : user_string($arg8),
+ $arg9 == NULL ? "" : user_string($arg9),
+ $arg9 == NULL ? "" : user_string($arg9));
+}
diff --git a/lib/runtime_tools/examples/user-probe.systemtap b/lib/runtime_tools/examples/user-probe.systemtap
index b41d7483ad..0482235324 100644
--- a/lib/runtime_tools/examples/user-probe.systemtap
+++ b/lib/runtime_tools/examples/user-probe.systemtap
@@ -28,12 +28,12 @@
* environment.
*/
-probe process("dyntrace.so").mark("user_trace-s1")
+probe process("beam").mark("user_trace-s1")
{
printf("%s\n", user_string($arg1));
}
-probe process("dyntrace.so").mark("user_trace-i4s4")
+probe process("beam").mark("user_trace-i4s4")
{
printf("%s %s %d %d %d %d '%s' '%s' '%s' '%s'\n",
user_string($arg1),
diff --git a/lib/runtime_tools/src/Makefile b/lib/runtime_tools/src/Makefile
index 810e3e8741..d6750f3a88 100644
--- a/lib/runtime_tools/src/Makefile
+++ b/lib/runtime_tools/src/Makefile
@@ -36,12 +36,6 @@ RELSYSDIR = $(RELEASE_PATH)/lib/runtime_tools-$(VSN)
MODULES= \
erts_alloc_config \
- inviso_rt \
- inviso_rt_meta \
- inviso_rt_lib \
- inviso_as_lib \
- inviso_autostart \
- inviso_autostart_server \
runtime_tools \
runtime_tools_sup \
dbg \
diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl
index 385047ee73..d35c8e781e 100644
--- a/lib/runtime_tools/src/dbg.erl
+++ b/lib/runtime_tools/src/dbg.erl
@@ -431,10 +431,8 @@ trace_port1(file, Filename, Options) ->
fun() ->
Name = filename:absname(Filename),
%% Absname is needed since the driver uses
- %% the supplied name without further investigations,
- %% and if the name is relative the resulting path
- %% might be too long which can cause a bus error
- %% on vxworks instead of a nice error code return.
+ %% the supplied name without further investigations.
+
%% Also, the absname must be found inside the fun,
%% in case the actual node where the port shall be
%% started is on another node (or even another host)
diff --git a/lib/runtime_tools/src/inviso_as_lib.erl b/lib/runtime_tools/src/inviso_as_lib.erl
deleted file mode 100644
index 75f3d9d004..0000000000
--- a/lib/runtime_tools/src/inviso_as_lib.erl
+++ /dev/null
@@ -1,155 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. 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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%% File : inviso_as_lib.erl
-%%% Author : Lennart �hman <[email protected]>
-%%% Description :
-%% The purpose of the inviso autostart library is to provide useful functions
-%% for anyone wanting to customize the autostart mechanism in the inviso
-%% tracer. It is intended to work well with the example 'inviso_autostart_server'.
-%%%
-%%% Created : 15 Dec 2005 by Lennart �hman
-%% -----------------------------------------------------------------------------
-
--module(inviso_as_lib).
-
--export([setup_autostart/7,setup_autostart/8,setup_autostart/9,
- inhibit_autostart/1,
- set_repeat/2,set_repeat_2/2]).
-%% -----------------------------------------------------------------------------
-
-%% setup_autostart(Node,Repeat,Options,TracerData,CmdFiles,Bindings) = ok|{error,Reason}.
-%% Repeat=integer(), where 0 means no (more) autostarts.
-%% Options=List of options as taken by the runtime component at start-up.
-%% TracerData= Tracerdata as given to inviso_rt:init_tracing.
-%% CmdFiles=[FileName,...] list of string(), files that will be executed
-%% by the subprocess started during autostart.
-%% Bindings=[{VarName,Value},...] Variable bindings for CmdFiles.
-%% VarName=atom(),
-%%
-%% This function creates the inviso_autostart.config file on Erlang node Node.
-%% This is useful when you wish to prepare for an autostarted trace.
-setup_autostart(Node,Repeat,Options,TracerData,CmdFiles,Bindings,Translations) ->
- setup_autostart(Node,Repeat,Options,TracerData,CmdFiles,
- Bindings,Translations,inviso_std_ref,off).
-setup_autostart(Node,Repeat,Options,TracerData,CmdFiles,Bindings,Translations,RTtag) ->
- setup_autostart(Node,Repeat,Options,TracerData,CmdFiles,
- Bindings,Translations,RTtag,off).
-setup_autostart(Node,Repeat,Options,TracerData,CmdFiles,Bindings,Translations,RTtag,Dbg) ->
- case rpc:call(Node,inviso_autostart,which_config_file,[]) of
- FileName when is_list(FileName) -> % Write to this file then.
- {String,Args}=format_config_file(Repeat,TracerData,Options,CmdFiles,
- Bindings,Translations,RTtag,Dbg),
- Bytes=list_to_binary(io_lib:format(String,Args)),
- case rpc:call(Node,file,write_file,[FileName,Bytes]) of
- ok ->
- ok;
- {error,Reason} ->
- {error,{write_file,Reason}};
- {badrpc,Reason} ->
- {error,{badrpc,{write_file,Reason}}}
- end;
- {error,Reason} ->
- {error,{which_config_file,Reason}};
- {badrpc,Reason} ->
- {error,{badrpc,{which_config_file,Reason}}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% inhibit_autostart(Node) = ok|{error,Reason}
-%%
-%% Inhibits autostart by simply making the repeat parameter zero in the
-%% configuration file at node Node. All other parameters are left untouched.
-inhibit_autostart(Node) ->
- set_repeat(Node,0).
-%% -----------------------------------------------------------------------------
-
-%% set_repeat(Node,N)=ok | {error,Reason}
-%% N=integer(), the number of time autostart shall be allowed.
-set_repeat(Node,N) ->
- case examine_config_file(Node) of
- {ok,FileName,Terms} ->
- NewTerms=[{repeat,N}|lists:keydelete(repeat,1,Terms)],
- case rpc:call(Node,?MODULE,set_repeat_2,[FileName,NewTerms]) of
- {badrpc,Reason} ->
- {error,{badrpc,{open,Reason}}};
- Result ->
- Result
- end;
- {error,Reason} ->
- {error,Reason}
- end.
-
-%% Must be a sepparate function to do rpc on. The entire function must be done
-%% in one rpc call. Otherwise the FD will die since it is linked to the opening
-%% process.
-set_repeat_2(FileName,NewTerms) ->
- case file:open(FileName,[write]) of
- {ok,FD} ->
- String=lists:flatten(lists:map(fun(_)->"~w.~n" end,NewTerms)),
- case catch io:format(FD,String,NewTerms) of
- ok ->
- file:close(FD),
- ok;
- {'EXIT',Reason} ->
- file:close(FD),
- {error,{format,Reason}}
- end;
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-examine_config_file(Node) ->
- case rpc:call(Node,inviso_autostart,which_config_file,[]) of
- FileName when is_list(FileName) -> % Read this file, and then modify it.
- case rpc:call(Node,file,consult,[FileName]) of
- {ok,Terms} ->
- {ok,FileName,Terms};
- {error,Reason} ->
- {error,{consult,Reason}};
- {badrpc,Reason} ->
- {error,{badrpc,{consult,Reason}}}
- end;
- {error,Reason} ->
- {error,{which_config_file,Reason}};
- {badrpc,Reason} ->
- {error,{badrpc,{which_config_file,Reason}}}
- end.
-%% -----------------------------------------------------------------------------
-
-format_config_file(Repeat,TracerData,Options,CmdFiles,Bindings,Translations,RTtag,Dbg) ->
- String="~w.~n~w.~n~w.~n~w.~n",
- Args=[{repeat,Repeat},
- {mfa,{inviso_autostart_server,init,[[{tracerdata,TracerData},
- {cmdfiles,CmdFiles},
- {bindings,Bindings},
- {translations,Translations},
- {debug,Dbg}]]}},
- {options,Options},
- {tag,RTtag}],
- {String,Args}.
-%% -----------------------------------------------------------------------------
-
-
-
-
-
-
-
diff --git a/lib/runtime_tools/src/inviso_autostart.erl b/lib/runtime_tools/src/inviso_autostart.erl
deleted file mode 100644
index 787292e244..0000000000
--- a/lib/runtime_tools/src/inviso_autostart.erl
+++ /dev/null
@@ -1,201 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2010. 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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%% Author: Lennart �hman, [email protected]
--module(inviso_autostart).
-
--export([autostart/1,which_config_file/0]).
-
-%% This module implements the default autostart module for the inviso runtime
-%% component.
-%% It will:
-%% (1) Open the autostart configuration file (either the default or the one
-%% pointed out by the runtime_tools application parameter inviso_autostart_config).
-%% (2) Check that the incarnation counter has not reached 0. If so, we do not
-%% allow (yet) one autostart.
-%% (3) Rewrite the configuration file if there was an incarnation counter.
-%% (With the counter decreased).
-%% (4) Inspect the content of the configuration file and pass paramters in the
-%% return value (which is interpreted by the runtime component).
-%%
-%% CONTENT OF A CONFIGURATION FILE:
-%% A plain text file containing erlang tuple terms, each ended with a period(.).
-%% The following parameters are recognized:
-%% {repeat,N} N=interger(),
-%% The number of remaining allowed autostart incarnations of inviso.
-%% {options,Options} Options=list()
-%% The options which controls the runtime component, such as overload and
-%% dependency.
-%% {mfa,{Mod,Func,Args}} Args=list()
-%% Controls how a spy process initiating tracing, patterns and flags shall
-%% be started.
-%% {tag,Tag}
-%% The tag identifying the runtime component to control components.
-%% =============================================================================
-
-%% This function is run in the runtime component's context during autostart
-%% to determine whether to continue and if, then how.
-autostart(_AutoModArgs) ->
- ConfigFile=
- case application:get_env(inviso_autostart_conf) of
- {ok,FileName} when is_list(FileName) -> % Use this filename then.
- FileName;
- {ok,{load,FileNames,{M,F}}} -> % First load the module, then...
- case try_load_module(FileNames) of
- ok ->
- autostart_apply(M,F);
-
- false -> % No such module available
- "inviso_autostart.config"
- end;
- {ok,{M,F}} -> % Use M:F(node())
- autostart_apply(M,F);
- {ok,no_autostart} ->
- false;
- _ -> % Use a default name, in CWD!
- "inviso_autostart.config"
- end,
- if
- is_list(ConfigFile) ->
- case file:consult(ConfigFile) of
- {ok,Terms} -> % There is a configuration.
- case handle_repeat(ConfigFile,Terms) of
- ok -> % Handled or not, we shall continue.
- {get_mfa(Terms),get_options(Terms),get_tag(Terms)};
- stop -> % We are out of allowed starts.
- true % Then no autostart.
- end;
- {error,_} -> % There is no config file
- true % Then no autostart!
- end;
- true -> % Skip it then.
- true
- end.
-
-autostart_apply(M,F) ->
- case catch M:F(node()) of
- FileName when is_list(FileName) ->
- FileName;
- no_autostart -> % No autostart after all.
- false;
- _ ->
- "inviso_autostart.config"
- end.
-
-%% This function is necessary since it is not always the case that all code-paths
-%% are set at the time of an autostart.
-try_load_module([AbsFileName|Rest]) when is_list(AbsFileName) ->
- case catch code:load_abs(AbsFileName) of % May not be a proper filename.
- {module,_Mod} ->
- try_load_module(Rest);
- _ ->
- false
- end;
-try_load_module([]) -> % Load all beam files successfully.
- ok;
-try_load_module(AbsFileName) when is_list(AbsFileName) ->
- try_load_module([AbsFileName]).
-%% -----------------------------------------------------------------------------
-
-%% Function returning the filename probably used as autostart config file.
-%% Note that this function must be executed at the node in question.
-which_config_file() ->
- case application:get_env(runtime_tools,inviso_autostart_conf) of
- {ok,FileName} when is_list(FileName) -> % Use this filename then.
- FileName;
- {ok,{M,F}} -> % Use M:F(node())
- case catch M:F(node()) of
- FileName when is_list(FileName) ->
- FileName;
- _ ->
- {ok,CWD}=file:get_cwd(),
- filename:join(CWD,"inviso_autostart.config")
- end;
- _ -> % Use a default name, in CWD!
- {ok,CWD}=file:get_cwd(),
- filename:join(CWD,"inviso_autostart.config")
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% Help function which finds out if there is a limit on the number of times
-%% we shall autostart. If there is a repeat parameter and it is greater than
-%% zero, the file must be rewritten with the parameter decreased with one.
-%% Returns 'ok' or 'stop'.
-handle_repeat(FileName,Terms) ->
- case lists:keysearch(repeat,1,Terms) of
- {value,{_,N}} when N>0 -> % Controlls how many time more.
- handle_repeat_rewritefile(FileName,Terms,N-1),
- ok; % Indicate that we shall continue.
- {value,_} -> % No we have reached the limit.
- stop;
- false -> % There is no repeat parameter.
- ok % No restrictions then!
- end.
-
-%% Help function which writes the configuration file again, but with the
-%% repeat parameter set to NewN.
-%% Returns nothing significant.
-handle_repeat_rewritefile(FileName,Term,NewN) ->
- case file:open(FileName,[write]) of
- {ok,FD} ->
- NewTerm=lists:keyreplace(repeat,1,Term,{repeat,NewN}),
- handle_repeat_rewritefile_2(FD,NewTerm),
- file:close(FD);
- {error,_Reason} -> % Not much we can do then?!
- error
- end.
-
-handle_repeat_rewritefile_2(FD,[Tuple|Rest]) ->
- io:format(FD,"~w.~n",[Tuple]),
- handle_repeat_rewritefile_2(FD,Rest);
-handle_repeat_rewritefile_2(_,[]) ->
- true.
-%% -----------------------------------------------------------------------------
-
-%% Three help functions finding the parameters possible to give to the runtime
-%% component. Note that some of them have default values, should the parameter
-%% not exist.
-get_mfa(Terms) ->
- case lists:keysearch(mfa,1,Terms) of
- {value,{_,MFA}} ->
- MFA;
- false ->
- false
- end.
-
-get_options(Terms) ->
- case lists:keysearch(options,1,Terms) of
- {value,{_,Options}} ->
- Options;
- false ->
- []
- end.
-
-get_tag(Terms) ->
- case lists:keysearch(tag,1,Terms) of
- {value,{_,Tag}} ->
- Tag;
- false ->
- default_tag
- end.
-%% -----------------------------------------------------------------------------
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
diff --git a/lib/runtime_tools/src/inviso_autostart_server.erl b/lib/runtime_tools/src/inviso_autostart_server.erl
deleted file mode 100644
index 1e352822f4..0000000000
--- a/lib/runtime_tools/src/inviso_autostart_server.erl
+++ /dev/null
@@ -1,311 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2010. 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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%% Author: Lennart �hman, [email protected]
-%%
--module(inviso_autostart_server).
--export([init/1]).
-
-%% -----------------------------------------------------------------------------
-%% Internal exports
-%% -----------------------------------------------------------------------------
--export([cmd_file_interpreter_init/4]).
-%% -----------------------------------------------------------------------------
-
-
-%% This module provides a (well working) example of how to program an
-%% autostart server responsible for initializing trace, setting patterns
-%% and flags.
-%%
-%% The general idea is that this code spawns interpreter processes in order to
-%% execute commands concurrently. Each of the interpreter processes opens one or
-%% several files (in sequence) containing erlang function calls which are evaluated
-%% in the interpreter process context.
-%% The argument provided to init shall be a list of options controlling
-%% how to initialize tracing, which file(s) to open and variable bindings.
-%%
-%% This autostart_server interpreters understands standard inviso trace case files.
-%%
-%% The runtime component provides an API very similar to the API provided
-%% by the control component. It is therefore easy to translate inviso calls to
-%% inviso_rt calls.
-%%
-%% This process may be killed by the inviso_rt process if stop_tracing is called.
-%% The reason is that there is no time limit to the interpreter processes. Hence
-%% they should be killed if tracing is not possible anylonger.
-%% =============================================================================
-
-
-%% -----------------------------------------------------------------------------
-
-%% The independent autostart process spawned by the runtime component to carry
-%% out initializations is spawened on this function (if using the example
-%% autostart which comes with inviso).
-%% ArgsFromConfig is as can be heard from the name comming from a paramater in
-%% the autostart configuration file. Here it is supposed to be:
-%% ArgsFromConfig=[ServerParam,...]
-%% ServerParam={tracerdata,TracerData}|{cmdfiles,Files}|{bindings,Bindings}|
-%% {translations,Translations}|{debug,DbgLevel}
-%% TracerData=tracerdata given to inviso_rt:init_tracing/1 function.
-%% Files=[FileNameSpecs,...] where each FileNameSpecs will be executed in
-%% a separate process. Making each FileNameSpec parallel.
-%% FileNameSpecs=[FileNameSpec,...]
-%% FileNameSpec=FileName | {FileName,Bindings}
-%% Bindings=[{Var,Value},...] variable environment understood by
-%% erl_eval:exprs/2.
-%% Translations=[Translation,...]
-%% A translation file is a text-file with following tuples
-%% Translation={{Mod,Func,Arity,{Mod2,Func2,ParamMF}}}|
-%% {{Func,Arity,{Mod2,Func2,ParamMF}}}
-%% ParamMF={M,F} | any()
-%% Translates Mod:Func/Arity to Mod2:Func2 with the arguments to
-%% Mod:Func translated using M:F/1. Note that ParamMF is not
-%% necessarily an MF. If no translation shall be done, ParamMF
-%% shall be anything else but an MF.
-%% Also note that Mod is optional in a Translation. That means that
-%% function calls without a module in the trace case file will
-%% be translated according to that translation.
-init(ArgsFromConfig) ->
- case get_tracerdata_opts(ArgsFromConfig) of
- {ok,TracerData} -> % Otherwise we can not start a trace!
- case inviso_rt:init_tracing(TracerData) of
- {ok,_Response} -> % Ok, tracing has been initiated.
- case get_cmdfiles_opts(ArgsFromConfig) of
- {ok,CmdFiles} -> % List of cmd-files.
- Bindings=get_initialbindings_opts(ArgsFromConfig),
- Translations=get_translations_opts(ArgsFromConfig),
- Dbg=get_dbg_opts(ArgsFromConfig),
- Procs=start_cmd_file_interpreters(CmdFiles,
- Bindings,
- Translations,
- Dbg),
- loop(Procs,Dbg); % Wait for procs to be done.
- false -> % Then we can terminate normally.
- true
- end;
- {error,Reason} -> % This is fault, lets terminate abnormally.
- exit({inviso,{error,Reason}})
- end;
- false -> % Then there is not much use then.
- true % Just terminate normally.
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function which starts a process for each item found in the FileNames
-%% list. The idea is that each item will be processed concurrently. The items
-%% them selves may be a sequence of filenames.
-%% Returns a list of spawned interpret processes.
-start_cmd_file_interpreters([FileNames|Rest],Bindings,Translations,Dbg) ->
- P=spawn_link(?MODULE,cmd_file_interpreter_init,[FileNames,Bindings,Translations,Dbg]),
- MRef=erlang:monitor(process,P), % Can't trap exits in this process.
- [{P,MRef}|start_cmd_file_interpreters(Rest,Bindings,Translations,Dbg)];
-start_cmd_file_interpreters([],_,_,_) ->
- [].
-%% -----------------------------------------------------------------------------
-
-
-%% The loop where this process simply waits for all of the interpreters to be
-%% done. Note that that may take som time. An interpreter may take as long time
-%% necessary to do its task.
-loop(Procs,Dbg) ->
- receive
- {'DOWN',MRef,process,Pid,_Reason} ->
- case lists:keysearch(MRef,1,Procs) of
- {value,{Pid,_}} -> % It was an interpreter that terminated.
- case lists:keydelete(MRef,1,Procs) of
- [] -> % No more interpreters.
- true; % Then terminate.
- NewProcs ->
- loop(NewProcs,Dbg)
- end;
- false ->
- loop(Procs,Dbg)
- end;
- _ ->
- loop(Procs,Dbg)
- end.
-
-
-%% -----------------------------------------------------------------------------
-%% The interpret process.
-%%
-%% An interpreter process executes trace case files. Several interpreter processes
-%% may be running in parallel. It is not within the scoop of this implementation
-%% of an autostart server to solve conflicts. (You may implement your own autostart
-%% server!).
-%% An interpret process may run for as long as necessary. Hence the function called
-%% within the trace case file can contain wait functions, waiting for a certain
-%% system state to occure before continuing.
-%% Note that this process also mixes global and local bindings. GlobalBindings
-%% is a binding() structure, where LocalBindings is a list of {Var,Value}.
-%% Further it is possible to let FileName be a {inviso,Func,Args} tuple instead.
-%% -----------------------------------------------------------------------------
-
-%% Init function for an interpreter process instance.
-cmd_file_interpreter_init(FileNames,GlobalBindings,Translations,Dbg) ->
- interpret_cmd_files(FileNames,GlobalBindings,Translations,Dbg).
-
-interpret_cmd_files([{FileName,LocalBindings}|Rest],GlobalBindings,Translations,Dbg) ->
- Bindings=join_local_and_global_vars(LocalBindings,GlobalBindings),
- interpret_cmd_files_1(FileName,Bindings,Translations,Dbg),
- interpret_cmd_files(Rest,GlobalBindings,Translations,Dbg);
-interpret_cmd_files([],_,_,_) -> % Done, return nothing significant!
- true;
-interpret_cmd_files(FileName,GlobalBindings,Translations,Dbg) ->
- interpret_cmd_files_1(FileName,GlobalBindings,Translations,Dbg).
-% interpret_cmd_files(Rest,GlobalBindings,Translations,Dbg).
-
-%% This is "inline" inviso calls.
-interpret_cmd_files_1({inviso,F,Args},Bindings,Translations,Dbg) ->
- {ok,Tokens1,_}=erl_scan:string("inviso:"++atom_to_list(F)++"("),
- Tokens2=tokenize_args(Args),
- {ok,Tokens3,_}=erl_scan:string(")."),
- case erl_parse:parse_exprs(Tokens1++Tokens2++Tokens3) of
- {ok,Exprs} ->
- interpret_cmd_files_3(Bindings,Exprs,Translations,Dbg);
- {error,_Reason} ->
- error
- end;
-interpret_cmd_files_1({Mod,Func,Args},_Bindings,_Translations,_Dbg) ->
- catch apply(Mod,Func,Args);
-%% This is the case when it actually is a trace case file.
-interpret_cmd_files_1(FileName,Bindings,Translations,Dbg) ->
- case file:open(FileName,[read]) of
- {ok,FD} ->
- interpret_cmd_files_2(FD,Bindings,io:parse_erl_exprs(FD,""),Translations,Dbg),
- file:close(FD);
- {error,Reason} -> % Something wrong with the file.
- inviso_rt_lib:debug(Dbg,interpret_cmd_files,[FileName,{error,Reason}])
- end.
-
-%% Help function which handles Exprs returned from io:parse_erl_exprs and
-%% tries to eval them. It is the side-effects we are interested in, like
-%% setting flags and patterns. Note that we will get a failure should there
-%% be a variable conflict.
-%% Also note that there is logic to translate control component API calls to
-%% corresponding runtime component calls.
-%% Returns nothing significant.
-interpret_cmd_files_2(FD,Bindings,{ok,Exprs,_},Translations,Dbg) ->
- {next,NewBindings}=interpret_cmd_files_3(Bindings,Exprs,Translations,Dbg),
- interpret_cmd_files_2(FD,NewBindings,io:parse_erl_exprs(FD,""),Translations,Dbg);
-interpret_cmd_files_2(FD,Bindings,{error,ErrorInfo,Line},Translations,Dbg) ->
- inviso_rt_lib:debug(Dbg,parse_erl_exprs,[ErrorInfo,Line]),
- interpret_cmd_files_2(FD,Bindings,io:parse_erl_exprs(FD,""),Translations,Dbg);
-interpret_cmd_files_2(_,_,{eof,_},_,_) -> % End of file.
- true.
-
-interpret_cmd_files_3(Bindings,Exprs,Translations,Dbg) ->
- case catch inviso_rt_lib:transform(Exprs,Translations) of
- NewExprs when is_list(NewExprs) -> % We may have translated the API.
- case catch erl_eval:exprs(NewExprs,Bindings) of
- {'EXIT',Reason} ->
- inviso_rt_lib:debug(Dbg,exprs,[Exprs,Bindings,{'EXIT',Reason}]),
- {next,Bindings};
- {value,_Val,NewBindings} -> % Only interested in the side effects!
- {next,NewBindings}
- end;
- {'EXIT',Reason} ->
- inviso_rt_lib:debug(Dbg,translate2runtime_funcs,[Exprs,Reason]),
- {next,Bindings}
- end.
-
-%% Help function adding variables to a bindings structure. If the variable already
-%% is assigned in the structure, it will be overridden. Returns a new
-%% bindings structure.
-join_local_and_global_vars([{Var,Val}|Rest],Bindings) when is_atom(Var) ->
- join_local_and_global_vars(Rest,erl_eval:add_binding(Var,Val,Bindings));
-join_local_and_global_vars([_|Rest],Bindings) ->
- join_local_and_global_vars(Rest,Bindings);
-join_local_and_global_vars([],Bindings) ->
- Bindings.
-
-%% Help function returning a string of tokens, including "," separation
-%% between the arguments.
-tokenize_args(Args=[Arg|Rest]) when length(Args)>1 ->
- AbsTerm=erl_parse:abstract(Arg),
- Tokens=erl_parse:tokens(AbsTerm),
- {ok,Token,_}=erl_scan:string(","),
- Tokens++Token++tokenize_args(Rest);
-tokenize_args([Arg]) ->
- AbsTerm=erl_parse:abstract(Arg),
- erl_parse:tokens(AbsTerm);
-tokenize_args([]) ->
- "".
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Help functions working on the options given as argument to init during spawn.
-%% -----------------------------------------------------------------------------
-
-get_tracerdata_opts(ArgsFromConfig) ->
- case lists:keysearch(tracerdata,1,ArgsFromConfig) of
- {value,{_,{mfa,{M,F,CompleteTDGargs}}}} -> % Dynamic tracerdata.
- case catch apply(M,F,CompleteTDGargs) of
- {'EXIT',_Reason} ->
- false;
- TracerData ->
- {ok,TracerData}
- end;
- {value,{_,TracerData}} -> % Interpret this as static tracerdata.
- {ok,TracerData};
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-get_cmdfiles_opts(ArgsFromConfig) ->
- case lists:keysearch(cmdfiles,1,ArgsFromConfig) of
- {value,{_,CmdFiles}} ->
- {ok,CmdFiles};
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-get_initialbindings_opts(ArgsFromConfig) ->
- case lists:keysearch(bindings,1,ArgsFromConfig) of
- {value,{_,Bindings}} ->
- Bindings;
- false -> % Then we use empty bindings.
- erl_eval:new_bindings()
- end.
-%% -----------------------------------------------------------------------------
-
-get_translations_opts(ArgsFromConfig) ->
- case lists:keysearch(translations,1,ArgsFromConfig) of
- {value,{_,Translations}} ->
- Translations;
- false -> % This becomes nearly point less.
- []
- end.
-%% -----------------------------------------------------------------------------
-
-get_dbg_opts(ArgsFromConfig) ->
- case lists:keysearch(debug,1,ArgsFromConfig) of
- {value,{_,DbgLevel}} ->
- DbgLevel;
- false ->
- off
- end.
-%% -----------------------------------------------------------------------------
-
-%% EOF
-
-
-
diff --git a/lib/runtime_tools/src/inviso_rt.erl b/lib/runtime_tools/src/inviso_rt.erl
deleted file mode 100644
index b162f5b045..0000000000
--- a/lib/runtime_tools/src/inviso_rt.erl
+++ /dev/null
@@ -1,2885 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2011. 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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%% Description:
-%% The runtime component of the trace tool Inviso.
-%%
-%% Authors:
-%% Ann-Marie L�f, [email protected]
-%% Lennart �hman, [email protected]
-%% -----------------------------------------------------------------------------
-
--module(inviso_rt).
-
-
-%% -----------------------------------------------------------------------------
-%% interface for supervisor
-%% -----------------------------------------------------------------------------
--export([start_link_man/3,start_link_auto/1]).
-
-%% API for controll component.
--export([start/4,stop/1,
- init_tracing/2,stop_tracing_parallel/1,
- try_to_adopt/3,confirm_connection/2,get_node_info/1,
- suspend/2,call_suspend/2,cancel_suspension/1,change_options/2,
- clear/2,clear_all_tp/1,
- flush/1,
- trace_patterns_parallel/3,
- trace_flags_parallel/3,trace_flags_parallel/2,trace_flags_parallel/1,
- meta_tracer_call_parallel/2,
- get_status/1,get_tracerdata/1,list_logs/1,list_logs/2,fetch_log/2,fetch_log/3,
- delete_log/1,delete_log/2,
- state/1]).
-%% -----------------------------------------------------------------------------
-
-%% API mostly for autostart scripts, instead of corresponding control component
-%% apis not available doing local function calls.
--export([init_tracing/1,tp/4,tp/5,tp/1,tpg/4,tpg/5,tpg/1,
- tpl/4,tpl/5,tpl/1,
- ctp/1,ctp/3,ctpg/1,ctpg/3,ctpl/1,ctpl/3,
- init_tpm/4,init_tpm/7,
- tpm/4,tpm/5,tpm/8,tpm_tracer/4,tpm_tracer/5,tpm_tracer/8,
- tpm_ms/5,tpm_ms_tracer/5,
- ctpm_ms/4,
- local_register/0,global_register/0,
- ctpm/3,remove_local_register/0,remove_global_register/0,
- tf/2,tf/1,ctf/2,ctf/1]).
-%% -----------------------------------------------------------------------------
-
-%% Internal exports.
--export([init/4,auto_init/2,fetch_init/4]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Constants.
-%% -----------------------------------------------------------------------------
-
--define(DEFAULT_OVERLOAD_FUNC,default_overload_func).
--define(NO_LOADCHECK,no_loadcheck).
-
--define(RT_SUP,runtime_tools_sup). % Refers to the registered name.
--define(CTRL,inviso_c). % Refers to the registered name.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Record definition.
-%% -----------------------------------------------------------------------------
-
-%% #rt
-%% All record fields must be bound to listed values when leaving init or
-%% auto_init.
-%% dependency: Timeout accepting being without control component.
-%% overload : Controlls which module to call, if any, when time for a check.
-%% timer_ref: Used when timing delayed shutdown due to lost control component.
--record(rt,{state = new, % new | idle | tracing
- status = running, % running | {suspended, Reason}
- next_loadcheck = now(), % now | "No Loadcheck"
- parent, % pid()
- tracerdata, % undefined|{fun(),term()}|{file,Param}|{ip,Param}
- tracer_port, % port() | undefined
- handler, % {fun(), term()} | undefined
- auto_starter, % pid() | undefined; proc starting interpreters.
- meta_tracer, % undefined | pid()
- fetchers=[], % [pid(),...] processes transfering logfiles.
-% spies = [],
- dependency={infinity,node()}, % {TOut,Node} | TOut; TOut=int()|infinity
- overload=no_loadcheck, % ?NO_LOADCHECK|{LoadMF,Interval,InitMFA,RemoveMFA}
- overload_data=void, % Datastructure given to LoadMF and RemoveMFA.
- timer_ref, % undefined | reference()
- ctrl, % undefined | pid()
- ctrl_ref, % undefined | reference()
- vsn, % list()
- tag % term()
- }).
-%% -----------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Start API
-%% ==============================================================================
-
-%% Note that the runtime component may be started in many different ways.
-%% It can be autostarted by the runtime_tools_sup during initial start-up of the
-%% system. It is actually most likely that it will be started that way. However
-%% if there are no autostart trace-cases to run, the inviso_rt runtime component
-%% will terminate. It will then however remain as a child of the runtime_tools_sup
-%% supervisor. This means that if the runtime component is started again, manually,
-%% by the control component, some actions must be taken.
-%% For instance is it very likely that the child already exists. But since it
-%% must be started with different arguments when started manually, the child-spec
-%% must be changed.
-%%
-%% The runtime component is not a proper gen_server, to allow full control of
-%% what happens. It however mimcs gen_server behaviour to be managed by the
-%% runtime_tools_sup supervisor.
-
-
-%% start_link_auto(AutoModArgs)={ok,Pid}
-%%
-%% This function is entered into the child-spec when planning on doing autostart
-%% of the runtime component. The autostart is controlled by the so called
-%% inviso_autostart_mod. It is an application environment parameter of the
-%% runtime_tools application. If it exists, it shall point out a module name.
-%% If it does not exist, the default 'inviso_autostart' module will be tried.
-%% Note that these start_link functions do not implement proper otp-behaviour.
-%% For instance they return {ok,Pid} immediately making the init-phase of the
-%% runtime component process empty.
-%%
-%% The inviso_autostart_mod shall export one function:
-%% autostart(AutoModArgs) -> {MFA,Options,Tag}, where
-%% AutoModArgs=term(), comes from the application start parameters in the
-%% runtime_tools application resource file.
-%% MFA={Mod,Func,Args} | term().
-%% If it is MFA it will cause a trace initiator process to start spawning
-%% on spawn_link(Mod,Func,Args). The trace initiator may for instance
-%% initiate the wanted tracing.
-start_link_auto(AutoModArgs) ->
- {ok,spawn_link(?MODULE,auto_init,[AutoModArgs,self()])}.
-%% ------------------------------------------------------------------------------
-
-%% This function is entered into the child-specification of the runtime_tools_sup
-%% if the runtime component shall be started manually via the control component.
-start_link_man(Ctrl,Options,Tag) ->
- {ok,spawn_link(?MODULE,init,[Ctrl,Options,Tag,self()])}.
-%% ------------------------------------------------------------------------------
-
-%% start(Node,Options,Tag,Condition)=tbd
-%% Node=The node where the runtime component shall be started.
-%% Options=[Opt]; List of options to the runtime component.
-%% Opt={dependency,Val}|{dependency,{Val,Node}}
-%% Val=int()|infinity
-%% If the runtime component may run on its own or not. Val=0 means a runtime
-%% component which will terminate immediately without its control component.
-%% Note that if the runtime component is started manually, the Node part
-%% is never used. The runtime is supposed to be dependent of the Ctrl mentioned
-%% in the start_link_man parameters.
-%% Opt={overload,OverLoad} | overload
-%% The latter means no loadcheck. Necessary if changing the options.
-%% Overload=Iterval (int() in milliseconds) |
-%% {LoadMF,Interval}|{LoadMF,Interval,InitMFA,RemoveMFA}
-%% LoadMF={Mod,Func}|function()
-%% InitMFA,RemoveMFA={Mod,Func,ArgList} where
-%% apply(InitM,InitF,InitArgs) -> {ok,DataStruct}|'void'.
-%% apply(RemoveM,RemoveF,[DataStruct|Args]) -> don't care
-%% LoadMF is called each time loadcheck is performed.
-%% Mod:Func(DataStruct)->ok|{suspend,Reason}
-%% If just Interval is used, it means using a default overload check.
-%% Tag=term(), used to identify an incarnation of a runtime component so that
-%% a control component reconnecting will know if it was its own incarnation
-%% still alive, or some elses.
-%% Condition='if_ref'|term(). Controls if we want to adopt the runtime component.
-%% If 'if_ref' is stated it means that we only want to adopt a runtime component
-%% with the suggested Tag.
-%%
-%% This is the API used by the control component when tries to start a runtime
-%% component. Note that it will try to adopt an already running, if possible.
-%% Adoptions are only possible if the runtime component at hand is running
-%% without control component.
-start(Node, Options, Tag, Condition) when Node == node() ->
- ChildSpec = {?MODULE, {?MODULE, start_link_man, [self(), Options, Tag]},
- temporary, 5000, worker, [?MODULE]},
- case catch supervisor:start_child(?RT_SUP, ChildSpec) of
- {ok, Pid} when is_pid(Pid) ->
- {node_info, _Node, Pid, VSN, State, Status, _Tag} =
- get_node_info(Pid),
- {node_info, Node, Pid, VSN, State, Status, new};
- {error, already_present} ->
- supervisor:delete_child(?RT_SUP, ?MODULE),
- start(Node, Options, Tag, Condition);
- {error, {already_started, Pid}} ->
- try_to_adopt(Pid, Tag, Condition);
- {error,Reason} ->
- {error,Reason};
- {'EXIT',Reason} ->
- {error,Reason}
- end;
-start(Node, Options, Tag, Condition) ->
- case rt_version(Node) of
- {error,Error} ->
- {error,Error};
- _VSN ->
- ChildSpec = {?MODULE, {?MODULE, start_link_man,
- [self(), Options, Tag]},
- temporary, 5000, worker, [?MODULE]},
- case catch rpc:call(Node, supervisor, start_child,
- [?RT_SUP, ChildSpec]) of
- {ok, Pid} when is_pid(Pid) ->
- {node_info, _Node, Pid,
- VSN, State, Status, _Tag} = get_node_info(Pid),
- {node_info, Node, Pid, VSN, State, Status, new};
- {error, already_present} ->
- rpc:call(Node, supervisor, delete_child,
- [?RT_SUP, ?MODULE]),
- start(Node, Options, Tag, Condition);
- {error, {already_started, Pid}} ->
- try_to_adopt(Pid, Tag, Condition);
- {error,Reason} -> % Could not start child.
- {error,Reason};
- {badrpc,nodedown} ->
- {error,nodedown};
- {badrpc,Reason} ->
- {error,{badrpc,Reason}};
- {'EXIT',Reason} ->
- {error,Reason}
- end
- end.
-
-rt_version(Node) ->
- case catch rpc:call(Node,application,loaded_applications,[]) of
- List when is_list(List) ->
- case lists:keysearch(runtime_tools,1,List) of
- {value,{_,_,VSN}} ->
- VSN;
- false ->
- {error,not_loaded}
- end;
- {badrpc,nodedown} ->
- {error,nodedown};
- {'EXIT',Reason} ->
- {error,Reason}
- end.
-%% ------------------------------------------------------------------------------
-
-%% stop(Node)=ok|{error,Reason}
-%% Stops the runtim component on node Node. Note that this is mearly calling the
-%% supervisor API to shutdown the inviso_rt child belonging to the runtime_tools_sup.
-stop(Node) when Node==node() ->
- supervisor:terminate_child(?RT_SUP,?MODULE),
- supervisor:delete_child(?RT_SUP,?MODULE),
- ok;
-stop(Node) ->
- case catch rpc:call(Node,supervisor,terminate_child,[?RT_SUP,?MODULE]) of
- ok ->
- stop_delete_child(Node);
- {error,_} -> % No child running.
- stop_delete_child(Node); % Make sure we remove it also.
- {badrpc,Reason} ->
- {error,{badrpc,Reason}};
- {'EXIT',Reason} ->
- {error,Reason}
- end.
-
-stop_delete_child(Node) ->
- case catch rpc:call(Node,supervisor,delete_child,[?RT_SUP,?MODULE]) of
- ok ->
- ok;
- {error,_} -> % No child running.
- ok;
- {badrpc,Reason} ->
- {error,{badrpc,Reason}};
- {'EXIT',Reason} ->
- {error,Reason}
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% API for the control component.
-%% ==============================================================================
-
-%% init_tracing(TracerData) ->
-%% TracerData = LogTD | [{trace,LogTD},{ti,TiTD}]
-%% LogTD = {HandlerFun, Data} | collector |
-%% {relayer, pid()} | {ip, IPPortParameters} |
-%% {file, FilePortParameters}
-%% TiTD = {file,FileName} | {file,FileName,{InitPublLD,RemovePublLD,CleanPublLD}}
-%% | {relay,Node} | {relay,Node,{InitPublLD,RemovePublLD,CleanPublLD}}
-%% HandlerFun=fun(TraceMsg,Data)->NewData
-%% IPPortParameters = Portno | {Portno, Qsiz}
-%% Qsiz =
-%% FilePortParameters = {Filename, wrap, Tail, {time, WrapTime}, WrapCnt} |
-%% {FileName, wrap, Tail, WrapSize, WrapCnt} |
-%% {FileName, wrap, Tail, WrapSize} |
-%% {FileName, wrap, Tail} | FileName
-%% Defines a tracer:
-%% {HandlerFun, Data} - will be used as handler inside the runtime component for
-%% every incomming trace message.
-%% relayer - the runtime component will relay all comming trace messages to
-%% the runtime component Pid.
-%% collector - the runtime component is used as tracer or collector of relayed
-%% trace messages using the default handler writing them to io.
-%% ip | file - will start a tracer port using PortParameters
-init_tracing(Pid,TracerData) ->
- call(Pid,{init_tracing,TracerData}).
-%% ------------------------------------------------------------------------------
-
-%% stop_tracing(RTpids)=[{Node,NodeResult},...]
-%% RTpids=[RTinfo,...]
-%% RTinfo={RTpid,Node} | {{error,Reason},Node}
-%% NodeResult={ok,State} | {error,Reason}
-%% Sends a request to stop tracing to all nodes in RTpids, in parallel. Stop
-%% tracing means that all trace flags are removed and the nodes go to idle
-%% state.
-stop_tracing_parallel(RTpids) ->
- call_parallel(lists:map(fun({Pid,Node})->{Pid,Node,stop_tracing};
- (Error)->Error
- end,
- RTpids)).
-%% ------------------------------------------------------------------------------
-
-%% try_to_adopt(Pid,NewTag,Condition)=
-%% {node_info,node(),self(),VSN,State,Status,{tag,PreviousTag}}|{error,Reason}
-%% NewTag=term(), the identification tag we want the runtime component to use
-%% from now on if adoption was successful.
-%% Condition='if_ref', only adopt if current tag is NewTag.
-%% PreviousTag= the tag the runtime component had before it accepted the
-%% adoption.
-%% This function shall only be used by a control component wishing to adopt this
-%% runtime component.
-try_to_adopt(Pid, Tag, Condition) ->
- call(Pid,{try_to_adopt,Tag,Condition}).
-%% ------------------------------------------------------------------------------
-
-%% confirm_connection(Pid,Tag)= {node_info,node(),self(),VSN,State,Status,Tag}|
-%% {error,refused}.
-%% Must only be used by a control component having been contacted by the runtime
-%% component Pid. It confirms to the runtime component that the control component
-%% has accepted the connect request.
-confirm_connection(Pid,Tag) ->
- call(Pid,{confirm_connection,Tag}).
-%% ------------------------------------------------------------------------------
-
-%% get_node_info(Pid)={node_info,Node,Pid,VSN,State,Status,Tag}.
-get_node_info(Pid) ->
- call(Pid,get_node_info).
-%% ------------------------------------------------------------------------------
-
-%% suspend(NodeOrPid,Reason)=ok
-%% call_suspend(NodeOrPid,Reason)=ok
-%% Makes the runtime component and all of its helpers suspend. suspend/2 is
-%% assynchronous.
-suspend(NodeOrPid,Reason) ->
- cast(NodeOrPid,{suspend,Reason}).
-
-call_suspend(NodeOrPid,Reason) ->
- call(NodeOrPid,{suspend,Reason}).
-%% ------------------------------------------------------------------------------
-
-%% cancel_suspension(Pid)=ok
-%% Function moving the runtime component to status running. Regardless of its
-%% current status.
-cancel_suspension(Pid) ->
- call(Pid,cancel_suspension).
-%% ------------------------------------------------------------------------------
-
-%% change_options(Pid,Options)=ok
-%% Options=list(); see the start_link_XXX functions.
-%% Changes options according to Options list.
-%% Changing the control component we shall be depending on has no effect. The
-%% dependency value in self can however be changed, and takes effect immediately.
-change_options(Pid,Options) ->
- call(Pid,{change_options,Options}).
-%% ------------------------------------------------------------------------------
-
-%% clear_all_tp(Pid)=ok
-%% Function removing all, both local and global trace-patterns from the node.
-clear_all_tp(Pid) ->
- call(Pid,clear_all_tp).
-%% ------------------------------------------------------------------------------
-
-%% clear(Pid,Options)={ok,{new,Status}}
-%% Options=[Opt,...]
-%% Opt=keep_trace_patterns | keep_log_files
-%% Resets the runtime component to state 'new' by stopping all ongoing tracing,
-%% closing and removing all associated logfiles. The Options can be used to
-%% prevent the runtime component from being totally erased.
-clear(Pid,Options) ->
- call(Pid,{clear,Options}).
-%% ------------------------------------------------------------------------------
-
-%% flush(Pid)=ok | {error,Reason}
-%% Sends the flush command to the trace-port, if we are using a trace-port and
-%% are tracing.
-flush(Pid) ->
- call(Pid,flush).
-%% ------------------------------------------------------------------------------
-
-%% trace_patterns_parallel(RTpids,Args,Flags)=[{Node,Answer},...]
-%% RTpids=[{RTpid,Node},...] or [{Error,Node},...]
-%% Args=[Arg,...]
-%% Arg={Mod,Func,Arity,MS}|{Mod,Func,Arity,MS,Opts}
-%% Mod=atom()|reg_exp()|{Dir,reg_exp()}
-%% Dir=reg_exp()
-%% Answer=[Answer,...]
-%% Answer=int()|{error,Reason}
-%% API function for the control component sending trace-patterns to a list of
-%% runtime components. Returns a [{Node,Answer},...] list in the same order.
-trace_patterns_parallel(RTpids,Args,Flags) -> % Same args and flags for all.
- call_parallel(lists:map(fun({Pid,Node})when is_pid(Pid)->{Pid,Node,{tp,Args,Flags}};
- (Error)-> Error
- end,
- RTpids)).
-%% ------------------------------------------------------------------------------
-
-%% trace_flags_parallel(RTpids,Args,How)=
-%% trace_flags_parallel(RTpidsArgs,How)=
-%% trace_flags_parallel(RTpidsArgsHow)=[{Node,Reply},...]
-%% RTpids=[RTpidEntry,...]
-%% RTpidEntry={RTpid,Node}|{Error,Node}
-%% Error=term(), any term you wish to have as reply in Answer assoc. to Node.
-%% Args=[{Process,Flags},...]
-%% Process=pid()|registeredname()|'all'|'new'|'existing'
-%% Flags=List of the allowed process trace flags.
-%% RTpidsArgs=[RTpidArgEntry,...]
-%% RTpidArgEntry={RTpid,Node,Args}|{Error,Node}
-%% RTpidsArgsHow=[RTpidArgsHowEntry,...]
-%% RTpidArgsHowEntry={RTpid,Node,Args,How}|{Error,Node}
-%% How=true|false
-%% Reply={ok,Answers}
-%% Answers=[Answer,...], one for each Args and in the same order.
-%% Answer=int()|{error,Reason}
-%% API function used by the control component to send flags to a list of runtime
-%% components. Returns a list of [{Node,Answer},... ] in the same order.
-trace_flags_parallel(RTpids,Args,How) -> % Same args for every node!
- call_parallel(lists:map(fun({Pid,Node})when is_pid(Pid)->{Pid,Node,{tf,Args,How}};
- (Error)-> Error
- end,
- RTpids)).
-
-trace_flags_parallel(RTpidArgs,How) -> % Different args but same how.
- call_parallel(lists:map(fun({Pid,Node,Args})when is_pid(Pid)->
- {Pid,Node,{tf,Args,How}};
- (Error)->
- Error
- end,
- RTpidArgs)).
-
-trace_flags_parallel(RTpidArgsHow) -> % Both different args and hows.
- call_parallel(lists:map(fun({Pid,Node,Args,How})when is_pid(Pid)->
- {Pid,Node,{tf,Args,How}};
- (Error)->
- Error
- end,
- RTpidArgsHow)).
-%% ------------------------------------------------------------------------------
-
-%% meta_pattern(RTpids,Args)=[{Node,Answer},...]
-%% RTpids=[{RTpid,Node},...] or [{Error,Node},...]
-%% Args={FunctionName,ArgList}
-%% FunctionName=atom()
-%% ArgList=list(), list of the arguments to FunctionName.
-%% Answer=[Answer,...]
-%% Answer=int()|{error,Reason}
-%% Makes a call to the meta-tracer through its runtime component. Returns a list
-%% a answers in the same order as RTpids. Note that if "someone" has discovered
-%% that there is an error with a particular node, the error answer can be placed
-%% in the RTpids list from the start.
-meta_tracer_call_parallel(RTpids,Args) -> % Same args for all nodes.
- call_parallel(lists:map(fun({Pid,Node})when is_pid(Pid)->
- {Pid,Node,{meta_tracer_call,Args}};
- (Error)->
- Error
- end,
- RTpids)).
-%% ------------------------------------------------------------------------------
-
-%% get_status(Pid)={ok,{State,Status}}
-%% State=new|tracing|idle
-%% Status=running|{suspended,Reason}
-get_status(Pid) ->
- call(Pid,get_status).
-%% ------------------------------------------------------------------------------
-
-%% get_tracerdata(Pid)={ok,TracerData} | {ok,no_tracerdata} | {error,Reason}
-%% TracerData=see init_tracing
-%% Fetches the current tracerdata from the runtime component.
-get_tracerdata(Pid) ->
- call(Pid,get_tracerdata).
-%% ------------------------------------------------------------------------------
-
-%% list_log(Pid)={ok,no_log}|{ok,LogCollection}|{error,Reason}
-%% list_log(Pid,TracerData)=
-%% LogCollection=[LogTypes,...]
-%% LogTypes={trace_log,Dir,Files}|{ti_log,Dir,Files}
-%% Dir=string()
-%% Files=[FileNameWithoutDir,...]
-%% Lists all files associated with the current tracerdata. Or finds out which
-%% files there are stored in this node given a tracerdata.
-list_logs(Pid) ->
- call(Pid,list_logs).
-list_logs(Pid,TD) ->
- call(Pid,{list_logs,TD}).
-%% ------------------------------------------------------------------------------
-
-%% fetch_log(Pid,CollectPid)={ok,FetcherPid}|{complete,no_log}|{error,Reason}
-%% fetch_log(Pid,CollectPid,Spec)=
-%% CollectPid=pid(), the process which will be given the transfered logs.
-%% Spec=TracerData|LogCollection
-%% Transferes a number of files using ditributed Erlang to CollectPid. This
-%% function is supposed to be used internally by a control component. It returns
-%% when the transfer is initiated and does not mean it is done or successful.
-fetch_log(Pid,CollectPid) ->
- call(Pid,{fetch_log,CollectPid}).
-fetch_log(Pid,CollectPid,Spec) ->
- call(Pid,{fetch_log,CollectPid,Spec}).
-%% ------------------------------------------------------------------------------
-
-%% delete_log(Pid,TracerDataOrLogList)={ok,Results}|{error,Reason}
-%% TracerDataOrLogList=[FileNameWithPath,...]|LogCollection|TracerData
-%% Results=[LogType,...]
-%% LogType={trace_log,FileSpecs}|{ti_log,FilesSpecs}
-%% FilesSpecs=[FileSpec,...]
-%% FileSpec={ok,FileName}|{error,{Posix,FileName}}
-%% Filename=string(), the filename without dir-path.
-delete_log(Pid) ->
- call(Pid,delete_logs).
-delete_log(Pid,TracerDataOrLogList) ->
- call(Pid,{delete_logs,TracerDataOrLogList}).
-%% ------------------------------------------------------------------------------
-
-%% state(NodeOrPid)=LoopData
-%% Returns the loopdata of the runtime component. Only meant for debugging.
-state(NodeOrPid) ->
- call(NodeOrPid,state).
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% API for local calls made from the same node. E.g autostart.
-%% ==============================================================================
-
-%% init_tracing(TracerData)=
-%% See init_tracing/2.
-init_tracing(TracerData) ->
- call_regname(?MODULE,{init_tracing,TracerData}).
-%% ------------------------------------------------------------------------------
-
-
-%% Meaning that these function does most often not have to be called by a
-%% control component because there are more efficient ones above.
-
-%% tp(Module,Function,Arity,MatchSpec) ->
-%% tp(Module,Function,Arity,MatchSpec,Opts) ->
-%% tp(PatternList) ->
-%% Module = '_'|atom()|ModRegExp|{DirRegExp,ModRegExp}
-%% Function == atom() | '_'
-%% Arity = integer() | '_'
-%% MatchSpec = true | false | [] | matchspec() see ERTS User's guide for a
-%% description of match specifications.
-%% Opts=list(); 'only_loaded'
-%% PatternList = [Pattern],
-%% Pattern = {Module,Function,Arity,MatchSpec,Opts},
-%% Set trace pattern (global).
-tp(Module,Function,Arity,MatchSpec) ->
- tp(Module,Function,Arity,MatchSpec,[]).
-tp(Module,Function,Arity,MatchSpec,Opts) ->
- call_regname(?MODULE,{tp,[{Module,Function,Arity,MatchSpec,Opts}],[global]}).
-tp(PatternList) ->
- call_regname(?MODULE,{tp,PatternList,[global]}).
-%% ------------------------------------------------------------------------------
-
-tpg(Mod,Func,Arity,MatchSpec) ->
- tp(Mod,Func,Arity,MatchSpec).
-tpg(Mod,Func,Arity,MatchSpec,Opts) ->
- tp(Mod,Func,Arity,MatchSpec,Opts).
-tpg(PatternList) ->
- tp(PatternList).
-%% ------------------------------------------------------------------------------
-
-%% tpl(Module,Function,Arity,MatchSpec) ->
-%% tpl(Module,Function,Arity,MatchSpec,Opts) ->
-%% tpl(PatternList) ->
-%% Module = Function == atom() | '_' | RegExpMod | {RegExpDir,RegExpMod}
-%% Arity = integer() | '_'
-%% MatchSpec = true | false | [] | matchspec() see ERTS User's guide for a
-%% Opts=list(); 'only_loaded'
-%% description of match specifications.
-%% PatternList = [Pattern],
-%% Pattern = {Module, Function, Arity, MatchSpec},
-%% Set trace pattern (local).
-tpl(Module,Function,Arity,MatchSpec) ->
- call_regname(?MODULE,{tp,[{Module,Function,Arity,MatchSpec,[]}],[local]}).
-tpl(Module,Function,Arity,MatchSpec,Opts) ->
- call_regname(?MODULE,{tp,[{Module,Function,Arity,MatchSpec,Opts}],[local]}).
-tpl(PatternList) ->
- call_regname(?MODULE,{tp,PatternList,[local]}).
-%% ------------------------------------------------------------------------------
-
-%% ctp(Module,Function,Arity) ->
-%% ctp(PatternList)=
-%% Module = atom()|'_'|RegExpMod|{RegExpDir,RegExpMod}
-%% Function == atom() | '_'
-%% Arity = integer() | '_'
-%% PatternList=[{Mod,Func,Arity},...]
-%% Clear trace pattern (global).
-%% Note that it is possible to clear patterns using regexps. But we can for
-%% natural reasons only clear patterns for loaded modules. Further more there
-%% seems to be a fault in the emulator (<=R10B) crashing if we remove patterns
-%% for deleted modules. Therefore we use the only_loaded option.
-ctp(Module,Function,Arity) ->
- call_regname(?MODULE,{tp,[{Module,Function,Arity,false,[only_loaded]}],[global]}).
-ctp(PatternList) ->
- call_regname(?MODULE,
- {tp,
- lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
- [global]}).
-%% ------------------------------------------------------------------------------
-
-ctpg(Mod,Func,Arity) ->
- ctp(Mod,Func,Arity).
-ctpg(PatternList) ->
- ctp(PatternList).
-%% ------------------------------------------------------------------------------
-
-%% ctpl(Module,Function,Arity) ->
-%% Module = atom()|'_'|RegExpMod|{RegExpDir,RegExpMod}
-%% Function == atom() | '_'
-%% Arity = integer() | '_'
-%% PatternList=[{Mod,Func,Arity},...]
-%% Clear trace pattern (local).
-ctpl(Module,Function,Arity) ->
- call_regname(?MODULE,{tp,[{Module,Function,Arity,false,[only_loaded]}],[local]}).
-ctpl(PatternList) ->
- call_regname(?MODULE,
- {tp,
- lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
- [local]}).
-%% ------------------------------------------------------------------------------
-
-init_tpm(Mod,Func,Arity,CallFunc) ->
- call_regname(?MODULE,{meta_tracer_call,{init_tpm,[Mod,Func,Arity,CallFunc]}}).
-
-init_tpm(Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- call_regname(?MODULE,
- {meta_tracer_call,
- {init_tpm,
- [Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc]}}).
-%% ------------------------------------------------------------------------------
-
-tpm(Mod,Func,Arity,MS) ->
- call_regname(?MODULE,{meta_tracer_call,{tpm,[Mod,Func,Arity,MS]}}).
-tpm(Mod,Func,Arity,MS,CallFunc) ->
- call_regname(?MODULE,{meta_tracer_call,{tpm,[Mod,Func,Arity,MS,CallFunc]}}).
-tpm(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- call_regname(?MODULE,
- {meta_tracer_call,
- {tpm,
- [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc]}}).
-%% ------------------------------------------------------------------------------
-
-tpm_tracer(Mod,Func,Arity,MS) ->
- call_regname(?MODULE,{meta_tracer_call,{tpm_tracer,[Mod,Func,Arity,MS]}}).
-tpm_tracer(Mod,Func,Arity,MS,CallFunc) ->
- call_regname(?MODULE,{meta_tracer_call,{tpm_tracer,[Mod,Func,Arity,MS,CallFunc]}}).
-tpm_tracer(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- call_regname(?MODULE,
- {meta_tracer_call,
- {tpm_tracer,
- [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc]}}).
-%% ------------------------------------------------------------------------------
-
-tpm_ms(Mod,Func,Arity,MSname,MS) ->
- call_regname(?MODULE,{meta_tracer_call,{tpm_ms,[Mod,Func,Arity,MSname,MS]}}).
-%% ------------------------------------------------------------------------------
-
-tpm_ms_tracer(Mod,Func,Arity,MSname,MS) ->
- call_regname(?MODULE,{meta_tracer_call,{tpm_ms_tracer,[Mod,Func,Arity,MSname,MS]}}).
-%% ------------------------------------------------------------------------------
-
-ctpm_ms(Mod,Func,Arity,MSname) ->
- call_regname(?MODULE,{meta_tracer_call,{ctpm_ms,[Mod,Func,Arity,MSname]}}).
-%% ------------------------------------------------------------------------------
-
-local_register() ->
- call_regname(?MODULE,{meta_tracer_call,{local_register,[]}}).
-%% ------------------------------------------------------------------------------
-
-global_register() ->
- call_regname(?MODULE,{meta_tracer_call,{global_register,[]}}).
-%% ------------------------------------------------------------------------------
-
-ctpm(Mod,Func,Arity) ->
- call_regname(?MODULE,{meta_tracer_call,{ctpm,[Mod,Func,Arity]}}).
-%% ------------------------------------------------------------------------------
-
-remove_local_register() ->
- call_regname(?MODULE,{meta_tracer_call,{remove_local_register,[]}}).
-%% ------------------------------------------------------------------------------
-
-remove_global_register() ->
- call_regname(?MODULE,{meta_tracer_call,{remove_global_register,[]}}).
-%% ------------------------------------------------------------------------------
-
-%% tf(PidSpec, FlagList) ->
-%% tf(TraceConfList) ->
-%% TraceConfList = [{PidSpec, FlagList}],
-%% FlagList = [Flags],
-%% PidSpec = all | new | existing | pid() | registeredname()
-%% Flags = all | send | 'receive' | procs | call | silent | return_to |
-%% running | garbage_collection | timestamp | cpu_timestamp | arity |
-%% set_on_spawn | set_on_first_spawn | set_on_link | set_on_first_link
-%% Set trace flags.
-tf(PidSpec, FlagList) ->
- call_regname(?MODULE,{tf,[{PidSpec,FlagList}],true}).
-
-tf(TraceConfList) ->
- call_regname(?MODULE,{tf,TraceConfList,true}).
-%% ------------------------------------------------------------------------------
-
-%% ctf(PidSpec, FlagList) ->
-%% ctf(TraceConfList) ->
-%% TraceConfList = [{PidSpec, FlagList}],
-%% FlagList = [Flags],
-%% PidSpec = all | new | existing | pid() | registeredname()
-%% Flags = all | send | 'receive' | procs | call | silent | return_to |
-%% running | garbage_collection | timestamp | cpu_timestamp | arity |
-%% set_on_spawn | set_on_first_spawn | set_on_link | set_on_first_link
-%% Clear trace flags.
-ctf(PidSpec, FlagList) ->
- call_regname(?MODULE,{tf,[{PidSpec,FlagList}],false}).
-
-ctf(TraceConfList) ->
- call_regname(?MODULE,{tf_as,TraceConfList,false}).
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Client side functions.
-%% ------------------------------------------------------------------------------
-
-%% Call function managing the client to server communication. This function may
-%% be run by a client on a different node.
-%% Note that we must use two different functions for calling a named process and
-%% calling the runtime component at a specified node.
-call(Pid,Request) when is_pid(Pid) ->
- call_2(Pid,Request);
-call(Node,Request) when Node==node() -> % To our node!
- call_2(?MODULE,Request);
-call(Node,Request) when is_atom(Node) ->
- call_2({?MODULE,Node},Request);
-call(To,_Request) ->
- {error,{badarg,To}}.
-
-call_regname(Name,Request) when is_atom(Name) -> % To a registered name.
- call_2(Name,Request).
-
-call_2(To,Request) ->
- MRef=erlang:monitor(process,To), % Use a monitor to avoid waiting for ever.
- Ref=make_ref(),
- case catch To ! {Request,self(),Ref} of % Can be a remote pid.
- {'EXIT',_} -> % If we use registered name.
- erlang:demonitor(MRef), % Maybe not necessary!?
- receive
- {'DOWN',MRef,_Type,_Obj,_Info} ->
- true
- after
- 0 ->
- true
- end,
- {error,not_started};
- _ -> % At least no obvious error.
- receive
- {Msg,Ref} ->
- erlang:demonitor(MRef),
- Msg;
- {'DOWN',MRef,_Type,_Obj,Info} -> % The runtime component disapeared.
- {error,{no_response,Info}}
- end
- end.
-%% -----------------------------------------------------------------------------
-
-%% Multicall function taking a list of [{Pid,Node,Request},...] and sends
-%% a request to every Pid. This function then also allows you to send multiple
-%% requests to the same Pid since it will sit and wait for all replies.
-%% Note that RTspec may also be an [{{error,Reason},Node},...]. That tuple will
-%% then be used as reply in the reply list.
-%% Returns [{Node,Reply},...] for every element in RTspec, in the same order.
-call_parallel(RTspec) ->
- Ref=make_ref(),
- {Nr,Pending}=call_parallel_2(RTspec,Ref,0,[]),
- Replies=call_parallel_3(Ref,Pending,Nr,[],[]),
- call_parallel_build_reply(RTspec,1,Replies).
-
-call_parallel_2([{Pid,Node,Request}|Rest],Ref,Nr,Pending) when is_pid(Pid) ->
- Pid ! {Request,self(),{Ref,Nr+1}},
- MRef=erlang:monitor(process,Pid), % So we won't wait for ever for it.
- call_parallel_2(Rest,Ref,Nr+1,[{Nr+1,Node,MRef}|Pending]);
-call_parallel_2([{{error,_Reason},_Node}|Rest],Ref,Nr,Pending) ->
- call_parallel_2(Rest,Ref,Nr,Pending); % Just skip it. This is no process.
-call_parallel_2([_Faulty|Rest],Ref,Nr,Pending) -> % Should not happend.
- call_parallel_2(Rest,Ref,Nr,Pending); % But we choose to skip it instead of crash.
-call_parallel_2([],_,Nr,Pending) ->
- {Nr,Pending}.
-
-%% Help function collecting reply-messages sent from the runtime components. We
-%% count down until we got a reply for every pending request. Or if we get a DOWN
-%% message indicating that the runtime component is no longer present. Note that
-%% we can by accident read away DOWN messages not belonging to this procedure.
-%% They are collected to be reissued after we are done.
-call_parallel_3(_Ref,_Pending,0,Replies,DownMsgs) -> % All expected received.
- lists:foreach(fun({MRef,Pid,Info}) -> self() ! {'DOWN',MRef,process,Pid,Info} end,
- DownMsgs), % Reissue the down messages!
- Replies;
-call_parallel_3(Ref,Pending,NrOfPending,Replies,DownMsgs) ->
- receive
- {Reply,{Ref,Nr}} ->
- case lists:keysearch(Nr,1,Pending) of
- {value,{_Nr,Node,MRef}} ->
- erlang:demonitor(MRef),
- call_parallel_3(Ref,Pending,NrOfPending-1,
- [{Nr,Node,Reply}|Replies],DownMsgs);
- false -> % Really strange!
- call_parallel_3(Ref,Pending,NrOfPending,Replies,DownMsgs)
- end;
- {'DOWN',MRef,process,Pid,Info} -> % Probably process we monitor terminated.
- case lists:keysearch(MRef,3,Pending) of
- {value,{Nr,Node,_}} -> % Yes it was one of our processes.
- call_parallel_3(Ref,Pending,NrOfPending-1,
- [{Nr,Node,{error,no_reponse}}|Replies],DownMsgs);
- false -> % We picked up a DOWN msg by misstake.
- call_parallel_3(Ref,Pending,NrOfPending,Replies,
- [{MRef,Pid,Info}|DownMsgs])
- end
- end.
-
-%% Help function which build up the [{Node,Reply},...] list in the same order as RTspec.
-call_parallel_build_reply([],_,_) ->
- [];
-call_parallel_build_reply([{Pid,Node,_Request}|Rest],Nr,Replies) when is_pid(Pid) ->
- {value,{_Nr,_Node,Reply}}=lists:keysearch(Nr,1,Replies),
- [{Node,Reply}|call_parallel_build_reply(Rest,Nr+1,Replies)];
-call_parallel_build_reply([{{error,Reason},Node}|Rest],Nr,Replies) ->
- [{Node,{error,Reason}}|call_parallel_build_reply(Rest,Nr,Replies)];
-call_parallel_build_reply([_Faulty|Rest],Nr,Replies) ->
- call_parallel_build_reply(Rest,Nr,Replies).
-%% ------------------------------------------------------------------------------
-
-cast(Pid,Request) when is_pid(Pid) ->
- cast2(Pid,Request);
-cast(Node,Request) when Node==node() ->
- catch cast2(?MODULE,Request),
- ok;
-cast(Node,Request) when is_atom(Node) ->
- catch cast2({?MODULE,Node},Request),
- ok;
-cast(BadAddress,_Request) ->
- {error,{badarg,BadAddress}}.
-
-cast2(To,Request) ->
- To ! {Request,void,void}. % Mimics the call protocol.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Implementation of the runtime component (server side).
-%% ==============================================================================
-
-%% Since the runtime component is not implemented using gen_sever we are "free"
-%% to use what ever functionnames we like.
-
-%% Initial function on which the runtime component is spawned on if started by
-%% a controlcomponent.
-init(Ctrl, Options, Tag, Parent) when is_list(Options) ->
- %% started from controller
- process_flag(trap_exit,true),
- register(?MODULE,self()), % Will crash if rt is already running
- do_clear_trace_patterns(), % Remove potential old patterns left.
- LD1=read_option_list(Options,
- #rt{state=new,
- parent=Parent,
- ctrl=Ctrl,
- vsn=get_application_vsn(),
- tag=Tag}),
- OverloadData=initialize_overload(LD1),
- CtrlRef=erlang:monitor(process,Ctrl), % Monitor our control component.
- loop1(LD1#rt{ctrl_ref=CtrlRef,overload_data=OverloadData}).
-%% ----------------------------------------------------------------------------
-
-%% Initial function on which the runtime component is spawned on if started
-%% by the runtime_tools supervisor. It is here it is determined if we shall
-%% autostart.
-auto_init(AutoModArgs,Parent) ->
- %% autostart
- process_flag(trap_exit, true),
- register(?MODULE, self()), % Will crash if a rt is already running
- AutoMod=get_autostart_module(), % Determine which module to use!
- case catch AutoMod:autostart(AutoModArgs) of
- {MFA,Options,Tag} ->
- do_clear_trace_patterns(), % Remove previously left patterns.
- LD1=read_option_list(Options,#rt{state=new,
- parent=Parent,
- vsn=get_application_vsn(),
- tag=Tag}),
- case auto_init_connect_control(LD1) of
- {ok,LD2} -> % Either connected or running_alone.
- OverloadData=initialize_overload(LD2),
- case auto_init_check_mfa(MFA) of
- {ok,{M,F,A}} -> % We shall start somekind of tracing!
- P=spawn_link(M,F,A), % It lives its own life, only link!
- loop1(LD2#rt{auto_starter=P,overload_data=OverloadData});
- false ->
- loop1(LD2#rt{overload_data=OverloadData})
- end;
- stop -> % Not allowed to run alone!
- true % Simply terminate.
- end;
- _ -> % Non existent or faulty autostart mod!
- true % Terminate normally.
- end.
-
-auto_init_connect_control(LD1) ->
- case auto_init_connect_find_pid(LD1#rt.dependency) of
- Pid when is_pid(Pid) -> % There is a control component.
- CtrlRef=erlang:monitor(process,Pid),
- Pid ! {connect,node(),self(),LD1#rt.vsn,LD1#rt.tag},
- {ok,LD1#rt{ctrl_ref=CtrlRef,ctrl=Pid}};
- _ -> % There is no control component.
- do_down_message(LD1) % Will return 'stop' or a LoopData.
- end.
-
-%% Help function which finds the pid of the control component.
-auto_init_connect_find_pid({_TimeOut,Node}) when Node==node() ->
- whereis(?CTRL);
-auto_init_connect_find_pid({_TimeOut,Node}) when is_atom(Node) ->
- rpc:call(Node,erlang,whereis,[?CTRL]);
-auto_init_connect_find_pid(_) -> % Node is not a proper node.
- undefined. % Act as could not find control comp.
-
-%% Help function checking that the parameter is reasonable to be used as
-%% spawn_link argument.
-auto_init_check_mfa({M,F,A}) when is_atom(M),is_atom(F),is_list(A) ->
- {ok,{M,F,A}};
-auto_init_check_mfa(_) ->
- false.
-
-%% Help function to init_auto which finds out which module to call for
-%% guidance on how to proceed. Returns an atom.
-get_autostart_module() ->
- case application:get_env(inviso_autostart_mod) of
- {ok,Mod} when is_atom(Mod) ->
- Mod;
- _ ->
- inviso_autostart % The default autostart module.
- end.
-%% ----------------------------------------------------------------------------
-
-
-%% This is the preloop function which performs loadcheck if necessary. Note
-%% that it calculates the timeout used in the after in the real loop. There is
-%% further no use doing overload checks if we are not tracing or already
-%% suspended. There is yet one more situation, we do not want to perform
-%% overload checks if the interval is set to infinity. This can be the case if
-%% we are using an external source pushing overload information instead.
-loop1(LD=#rt{overload=Overload}) ->
- if
- Overload/=?NO_LOADCHECK,element(2,Overload)/=infinity ->
- Now=now(),
- if
- LD#rt.status==running,
- LD#rt.state==tracing,
- Now>LD#rt.next_loadcheck -> % Do loadcheck only then!
- {NewLD,TimeOut}=do_check_overload(LD,{timeout,LD#rt.overload_data}),
- loop(NewLD,TimeOut);
- LD#rt.status==running,LD#rt.state==tracing ->
- Timeout=calc_diff_to_now(Now,LD#rt.next_loadcheck),
- loop(LD,Timeout);
- true -> % Do not spend CPU on this! :-)
- loop(LD,infinity)
- end;
- true -> % Either no check or infinity.
- loop(LD,infinity)
- end.
-
-loop(LoopData,Timeout) ->
- receive
- Msg when element(1,Msg)==trace_ts;
- element(1,Msg)==trace;
- element(1,Msg)==drop;
- element(1,Msg)==seq_trace ->
- case LoopData#rt.handler of
- {HandlerFun,Data} ->
- NewData=HandlerFun(Msg,Data),
- loop1(LoopData#rt{handler={HandlerFun,NewData}});
- _ ->
- loop1(LoopData)
- end;
- {{tp,Args,Flags},From,Ref} ->
- if
- LoopData#rt.status==running -> % Not when suspended.
- Reply=do_set_trace_patterns(Args,Flags),
- if
- LoopData#rt.state==new -> % No longer new when tp set.
- reply_and_loop({ok,Reply},From,Ref,LoopData#rt{state=idle});
- true ->
- reply_and_loop({ok,Reply},From,Ref,LoopData)
- end;
- true -> % We are suspended!
- reply_and_loop({error,suspended},From,Ref,LoopData)
- end;
- {{tf,Args,How},From,MRef} ->
- Reply=
- case How of
- true ->
- if
- LoopData#rt.status==running ->
- case {LoopData#rt.tracer_port,LoopData#rt.handler} of
- {Port,_} when is_port(Port) ->
- do_set_trace_flags(Port,Args,How);
- {_,{Handler,_D}} when is_function(Handler) ->
- do_set_trace_flags(self(),Args,How);
- _ ->
- {error,no_tracer}
- end;
- true -> % Can't turn *on* flags if suspended.
- {error, suspended}
- end;
- false -> % No tracer needed when turning off.
- do_set_trace_flags(void,Args,How)
- end,
- reply_and_loop(Reply,From,MRef,LoopData);
- {{meta_tracer_call,Args},From,MRef} ->
- if
- LoopData#rt.status==running ->
- case LoopData#rt.meta_tracer of
- MPid when is_pid(MPid) ->
- Reply=do_meta_pattern(MPid,Args),
- reply_and_loop(Reply,From,MRef,LoopData);
- _ ->
- reply_and_loop({error,no_metatracer},From,MRef,LoopData)
- end;
- true ->
- reply_and_loop({error,suspended},From,MRef,LoopData)
- end;
- {clear_all_tp,From,MRef} ->
- do_clear_trace_patterns(),
- reply_and_loop(ok,From,MRef,LoopData);
- {{init_tracing,TracerData},From,MRef} ->
- {NewLoopData,Reply}=
- if
- LoopData#rt.status==running ->
- if
- LoopData#rt.state==tracing ->
- {LoopData,{error,already_initiated}};
- true -> % Otherwise, try to init-tracing!
- case translate_td(TracerData) of
- {ok,LogTD,MetaTD} ->
- do_init_tracing(LoopData,TracerData,LogTD,MetaTD);
- Error ->
- {LoopData,Error}
- end
- end;
- true -> % Can't init tracing if not running.
- {LoopData,{error,suspended}}
- end,
- reply_and_loop(Reply,From,MRef,NewLoopData);
- {stop_tracing,From,MRef} ->
- case LoopData#rt.state of
- tracing -> % Only case we need to do anything.
- reply_and_loop({ok,idle},From,MRef,do_stop_tracing(LoopData));
- idle -> % Already idle!
- reply_and_loop({ok,idle},From,MRef,LoopData);
- new -> % Have actually never traced!
- reply_and_loop({ok,new},From,MRef,LoopData)
- end;
- {{suspend,Reason},From,MRef} ->
- if
- LoopData#rt.status==running ->
- NewLD=do_suspend(LoopData,Reason),
- reply_and_loop(ok,From,MRef,NewLD);
- true -> % No need suspend if not running!
- reply_and_loop(ok,From,MRef,LoopData)
- end;
- {cancel_suspension,From,MRef} ->
- NewLoopData=LoopData#rt{status=running,next_loadcheck=now()},
- send_event(state_change,NewLoopData),
- reply_and_loop(ok,From,MRef,NewLoopData);
- {{clear,Options},From,MRef} ->
- NewLoopData=do_clear(LoopData,Options),
- reply_and_loop({ok,{new,NewLoopData#rt.status}},From,MRef,NewLoopData);
- {flush,From,MRef} ->
- case LoopData#rt.state of
- tracing -> % Can only flush if we are tracing.
- if
- is_port(LoopData#rt.tracer_port) ->
- trace_port_control(LoopData#rt.tracer_port,flush),
- reply_and_loop(ok,From,MRef,LoopData);
- true -> % Not necessary but lets pretend.
- reply_and_loop(ok,From,MRef,LoopData)
- end;
- State ->
- reply_and_loop({error,{not_tracing,State}},From,MRef,LoopData)
- end;
- {list_logs,From,MRef} ->
- TracerData=LoopData#rt.tracerdata, % Current tracerdata.
- if
- TracerData/=undefined -> % There is tracerdata!
- reply_and_loop(do_list_logs(TracerData),From,MRef,LoopData);
- true -> % Have no current tracerdata!
- reply_and_loop({error,no_tracerdata},From,MRef,LoopData)
- end;
- {{list_logs,TracerData},From,MRef} ->
- reply_and_loop(do_list_logs(TracerData),From,MRef,LoopData);
- {{fetch_log,CollectPid},From,MRef} -> % Fetch according to current tracerdata.
- TracerData=LoopData#rt.tracerdata, % Current tracerdata.
- if
- TracerData/=undefined -> % There is tracerdata!
- {Reply,NewLD}=do_fetch_log(LoopData,CollectPid,TracerData),
- reply_and_loop(Reply,From,MRef,NewLD);
- true -> % No tracerdata!
- reply_and_loop({error,no_tracerdata},From,MRef,LoopData)
- end;
- {{fetch_log,CollectPid,Spec},From,MRef} -> % Either list of files or tracerdata.
- {Reply,NewLD}=do_fetch_log(LoopData,CollectPid,Spec),
- reply_and_loop(Reply,From,MRef,NewLD);
- {delete_logs,From,MRef} ->
- if
- LoopData#rt.state==tracing -> % Can't remove then!
- reply_and_loop({error,tracing},From,MRef,LoopData);
- true ->
- TracerData=LoopData#rt.tracerdata,
- reply_and_loop(do_delete_logs(TracerData),From,MRef,LoopData)
- end;
- {{delete_logs,TracerDataOrLogList},From,MRef} ->
- if
- LoopData#rt.state==tracing -> % Can't remove then!
- reply_and_loop({error,tracing},From,MRef,LoopData);
- true ->
- reply_and_loop(do_delete_logs(TracerDataOrLogList),From,MRef,LoopData)
- end;
- {get_node_info,From,MRef} ->
- Reply=collect_node_info(LoopData),
- reply_and_loop(Reply,From,MRef,LoopData);
- {{try_to_adopt,Tag,Condition},From,MRef} ->
- if
- LoopData#rt.ctrl_ref==undefined -> % We have no control component.
- {Reply,NewLoopData}=do_try_to_adopt(Tag,Condition,LoopData,From),
- reply_and_loop(Reply,From,MRef,NewLoopData);
- true -> % We already have a control component.
- reply_and_loop({error,refused},From,MRef,LoopData)
- end;
- {{confirm_connection,_Tag},From,MRef} ->
- if
- LoopData#rt.ctrl==From -> % It must be from this process!
- Reply=collect_node_info(LoopData),
- reply_and_loop(Reply,From,MRef,LoopData);
- true -> % Strange, some one is joking?
- reply_and_loop({error,refused},From,MRef,LoopData)
- end;
- {{change_options,Options},From,MRef} ->
- case do_change_options(Options,LoopData) of
- stop -> % Can't run alone with these options!
- terminate_overload(LoopData),
- From ! {ok,MRef}; % Don't care if From not a proper pid!
- NewLoopData when is_record(NewLoopData,rt) ->
- reply_and_loop(ok,From,MRef,NewLoopData)
- end;
- {get_status,From,MRef} ->
- Reply={ok,{LoopData#rt.state,LoopData#rt.status}},
- reply_and_loop(Reply,From,MRef,LoopData);
- {get_tracerdata,From,MRef} ->
- case LoopData#rt.tracerdata of
- undefined ->
- reply_and_loop({ok,no_tracerdata},From,MRef,LoopData);
- TracerData ->
- reply_and_loop({ok,TracerData},From,MRef,LoopData)
- end;
- {state,From,MRef} -> % For debugging purposes.
- reply_and_loop(LoopData,From,MRef,LoopData);
-
- {'DOWN',CtrlRef,process,_,_} when CtrlRef==LoopData#rt.ctrl_ref ->
- case do_down_message(LoopData) of
- stop -> % inviso_c gone and we must stop!
- terminate_overload(LoopData),
- exit(running_alone);
- {ok,NewLoopData} ->
- loop1(NewLoopData)
- end;
- {'EXIT',Pid,Reason} ->
- case act_on_exit(Pid,Reason,LoopData) of
- exit ->
- terminate_overload(LoopData),
- exit(Reason);
- NewLoopData when is_record(NewLoopData,rt) ->
- loop1(NewLoopData);
- {NewLoopData,NewTimeOut} when is_record(NewLoopData,rt) ->
- loop(NewLoopData,NewTimeOut)
- end;
- Other -> % Check if it concerns overload.
- if
- LoopData#rt.overload/=?NO_LOADCHECK,
- LoopData#rt.status==running,
- LoopData#rt.state==tracing ->
- {NewLD,NewTimeOut}=
- do_check_overload(LoopData,
- {msg,{Other,LoopData#rt.overload_data}}),
- loop(NewLD,NewTimeOut);
- true ->
- NewTimeOut=calc_diff_to_now(now(),LoopData#rt.next_loadcheck),
- loop(LoopData,NewTimeOut)
- end
- after
- Timeout ->
- loop1(LoopData)
- end.
-
-reply_and_loop(Reply,To,MRef,LoopData) when is_pid(To) ->
- To ! {Reply,MRef},
- loop1(LoopData);
-reply_and_loop(_,_,_,LoopData) -> % Used together with incoming casts.
- loop1(LoopData).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% File transfer process implementation.
-%% =============================================================================
-
-%% Files that are to to be transfered from the runtime component to the control
-%% component are done so by reading them as binaries and sending them with
-%% normal message passing (over distributed Erlang).
-%% Reading the files are done in a process separate to the runtime component,
-%% to both make the code more simple. But also to free up the runtime component.
-%%
-%% This help process must be capable of recognizing the fact that the runtime
-%% component has been suspended, and then of course also discontinue any file
-%% transfere.
-fetch_init(Parent,Files,CollectPid,ChunkSize) ->
- process_flag(trap_exit,true), % We must clean-up.
- process_flag(priority,low), % Lets be careful.
- case fetch_open_file(Files,CollectPid) of
- {ok,FileName,FD,RestFiles} ->
- MRef=erlang:monitor(process,CollectPid),
- fetch_loop(Parent,RestFiles,CollectPid,ChunkSize,FileName,FD,MRef);
- done ->
- fetch_end(CollectPid);
- error ->
- fetch_incomplete(CollectPid)
- end.
-
-fetch_loop(Parent,Files,CollectPid,ChunkSize,FName,FD,MRef) ->
- receive
- {suspend,Parent} -> % The runtime component is suspended.
- file:close(FD), % We must clean-up.
- fetch_incomplete(CollectPid);
- {'DOWN',MRef,process,_,_} -> % The CollectPid terminated!
- file:close(FD); % Close file and terminate.
- {'EXIT',Parent,_Reason} -> % The runtime component terminated.
- file:close(FD),
- fetch_incomplete(CollectPid);
- _ ->
- fetch_loop(Parent,Files,CollectPid,ChunkSize,FName,FD,MRef)
- after
- 0 -> % If non of the above, get to work!
- case file:read(FD,ChunkSize) of
- {ok,Bin} ->
- fetch_send_chunk(CollectPid,Bin),
- case fetch_wait_for_chunk_ack(CollectPid,MRef) of
- ok -> % Collector ready to receive next chunk.
- fetch_loop(Parent,Files,CollectPid,ChunkSize,FName,FD,MRef);
- cancel -> % Send no more files!
- file:close(FD), % Close file, send incomplete, terminate!
- fetch_incomplete(CollectPid);
- 'DOWN' -> % Collector has terminate, stop!
- file:close(FD) % Close file and terminate.
- end;
- eof -> % Ok, go on with the next file.
- file:close(FD),
- fetch_send_eof(CollectPid),
- case fetch_open_file(Files,CollectPid) of
- {ok,NewFName,NewFD,RestFiles} ->
- fetch_loop(Parent,RestFiles,CollectPid,
- ChunkSize,NewFName,NewFD,MRef);
- done ->
- fetch_end(CollectPid);
- error ->
- fetch_incomplete(CollectPid)
- end;
- {error,Reason} -> % Do not continue.
- file:close(FD),
- fetch_send_readerror(CollectPid,FName,Reason),
- fetch_incomplete(CollectPid)
- end
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function which opens the next file to be transferred. It also communicates
-%% the opening of the file to the collector process.
-%% We know here that it will be a list of three-tuples. But there is no guarantee
-%% that Dir or FileName are proper strings.
-%% Returns {ok,FileName,FileDescriptor,RemainingFiles} or 'done'.
-fetch_open_file([{FType,Dir,FileName}|RestFiles],CollectPid) ->
- case catch file:open(filename:join(Dir,FileName),[read,raw,binary]) of
- {ok,FD} ->
- CollectPid ! {node(),open,{FType,FileName}},
- {ok,FileName,FD,RestFiles};
- {error,_Reason} ->
- CollectPid ! {node(),open_failure,{FType,FileName}},
- error;
- {'EXIT',_Reason} -> % Faulty Dir or FileName.
- CollectPid ! {node(),open_failure,{FType,FileName}},
- error
- end;
-fetch_open_file([],_CollectPid) ->
- done.
-%% -----------------------------------------------------------------------------
-
-%% A group of help functions sending information to the collector process.
-%% Returns nothing significant.
-fetch_send_chunk(CollectPid,Bin) ->
- CollectPid ! {node(),payload,Bin,self()}.
-%% -----------------------------------------------------------------------------
-
-fetch_send_eof(CollectPid) ->
- CollectPid ! {node(),end_of_file}.
-%% -----------------------------------------------------------------------------
-
-fetch_end(CollectPid) ->
- CollectPid ! {node(),end_of_transmission}.
-%% -----------------------------------------------------------------------------
-
-fetch_send_readerror(CollectPid,FName,Reason) ->
- CollectPid ! {node(),{error,{file_read,{Reason,FName}}}}.
-%% -----------------------------------------------------------------------------
-
-fetch_incomplete(CollectPid) ->
- CollectPid ! {node(),incomplete}.
-%% -----------------------------------------------------------------------------
-
-%% Help function waiting for the collector to respond that it is ready to receive
-%% the next chunk. This is in order to exercise flow control protecting the
-%% collector to get swamped if the node where the collector runs is busy.
-fetch_wait_for_chunk_ack(CollectPid,MRef) ->
- receive
- {CollectPid,chunk_ack} ->
- ok;
- {CollectPid,cancel_transmission} -> % Some problem at collector side.
- cancel;
- {'DOWN',MRef,process,_,_} -> % The collector terminated.
- 'DOWN'
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% First level do-functions, called from the main server loop on incomming
-%% requests.
-%% =============================================================================
-
-%% Function performing the overload check. Returns {NewLoopData,TimeOut}.
-%% Note that this function may also cause a suspend to be carried out if the
-%% loadcheck turns out negative.
-do_check_overload(LD,Data) ->
- case do_check_overload_2(LD#rt.overload,Data) of
- ignore -> % Load check not performed.
- {LD,calc_diff_to_now(now(),LD#rt.next_loadcheck)};
- {ok,Interval} -> % No problem, continue.
- NextLoadCheck=add_to_now(now(),Interval),
- {LD#rt{next_loadcheck=NextLoadCheck},Interval};
- {suspend,Reason} -> % Emergency! suspend, suspend!
- NewLD=do_suspend(LD,Reason),
- {NewLD,infinity}; % No need to do load-checks now!
- {new,NewData,Interval} -> % The overload was restarted or something.
- NextLoadCheck=add_to_now(now(),Interval),
- {LD#rt{overload_data=NewData,next_loadcheck=NextLoadCheck},Interval};
- error -> % Inhibit overload check then.
- {LD#rt{overload=?NO_LOADCHECK},infinity}
- end.
-
-%% Help function performing an overload check. Returns {ok,Interval},
-%% {suspend,Reason}, 'error' ir 'ignore'.
-do_check_overload_2({{Mod,Func},Interval,_,_},Data) ->
- do_check_overload_3(Interval,catch Mod:Func(Data));
-do_check_overload_2({Fun,Interval,_,_},Data) when is_function(Fun) ->
- do_check_overload_3(Interval,catch Fun(Data));
-do_check_overload_2(_,_) -> % Bad loadcheck configuration.
- error. % Stop using load checks then.
-
-do_check_overload_3(Interval,ok) ->
- {ok,Interval};
-do_check_overload_3(Interval,{new,NewData}) ->
- {new,NewData,Interval};
-do_check_overload_3(_Interval,{suspend,Reason}) ->
- {suspend,Reason};
-do_check_overload_3(_Interval,ignore) -> % Loadcheck not triggered.
- ignore;
-do_check_overload_3(_Interval,_) -> % Failure or other return value.
- error. % Stop doing loadchecks from now on.
-%% ------------------------------------------------------------------------------
-
-%% Function setting the trace-pattern according to Args and Flags. Note that
-%% Args can contain regexps which must be expanded here.
-%% Returns a list: [Result], where Result can be: int()|{error,Reason}.
-%% Sometimes an error tuple will represent an entire pattern, sometimes the
-%% pattern will expand to a number of error-tuples.
-do_set_trace_patterns(Args,Flags) ->
- Replies=do_set_trace_patterns_2(Args,Flags,[]),
- lists:reverse(Replies).
-
-do_set_trace_patterns_2([{M,F,Arity,MS}|Rest],Flags,Replies) -> % Option-less.
- do_set_trace_patterns_2([{M,F,Arity,MS,[]}|Rest],Flags,Replies);
-do_set_trace_patterns_2(Mlist = [{M,F,Arity,MS,Opts}|Rest],Flags,Replies) when is_atom(M) ->
- case length(Mlist) rem 10 of
- 0 ->
- timer:sleep(100);
- _ ->
- ok
- end,
- %% sleep 100 ms for every 10:th element in the list to let other
- %% processes run since this is a potentially
- %% heavy operation that might result in an unresponsive Erlang VM for
- %% several seconds otherwise
- case load_module_on_option(M,Opts) of
- true -> % Already present, loaded or no option!
- case catch erlang:trace_pattern({M,F,Arity},MS,Flags) of
- No when is_integer(No) ->
- do_set_trace_patterns_2(Rest,Flags,[No|Replies]);
- {'EXIT',Reason} ->
- do_set_trace_patterns_2(Rest,
- Flags,
- [{error,{bad_trace_args,[{M,F,Arity,MS},Reason]}}|
- Replies])
- end;
- false -> % Module not present, or not found!
- do_set_trace_patterns_2(Rest,Flags,[0|Replies])
- end;
-do_set_trace_patterns_2([{M,F,Arity,MS,Opts}|Rest],Flags,Replies) when is_list(M) ->
- do_set_trace_patterns_2([{{void,M},F,Arity,MS,Opts}|Rest],Flags,Replies);
-do_set_trace_patterns_2([{{Dir,M},F,Arity,MS,Opts}|Rest],Flags,Replies)
- when is_list(Dir),is_list(M) ->
- case check_pattern_parameters('_',F,Arity,MS) of % We don't want to repeat bad params.
- true ->
- case inviso_rt_lib:expand_regexp(Dir,M,Opts) of % Get a list of real modulnames.
- Mods when is_list(Mods) ->
- MoreReplies=
- do_set_trace_patterns_2(lists:map(fun(Mod)->
- {Mod,F,Arity,MS,Opts}
- end,
- Mods),
- Flags,
- Replies),
- do_set_trace_patterns_2(Rest,Flags,MoreReplies);
- {error,Reason} ->
- do_set_trace_patterns_2(Rest,Flags,[{error,Reason}|Replies])
- end;
- false -> % Bad pattern parameters.
- do_set_trace_patterns_2(Rest,
- Flags,
- [{error,{bad_trace_args,{M,F,Arity,MS}}}|Replies])
- end;
-do_set_trace_patterns_2([Arg|Rest],Flags,Replies) ->
- do_set_trace_patterns_2(Rest,Flags,[{error,{bad_trace_args,Arg}}|Replies]);
-do_set_trace_patterns_2([],_Flags,Replies) ->
- Replies.
-%% -----------------------------------------------------------------------------
-
-%% Help function which sets the trace flags for all processes specifed in Args.
-%% Args shall be a list of {ProcessSpecification,ProcessTraceFlags}.
-%% Returns {ok,Answers} where Answers is a list of integer and error descriptions.
-%% Note that a process specification may be a particular pid or a {global,Name}.
-%% In the case the process does not exist we will fake a zero instead of an
-%% error.
-do_set_trace_flags(Tracer,Args,How) ->
- Fun=fun({Proc,Flags}) ->
- case check_traceflag_pidspec(Proc) of
- {ok,Proc2} -> % Reg-names converted.
- case check_flags(Flags) of
- Flags2 when is_list(Flags2) -> % No error!
- case (catch
- case How of
- true ->
- erlang:trace(Proc2,
- true,
- [{tracer,Tracer}|Flags2]);
- false -> % No tracer of turning off.
- erlang:trace(Proc2,
- false,
- Flags2)
- end) of
- N when is_integer(N) ->
- N;
- {'EXIT',Reason} ->
- if
- is_pid(Proc2) ->
- 0; % Proc2 not alive or not at this node!
- true -> % Otherwise, just error!
- {error,
- {bad_trace_args,
- [Reason,Proc2,How,Flags2,Tracer]}}
- end
- end;
- FlagError ->
- FlagError
- end;
- false -> % Skip it.
- 0; % Indicate that zero processes matched.
- {error,Reason} -> % Bad process specification.
- {error,{bad_process,[Reason,Proc]}}
- end;
- (Faulty) ->
- {error,{bad_process,Faulty}}
- end,
- {ok,lists:map(Fun,Args)}.
-%% ------------------------------------------------------------------------------
-
-%% Function calling API:s in the trace information server. Note that we have
-%% given the responsibility to form a correct functionsname and argument list
-%% to the caller.
-%% Returns whatever the called function returns.
-do_meta_pattern(MPid,{FuncName,ArgList}) ->
- case catch apply(inviso_rt_meta,FuncName,[MPid|ArgList]) of
- {'EXIT',_Reason} ->
- {error,{badarg,{FuncName,ArgList}}};
- Result ->
- Result
- end;
-do_meta_pattern(_MPid,BadArgs) ->
- {error,{bad_args,BadArgs}}.
-%% ------------------------------------------------------------------------------
-
-%% Function removing *all* patterns. Beaware that the one for local patterns
-%% causes a walkthrough of all loaded modules.
-do_clear_trace_patterns() ->
- erlang:trace_pattern({'_','_','_'},false,[local]), %% inc. meta, call_count
- erlang:trace_pattern({'_','_','_'},false,[global]).
-%% ------------------------------------------------------------------------------
-
-%% Function that takes TracerData and initializes the tracing. That can be
-%% opening appropriate logfiles, starting meta-tracer. There must be one
-%% clause here for every "type" of logging we want to be able to do.
-%% Returns the Reply to be forwarded to the caller.
-do_init_tracing(LoopData,TD,{HandlerFun,Data},TiTD) when is_function(HandlerFun) ->
- {NewLoopData,Reply}=
- case do_init_metatracing(TiTD,self()) of
- {ok,MetaPid} ->
- {LoopData#rt{handler={HandlerFun,Data},
- tracerdata=TD,
- meta_tracer=MetaPid,
- state=tracing},
- {ok,[{trace_log,ok},{ti_log,ok}]}};
- false -> % No meta tracing requested.
- {LoopData#rt{handler={HandlerFun,Data},
- tracerdata=TD,
- state=tracing},
- {ok,[{trace_log,ok}]}};
- {error,Reason} -> % Problems starting meta tracing.
- {LoopData#rt{handler={HandlerFun,Data},
- tracerdata=TD,
- state=tracing},
- {ok,[{trace_log,ok},{ti_log,{error,Reason}}]}}
- end,
- send_event(state_change,NewLoopData), % Send to subscribing processes.
- {NewLoopData,Reply};
-do_init_tracing(LoopData,TD,{Type,Parameters},TiTD) when Type==ip;Type==file ->
- case check_traceport_parameters(Type,Parameters) of
- ok ->
- case catch trace_port(Type,Parameters) of
- Fun when is_function(Fun) ->
- case catch Fun() of
- Port when is_port(Port) -> % Ok, our trace-port is open.
- {NewLoopData,Reply}=
- case do_init_metatracing(TiTD,Port) of
- {ok,MetaPid} ->
- {LoopData#rt{tracer_port=Port,
- tracerdata=TD,
- meta_tracer=MetaPid,
- state=tracing},
- {ok,[{trace_log,ok},{ti_log,ok}]}};
- false -> % No meta tracing requested.
- {LoopData#rt{tracer_port=Port,
- tracerdata=TD,
- state=tracing},
- {ok,[{trace_log,ok}]}};
- {error,Reason} -> % Problems starting meta tracing.
- {LoopData#rt{tracer_port=Port,
- tracerdata=TD,
- state=tracing},
- {ok,[{trace_log,ok},{ti_log,{error,Reason}}]}}
- end,
- send_event(state_change,NewLoopData),
- {NewLoopData,Reply};
- {'EXIT',Reason} ->
- {LoopData,{error,{bad_port_fun,[Parameters,Reason]}}}
- end;
- {'EXIT',Reason} ->
- {LoopData,{error,{bad_port_args,[Parameters,Reason]}}}
- end;
- {error,Reason} -> % Bad traceport parameters.
- {LoopData,{error,Reason}}
- end.
-
-%% Help function that starts the meta-tracing. Note that the runtime component
-%% will becom linked to it.
-%% Currently the meta tracer handles two types, 'file' and 'relay'.
-%% Note that Tracer tells the meta tracer where regular trace messages shall be
-%% sent. This is because the meta tracer is capable of appending a {tracer,Tracer}
-%% action term to meta match specs.
-do_init_metatracing(LogSpec={_Type,_Arg},Tracer) ->
- case inviso_rt_meta:start(LogSpec,Tracer) of
- {ok,MetaPid} ->
- {ok,MetaPid};
- {error,Reason} ->
- {error,Reason}
- end;
-do_init_metatracing({Type,Arg,{InitPublLDmfa,RemovePublLDmf,CleanPublLDmf}},Tracer)->
- case inviso_rt_meta:start({Type,Arg},Tracer,InitPublLDmfa,RemovePublLDmf,CleanPublLDmf) of
- {ok,MetaPid} ->
- {ok,MetaPid};
- {error,Reason} ->
- {error,Reason}
- end;
-do_init_metatracing(void,_) -> % Means no meta tracer.
- false.
-%% -----------------------------------------------------------------------------
-
-%% Function that stops all tracing and closes all open files. This function
-%% can't fail :-) It tries as hard as it can.
-%% This function also kills the autostarter process if one exists. Otherwise it
-%% will not be possible from a control component to end an ongoing autostarted
-%% tracing.
-%% Returns a new loopdata structure since stopping tracing involves updating it.
-do_stop_tracing(LoopData) ->
- do_stop_tracing_kill_autostarter(LoopData#rt.auto_starter),
- do_clear_trace_flags(), % Do not generate any more traces.
- NewLoopData1=do_stop_tracing_tracelog(LoopData),
- NewLoopData2=do_stop_tracing_metatracing(NewLoopData1),
- NewLoopData3=NewLoopData2#rt{state=idle,auto_starter=undefined},
- send_event(state_change,NewLoopData3),
- NewLoopData3.
-
-do_stop_tracing_tracelog(LoopData=#rt{tracer_port=Port}) when is_port(Port) ->
- trace_port_control(Port,flush), % Write buffered trace messages.
- catch port_close(Port),
- LoopData#rt{tracer_port=undefined};
-do_stop_tracing_tracelog(LoopData) ->
- LoopData#rt{handler=undefined}.
-
-do_stop_tracing_metatracing(LoopData=#rt{meta_tracer=MPid}) when is_pid(MPid) ->
- inviso_rt_meta:stop(MPid),
- LoopData#rt{meta_tracer=undefined};
-do_stop_tracing_metatracing(LoopData) -> % No meta tracer running!
- LoopData.
-
-%% Help function killing the autostarter, if one is active.
-do_stop_tracing_kill_autostarter(P) when is_pid(P) ->
- exit(P,stop_tracing);
-do_stop_tracing_kill_autostarter(_) -> % No autostarter, do nothing.
- true.
-%% -----------------------------------------------------------------------------
-
-%% Help function implementing suspending the runtime component.
-%% Returns a new loopdata structure.
-do_suspend(LD,Reason) ->
- do_clear_trace_flags(), % If no process flags, no output!
- do_suspend_metatracer(LD#rt.meta_tracer),
- do_suspend_fetchers(LD#rt.fetchers),
- do_stop_tracing_kill_autostarter(LD#rt.auto_starter),
- NewLD=LD#rt{fetchers=[],status={suspended,Reason},auto_starter=undefined},
- send_event(state_change,NewLD), % Notify subscribers.
- NewLD.
-
-do_suspend_metatracer(MetaTracer) when is_pid(MetaTracer) ->
- inviso_rt_meta:suspend(MetaTracer); % This makes it suspended.
-do_suspend_metatracer(_) ->
- true.
-
-do_suspend_fetchers([FetcherPid|Rest]) ->
- FetcherPid ! {suspend,self()}, % This makes it terminate.
- do_suspend_fetchers(Rest);
-do_suspend_fetchers([]) ->
- true.
-%% ------------------------------------------------------------------------------
-
-%% Function that stops all tracing, removes all trace-patterns and removes all
-%% logfiles. The idea is to return the runtime component to the 'new' state.
-do_clear(LoopData,Opts) when is_list(Opts) ->
- NewLoopData=do_stop_tracing(LoopData), % First stop tracing, if tracing.
- case lists:member(keep_trace_patterns,Opts) of
- false ->
- do_clear_trace_patterns();
- _ ->
- true
- end,
- case lists:member(keep_log_files,Opts) of
- false ->
- if
- NewLoopData#rt.tracerdata/=undefined ->
- do_delete_logs(NewLoopData#rt.tracerdata);
- true -> % If no tracerdata, nothing to remove!
- true % Do nothing then.
- end;
- _ ->
- true
- end,
- NewLoopData#rt{state=new,tracerdata=undefined};
-do_clear(LoopData,_Opts) -> % Faulty Opts.
- do_clear(LoopData,[]). % Then just ignore the options.
-%% -----------------------------------------------------------------------------
-
-%% Function which takes a tracerdata, either our own or a "suggested"
-%% and tries to find the corresponding files. Note that the return value only
-%% contains "types" of logs that the tracerdata is pointing out. Hence
-%% is there no ti-log, no one will be mentioned in the return value.
-do_list_logs(TracerData) -> % Handles both list and tuple.
- case translate_td(TracerData) of
- {ok,LogTD,TiTD} ->
- {TraceDir,TraceLogs}=list_logs_tracelog(LogTD),
- {TiDir,TiLogs}=list_logs_tilog(TiTD),
- case {TraceLogs,TiLogs} of
- {no_log,no_log} -> % Tracerdata not generating logs!
- {ok,no_log};
- {_,no_log} -> % No ti logs.
- {ok,[{trace_log,TraceDir,TraceLogs}]};
- {no_log,_} -> % Only ti-logs, unusual!
- {ok,[{ti_log,TiDir,TiLogs}]};
- _ -> % Both trace and ti logs.
- {ok,[{trace_log,TraceDir,TraceLogs},{ti_log,TiDir,TiLogs}]}
- end;
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function implementing fetching logfiles using distributed Erlang.
-%% This function works for both situations, a list of specific files are
-%% requested, or a tracerdata is specified.
-%% Returns {Reply,NewLoopData}.
-do_fetch_log(LD,CollectPid,What) ->
- if
- LD#rt.state/=tracing ->
- case is_list_of_files_or_tracerdata(What) of
- files ->
- FetcherPid=do_fetch_log_listoffiles(CollectPid,What),
- {{ok,FetcherPid},add_fetcher_ld(FetcherPid,LD)};
- tracerdata ->
- case do_fetch_log_tracerdata(CollectPid,What) of
- {Reply,FetcherPid} when is_pid(FetcherPid) ->
- {Reply,add_fetcher_ld(FetcherPid,LD)};
- {Reply,_} -> % No fetch process was started.
- {Reply,LD}
- end;
- false -> % It is an empty list!
- {{complete,no_log},LD};
- error -> % Incorrect parameter.
- {{error,badarg},LD}
- end;
- true -> % No transfere during tracing.
- {{error,tracing},LD}
- end.
-
-%% Function taking tracerdata to find out what files to send over to the RemotePid.
-%% Note that we will not go back to the loop function from here but rather call
-%% the fetch_loop instead, prepresenting the fetch-log state. Unless we encounter
-%% a problem.
-do_fetch_log_tracerdata(CollectPid,TracerData) ->
- case do_list_logs(TracerData) of
- {ok,no_log} ->
- {{complete,no_log},void};
- {ok,Logs} -> % Ok, some trace_log and ti_log.
- FetcherPid=do_fetch_log_listoffiles(CollectPid,Logs),
- {{ok,FetcherPid},FetcherPid};
- {error,Reason} -> % Problem with tracerdata!
- {{error,Reason},void}
- end.
-
-do_fetch_log_listoffiles(CollectPid,FileSpec) ->
- ExpandedFileSpec=do_fetch_log_expand_filespec(FileSpec),
-%% !!! try out different ChunkSizes
-% ChunkSize = 60,
-% ChunkSize = 7*1024,
- ChunkSize=1024,
- _Fetcher=spawn_link(?MODULE,
- fetch_init,
- [self(),ExpandedFileSpec,CollectPid,ChunkSize]).
-
-%% Help function which expands the list of logs to have tags in front of every
-%% file, as required by the fetch_loop.
-do_fetch_log_expand_filespec(Logs) ->
- TraceLogs=
- case lists:keysearch(trace_log,1,Logs) of
- {value,{_,Dir1,Logs1}} -> % There is a list of trace-logs.
- lists:map(fun(File)->{trace_log,Dir1,File} end,Logs1);
- false -> % No trace-logs!
- []
- end,
- TiLogs=
- case lists:keysearch(ti_log,1,Logs) of
- {value,{_,Dir2,Logs2}} ->
- lists:map(fun(File)->{ti_log,Dir2,File} end,Logs2);
- false ->
- []
- end,
- TiLogs++TraceLogs.
-
-%% ------------------------------------------------------------------------------
-
-%% Function that removes all logfiles associated with a certain tracerdata.
-do_delete_logs(TracerDataOrLogList) ->
- case is_list_of_files_or_tracerdata(TracerDataOrLogList) of
- tracerdata ->
- case translate_td(TracerDataOrLogList) of
- {ok,LogTD,TiTD} ->
- case {list_logs_tracelog(LogTD),list_logs_tilog(TiTD)} of
- {{_,no_log},{_,no_log}} -> % No logs nowhere!
- {ok,no_log};
- {{LogDir,LogFiles},{_,no_log}} -> % No ti.
- {ok,[{trace_log,delete_files(LogDir,LogFiles)}]};
- {{_,no_log},{TiDir,TiFiles}} ->
- {ok,[{ti_log,delete_files(TiDir,TiFiles)}]};
- {{LogDir,LogFiles},{TiDir,TiFiles}} ->
- {ok,[{trace_log,delete_files(LogDir,LogFiles)},
- {ti_log,delete_files(TiDir,TiFiles)}]}
- end;
- {error,Reason} ->
- {error,Reason}
- end;
- files -> % It is [{trace_log,Dir,Files},..
- if
- is_list(hd(TracerDataOrLogList)) -> % Just a list of files.
- {ok,delete_files(".",TracerDataOrLogList)};
- is_tuple(hd(TracerDataOrLogList)) -> % A "modern" logspec.
- case {lists:keysearch(trace_log,1,TracerDataOrLogList),
- lists:keysearch(ti_log,1,TracerDataOrLogList)} of
- {false,false} -> % Hmm, no logs specified!
- {ok,[]}; % Easy response!
- {{value,{_,LogDir,LogFiles}},false} ->
- {ok,[{trace_log,delete_files(LogDir,LogFiles)}]};
- {false,{value,{_,TiDir,TiFiles}}} ->
- {ok,[{ti_log,delete_files(TiDir,TiFiles)}]};
- {{value,{_,LogDir,LogFiles}},{value,{_,TiDir,TiFiles}}} ->
- {ok,[{trace_log,delete_files(LogDir,LogFiles)},
- {ti_log,delete_files(TiDir,TiFiles)}]}
- end
- end;
- false -> % Can't tell which!
- {ok,[]};
- error ->
- {error,{badarg,TracerDataOrLogList}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function handling the request when a control component wishing to take
-%% control over this already existing control component. It does not matter
-%% what state it is in. It can very well already be tracing.
-%% Returns {Reply,NewLoopData}.
-%% Where the Reply tells the control component wether it took control of it
-%% or not. {node_info,node(),self(),Vsn,State,Status,{tag,Tag}} means that we
-%% can be adopted (and more precisely considers ourselves being adopted now).
-do_try_to_adopt(Tag,if_ref,LoopData=#rt{tag=Tag},_Ctrl) ->
- {{error,{wrong_reference,LoopData#rt.tag}},LoopData};
-do_try_to_adopt(NewTag,_Condition,LoopData,CtrlPid) ->
- case LoopData#rt.timer_ref of % Do we have a running-alone timer?
- undefined -> % No we don't.
- true;
- TimerRef ->
- timer:cancel(TimerRef)
- end,
- CtrlRef=erlang:monitor(process,CtrlPid), % Lets monitor our new "master"!
- {DepVal,_}=LoopData#rt.dependency,
- {node_info,Node,Pid,VSN,State,Status,Tag}=collect_node_info(LoopData),
- NewLoopData=
- LoopData#rt{dependency={DepVal,node(CtrlPid)},
- ctrl=CtrlPid,
- ctrl_ref=CtrlRef, % Monitoring our new master.
- tag=NewTag, % Use this tag from now on.
- timer_ref=undefined},
- {{node_info,Node,Pid,VSN,State,Status,{tag,Tag}},NewLoopData}.
-%% -----------------------------------------------------------------------------
-
-%% Function changing parameters accoring to a new options list. Note that we
-%% can not change control component if the one we have is still working.
-%% We can however of course change how this runtime component will react to
-%% a running alone scenario.
-%% Returns 'stop' or NewLoopData.
-do_change_options(Options,LoopData) ->
- NewLoopData=read_option_list(Options,LoopData),
- if
- NewLoopData/=LoopData -> % Some options changed.
- case do_change_options_ctrl(LoopData,NewLoopData) of
- stop ->
- stop;
- {ok,NewLoopData2} ->
- NewLoopData3=do_change_options_overload(LoopData,NewLoopData2),
- NewLoopData3#rt{next_loadcheck=now()} % Force a load check next.
- end;
- true ->
- LoopData
- end.
-
-%% Help function which sets up the new dependencies. Note that we only do that
-%% if do not have a working control component.
-%% Returns {ok,NewLoopData} or 'stop'.
-do_change_options_ctrl(OldLD,NewLD) ->
- if
- OldLD#rt.timer_ref/=undefined -> % No control and waiting to terminate.
- timer:cancel(OldLD#rt.timer_ref),
- do_down_message(NewLD#rt{timer_ref=undefined});
- OldLD#rt.ctrl==undefiend -> % No control component.
- do_down_message(NewLD);
- true -> % We have a working control component!
- {ok,NewLD}
- end.
-
-do_change_options_overload(OldLD,NewLD) ->
- if
- OldLD#rt.overload/=NewLD#rt.overload ->
- terminate_overload(OldLD),
- NewOverloadData=initialize_overload(NewLD),
- NewLD#rt{overload_data=NewOverloadData};
- true -> % No changes done.
- NewLD
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function handling an incoming DOWN message from our control component.
-%% If the runtime component is not allowed to run without a control component, it
-%% simply terminates which closes the trace-port and process trace flags are
-%% therefore automatically removed.
-%% Returns 'stop' or a {ok,NewLoopData} structure.
-do_down_message(LoopData) ->
- case LoopData#rt.dependency of
- {0,_} -> % Not allowed to run without controller.
- stop;
- {infinity,_} -> % Don't care. Just remove the controller.
- {ok,LoopData#rt{ctrl=undefined,ctrl_ref=undefined}};
- {TimeOut,_} -> % Allowed to run TimeOut ms alone.
- {ok,TimerRef}=timer:exit_after(TimeOut,self(),running_alone),
- {ok,LoopData#rt{timer_ref=TimerRef,ctrl=undefined,ctrl_ref=undefined}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function handling incomming exit signals. We can expect exit signals from the
-%% following: Our parent supervisor (runtime_tools_sup), a meta-tracer process,
-%% a logfile fetcher process, or the auto_starter.
-%% A trace-port may also generate an exit signal.
-%% In addition it is possible that an overload mechanism generates exit-signals.
-%% We can also get the running_alone exit signal from our self. This is the
-%% situation if our control component has terminated and this runtime component
-%% is not allowed to exist on its own for ever.
-%% Also note that after we have stopped tracing, for any reason, it is not
-%% impossible that we receive the EXIT signals from still working parts that
-%% we are now shuting down. This is no problem, the code will mearly update
-%% the loopdata structure once again.
-%% Returns 'exit' indicating that the runtime component shall terminate now,
-%% {NewLoopData,NewTimeOut} if the exit-signal resulted in an overload check, or
-%% a new loopdata structure shall we ignore the exit, or it simply resulted in
-%% a state-change.
-act_on_exit(Parent,_Reason,#rt{parent=Parent}) ->
- exit;
-act_on_exit(_Pid,running_alone,_LoopData) ->
- exit;
-act_on_exit(MetaTracer,_Reason,LoopData=#rt{meta_tracer=MetaTracer}) ->
- LoopData#rt{meta_tracer=undefined}; % It does not exit anylonger.
-act_on_exit(Port,Reason,LoopData=#rt{tracer_port=Port}) ->
- send_event({port_down,node(),Reason},LoopData),
- _NewLoopData=do_stop_tracing(LoopData);
-act_on_exit(AutoStarter,_Reason,LoopData=#rt{auto_starter=AutoStarter}) ->
- LoopData#rt{auto_starter=undefined}; % The autostarter has terminated.
-act_on_exit(Pid,Reason,LoopData) ->
- case remove_fetcher_ld(Pid,LoopData) of
- {true,NewLoopData} -> % Yes it really was a fetcher.
- NewLoopData;
- false -> % No it was not a fetcher.
- act_on_exit_overload(Pid,Reason,LoopData)
- end.
-
-%% Help function checking if this exit has anything to do with an overload
-%% mechanism. Note that here we run the overload mechanism regardless of
-%% if we are tracing or not. This because an exit signal from the overload
-%% must most likely always be handled.
-act_on_exit_overload(Pid,Reason,LoopData) ->
- if
- LoopData#rt.overload/=?NO_LOADCHECK ->
- {_NewLD,_NewTimeOut}=
- do_check_overload(LoopData,
- {'EXIT',{Pid,Reason,LoopData#rt.overload_data}});
- true -> % Overload not in use.
- LoopData
- end.
-%% -----------------------------------------------------------------------------
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-%% ==============================================================================
-%% Various help functions.
-%% ==============================================================================
-
-%% Help function which calculates a new now-tuple by adding Interval milliseconds
-%% to the first argument. Note that Interval may be 'infinity' too.
-%% Returns a new now-tuple or "bigvalue" which is greater than any now-tuple.
-add_to_now({MegSec,Sec,MicroSec},Interval) when is_integer(Interval) ->
- NewSec=Sec+(Interval div 1000),
- if
- NewSec>=1000000 ->
- {MegSec+1,NewSec-1000000,MicroSec};
- true ->
- {MegSec,NewSec,MicroSec}
- end;
-add_to_now(_,infinity) ->
- "bigvalue".
-%% ------------------------------------------------------------------------------
-
-%% Help function calculating the difference in milliseconds between its first
-%% and second argument. This is useful when calculating an after timeout value
-%% from current now() and next_loadcheck value.
-calc_diff_to_now(T1={_,_,_},T2={_,_,_}) ->
- TimeOut1=timer:now_diff(T2,T1), % The difference in microseconds.
- if
- TimeOut1<0 ->
- 0;
- true -> % Make milliseconds out of it.
- TimeOut1 div 1000
- end;
-calc_diff_to_now(_T1,_) -> % Next loadcheck is not activated.
- infinity. % The the after timeout is infinity.
-%% ------------------------------------------------------------------------------
-
-
-%% Help function returning information about this runtime component.
-collect_node_info(#rt{vsn=VSN,state=State,status=Status,tag=Tag}) ->
- {node_info,node(),self(),VSN,State,Status,Tag}.
-%% ------------------------------------------------------------------------------
-
-%% Help function sending information to the control component that state/status
-%% change has occurred. Returns nothing significant.
-send_event(state_change,LoopData=#rt{ctrl=CtrlPid}) when is_pid(CtrlPid) ->
- Event={trace_event,{state_change,node(),{LoopData#rt.state,LoopData#rt.status}}},
- CtrlPid ! Event;
-send_event(Event,#rt{ctrl=CtrlPid}) when is_pid(CtrlPid) ->
- CtrlPid ! {event,Event};
-send_event(_,_) -> % We have no control to send to!
- true. % Maybe tracing alone after autostart.
-%% ------------------------------------------------------------------------------
-
-%% Help function initializing the overload protection mechanism. This may be
-%% necessary if it is a port program or similar. Returns {ok,Data} or 'void'.
-%% The datastructure vill be given to LoadMF as argument whenever loadchecks
-%% are done.
-initialize_overload(#rt{overload={_MF,_Interval,{M,F,Args},_RemoveMFA}}) ->
- case catch apply(M,F,Args) of
- {ok,Data} ->
- Data;
- _ -> % 'EXIT' or other faulty returnvalue.
- void
- end;
-initialize_overload(_) ->
- void.
-%% ------------------------------------------------------------------------------
-
-%% Help function which terminates an overload protection mechanism.
-%% Returns nothing significant.
-terminate_overload(#rt{overload={_MF,_Interval,_InitMFA,{M,F,Args}},
- overload_data=Data}) ->
- catch apply(M,F,[Data|Args]), % Interested in the side-effect.
- true;
-terminate_overload(_) ->
- true.
-%% ------------------------------------------------------------------------------
-
-
-%% Help function which checks that a process specified for trace flags is correct.
-%% Either the built-in "aliases" for groups of processes, a pid, a locally registered
-%% name. This function also works for globally registered names. It must then
-%% first be established that the process is local for this node before setting any
-%% process flags.
-%% Returns {ok,PidSpec}, 'false' or {error,Reason}.
-check_traceflag_pidspec(all) -> {ok,all};
-check_traceflag_pidspec(new) -> {ok,new};
-check_traceflag_pidspec(existing) -> {ok,existing};
-check_traceflag_pidspec(Name) when is_atom(Name) ->
- check_traceflag_pidspec({local,Name});
-check_traceflag_pidspec({local,A}) when is_atom(A) ->
- case whereis(A) of
- undefined -> % Then it is considered faulty.
- {error,{nonexistent_name,A}};
- Pid when is_pid(Pid) ->
- {ok,Pid}
- end;
-check_traceflag_pidspec({global,Name}) when is_atom(Name) ->
- case global:whereis_name(Name) of
- undefined -> % Then the name does not exist at all.
- {error,{nonexistent_name,{global,Name}}};
- Pid when is_pid(Pid) -> % Ok, but must check that it is here.
- if
- node()==node(Pid) ->
- {ok,Pid};
- true -> % Pid is not at this node.
- false % Not an error but cant be used.
- end
- end;
-check_traceflag_pidspec(Pid) when is_pid(Pid) ->
- {ok,Pid};
-check_traceflag_pidspec(Proc) ->
- {error,{faulty,Proc}}.
-%% ------------------------------------------------------------------------------
-
-%% Help function removing all trace flags from all processes. Useful in connection
-%% with suspend. Returns nothing significant.
-do_clear_trace_flags() ->
- erlang:trace(all, false, [all]).
-%% ------------------------------------------------------------------------------
-
-%% Help function which checks that only valid process trace flags are mentioned.
-%% In order to create better fault reports.
-%% Returns a list of the approved flags, or {error,Reason}.
-check_flags(Flags) ->
- check_flags_2(Flags,Flags).
-
-check_flags_2([send|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2(['receive'|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([call|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([return_to|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([procs|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([garbage_collection|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([running|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([set_on_spawn|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([set_on_first_spawn|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([set_on_link|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([timestamp|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([arity|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([silent|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([],Flags) -> Flags;
-check_flags_2([Faulty|_],_Flags) -> {error,{bad_flag,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Help function which checks parameters to erlang:trace_pattern. The purpose of
-%% the function is to avoid to get multiple error return values in the return
-%% list for a pattern used together with a regexp expanded module name.
-check_pattern_parameters(Mod,Func,Arity,MS) ->
- MSresult = check_MS(MS),
- MFAresult = check_MFA(Mod,Func,Arity),
- MFAresult and MSresult.
-
-check_MS(MS) when is_list(MS) -> true;
-check_MS(true) -> true;
-check_MS(false) -> true.
-
-check_MFA('_','_','_') -> true;
-check_MFA(Mod,'_','_') when is_atom(Mod) -> true;
-check_MFA(Mod,'_',A) when is_atom(Mod), is_integer(A) -> false;
-check_MFA(Mod,F,'_') when is_atom(Mod), is_atom(F) -> true;
-check_MFA(Mod,F,A) when is_atom(Mod), is_atom(F), is_integer(A) -> true.
-
-%% -----------------------------------------------------------------------------
-
-%% Help function finding out if Mod is loaded, and if not, if it can successfully
-%% be loaded. The Opts list can prevent modules from being loaded.
-%% Returns 'true' or 'false'.
-load_module_on_option(Mod,Opts) when is_list(Opts) ->
- case lists:member(no_loadcheck,Opts) of
- true -> % Then just skip this, return true.
- true;
- false ->
- case erlang:module_loaded(Mod) of
- true ->
- true; % It is loaded, do no more.
- false ->
- case lists:member(only_loaded,Opts) of
- true -> % Then, make no attempts to load.
- false;
- false -> % Try to load!
- case code:ensure_loaded(Mod) of
- {module,_Mod} -> % Successfully loaded!
- true;
- {error,_Reason} ->
- false
- end
- end
- end
- end;
-load_module_on_option(Mod,_Opts) -> % Most likely Opts not a list!
- load_module_on_option(Mod,[]). % Call without options.
-%% -----------------------------------------------------------------------------
-
-%% Help function taking a tuplelist of options turning them into a loopdata
-%% structure. Returns the loopdata structure with the new values changed.
-read_option_list([],LD) -> % Done, return loopdata.
- LD;
-read_option_list([{dependency,{Value,Node}}|Rest],LD) ->
- read_option_list(Rest,LD#rt{dependency={Value,Node}});
-read_option_list([{dependency,Value}|Rest],LD) when is_integer(Value);Value==infinity ->
- read_option_list(Rest,LD#rt{dependency={Value,node()}});
-read_option_list([overload|Rest],LD) -> % So that we can remove loadcheck.
- read_option_list(Rest,LD#rt{overload=?NO_LOADCHECK});
-read_option_list([{overload,{MF,Interval}}|Rest],LD)
- when is_integer(Interval);Interval==infinity ->
- read_option_list(Rest,LD#rt{overload={MF,Interval,void,void}});
-read_option_list([{overload,{MF,Interval,InitMFA,RemoveMFA}}|Rest],LD)
- when is_integer(Interval);Interval==infinity ->
- read_option_list(Rest,LD#rt{overload={MF,Interval,InitMFA,RemoveMFA}});
-read_option_list([{overload,Interval}|Rest],LD)
- when is_integer(Interval);Interval==infinity ->
- read_option_list(Rest,LD#rt{overload={fun ?DEFAULT_OVERLOAD_FUNC/1,
- Interval,
- void,
- void}});
-read_option_list([_|Rest],LD) -> % Unknown option.
- read_option_list(Rest,LD).
-%% -----------------------------------------------------------------------------
-
-%% Help function which returns the version number for the runtime_tools
-%% application. Since it is called from within the runtime_tools application
-%% we can be "sure" that it really exists.
-get_application_vsn() ->
- {value,{_,_,VSN}}=lists:keysearch(runtime_tools,1,application:loaded_applications()),
- VSN.
-%% -----------------------------------------------------------------------------
-
-%% Help function that examines an argument to determine if it is a list of files
-%% or tracerdata. This since they are both complex structures, looking alike.
-%% Returns 'tracerdata', 'files', 'false' or 'error'. Error is returned if it
-%% can not be decided which it is.
-is_list_of_files_or_tracerdata(What) ->
- case inviso_rt_lib:is_tracerdata(What) of
- true ->
- tracerdata;
- false ->
- if
- What==[] ->
- false;
- is_list(What),is_list(hd(What)) ->
- files;
- is_list(What) ->
- case lists:keysearch(trace_log,1,What) of
- {value,_} ->
- files;
- false ->
- case lists:keysearch(ti_log,1,What) of
- {value,_} ->
- files;
- false ->
- error % Neither tracerdata nor list of files.
- end
- end;
- true ->
- error
- end
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function which removes all files in the ListOfFiles, assuming they
-%% are located in Dir.
-%% Returns a list of [{ok,FileName},...{error,Reason},...]
-delete_files(Dir,ListOfFiles) ->
- delete_files_2(Dir,ListOfFiles, []).
-
-delete_files_2(Dir,[File|Tail],Reply) when is_list(Dir),is_list(File) ->
- case catch file:delete(filename:join(Dir,File)) of
- ok ->
- delete_files_2(Dir,Tail,[{ok,File}|Reply]);
- {error,Posix} ->
- delete_files_2(Dir,Tail,[{error,{Posix,File}}|Reply]);
- {'EXIT',_Reason} -> % Probably not proper string.
- delete_files_2(Dir,Tail,[{error,{badarg,[Dir,File]}}|Reply])
- end;
-delete_files_2(Dir,[Faulty|Tail],Reply) ->
- delete_files_2(Dir,Tail,[{error,{badarg,[Dir,Faulty]}}|Reply]);
-delete_files_2(_,[],Reply) ->
- Reply.
-%% -----------------------------------------------------------------------------
-
-%% Help function which lists all trace logs belonging to this tracerdata.
-%% Note that this function operates on internal LogTD structures.
-list_logs_tracelog({file,FileName}) when is_list(FileName) ->
- case file:read_file_info(FileName) of
- {ok,_} -> % The file exists.
- {filename:dirname(FileName),[filename:basename(FileName)]};
- _ -> % The file does not exist
- {filename:dirname(FileName),[]}
- end;
-list_logs_tracelog({file,Wrap}) when is_tuple(Wrap),element(2,Wrap)==wrap ->
- case {element(1,Wrap),element(3,Wrap)} of
- {FileName,Tail} when is_list(FileName),is_list(Tail) ->
- case catch {filename:dirname(FileName),list_wrapset(FileName,Tail)} of
- {'EXIT',_Reason} -> % Garbage in either lists.
- {"",no_log}; % Interpret as no log for tracerdata.
- Tuple ->
- Tuple
- end;
- _ ->
- {"",no_log}
- end;
-list_logs_tracelog(void) -> % Trace log not used.
- {"",no_log};
-list_logs_tracelog(_) -> % Some fun or similar.
- {"",no_log}. % Then there are no files to report.
-%% -----------------------------------------------------------------------------
-
-%% Help function which lists all ti-files belonging to this tracerdata.
-%% Note that this function operates on the internal TiTD structure.
-list_logs_tilog(TiTD)
- when tuple_size(TiTD)>=2,element(1,TiTD)==file,is_list(element(2,TiTD)) ->
- FileName=element(2,TiTD),
- case file:read_file_info(FileName) of
- {ok,_} -> % Yes the file exists.
- {filename:dirname(FileName),[filename:basename(FileName)]};
- _ ->
- {filename:dirname(FileName),[]}
- end;
-list_logs_tilog(void) -> % Internal representation for
- {"",no_log}; % ti-file not in use.
-list_logs_tilog(_) ->
- {"",no_log}.
-%% -----------------------------------------------------------------------------
-
-%% Help function which lists all files belonging to the wrap-set specified by
-%% Prefix and Suffix. Note that there can be a directory in Prefix as well.
-%% Will fail if either of Prefix or Suffix are not proper strings.
-%% Returns a list of files, without dirname.
-list_wrapset(Prefix,Suffix) ->
- Name=filename:basename(Prefix),
- Dirname=filename:dirname(Prefix),
- case file:list_dir(Dirname) of
- {ok,Files} ->
- RegExp="^"++list_wrapset_escapes(Name)++"[0-9]+"++
- list_wrapset_escapes(Suffix)++"$",
- list_wrapset_2(Files,RegExp);
- {error,_Reason} -> % Translate this to no files!
- []
- end.
-
-list_wrapset_2([File|Rest],RegExp) ->
- Length=length(File),
- case re:run(File,RegExp) of
- {match,[{0,Length}]} -> % This is a member of the set.
- [File|list_wrapset_2(Rest,RegExp)];
- _ ->
- list_wrapset_2(Rest,RegExp)
- end;
-list_wrapset_2([],_) ->
- [].
-
-%% Help function which inserts escape characters infront of characters which
-%% will otherwise be missinterpreted by the regexp function as meta rather than
-%% just the character itself.
-list_wrapset_escapes([$.|Rest]) ->
- [$\\,$.|list_wrapset_escapes(Rest)];
-list_wrapset_escapes([Char|Rest]) ->
- [Char|list_wrapset_escapes(Rest)];
-list_wrapset_escapes([]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-
-
-
-
-
-%% ==============================================================================
-%% Handler functions for implementing simple trace-message handlers.
-%% ==============================================================================
-%%
-%% A handler must be a function taking two arguments. The first is the trace-
-%% message. The second is datastructure used by the handler. The handler shall
-%% returns (possibly) new datastructure.
-
-%% ------------------------------------------------------------------------------
-%% Function implementing a relayer. This function is used to creat a fun handler
-%% if the relay option is used in tracer-data.
-%% ------------------------------------------------------------------------------
-relay_handler(Msg,Tracer) ->
- Tracer ! Msg,
- Tracer.
-
-%% ------------------------------------------------------------------------------
-%% Function implementing a default terminal io handler.
-%% ------------------------------------------------------------------------------
-
-dhandler(end_of_trace, Out) ->
- Out;
-dhandler(Trace, Out) when element(1, Trace) == trace,
- tuple_size(Trace) >= 3 ->
- dhandler1(Trace, tuple_size(Trace), Out);
-dhandler(Trace, Out) when element(1, Trace) == trace_ts,
- tuple_size(Trace) >= 4 ->
- dhandler1(Trace, tuple_size(Trace)-1, Out);
-dhandler(Trace, Out) when element(1, Trace) == drop,
- tuple_size(Trace) == 2 ->
- io:format(Out, "*** Dropped ~p messages.~n", [element(2,Trace)]),
- Out;
-dhandler(Trace, Out) when element(1, Trace) == seq_trace,
- tuple_size(Trace) >= 3 ->
- SeqTraceInfo = case Trace of
- {seq_trace, Lbl, STI, TS} ->
- io:format(Out, "SeqTrace ~p [~p]: ",
- [TS, Lbl]),
- STI;
- {seq_trace, Lbl, STI} ->
- io:format(Out, "SeqTrace [~p]: ",
- [Lbl]),
- STI
- end,
- case SeqTraceInfo of
- {send, Ser, Fr, To, Mes} ->
- io:format(Out, "(~p) ~p ! ~p [Serial: ~p]~n",
- [Fr, To, Mes, Ser]);
- {'receive', Ser, Fr, To, Mes} ->
- io:format(Out, "(~p) << ~p [Serial: ~p, From: ~p]~n",
- [To, Mes, Ser, Fr]);
- {print, Ser, Fr, _, Info} ->
- io:format(Out, "-> ~p [Serial: ~p, From: ~p]~n",
- [Info, Ser, Fr]);
- Else ->
- io:format(Out, "~p~n", [Else])
- end,
- Out;
-dhandler(_Trace, Out) ->
- Out.
-
-dhandler1(Trace, Size, Out) ->
-%%%! Self = self(),
- From = element(2, Trace),
- case element(3, Trace) of
- 'receive' ->
- case element(4, Trace) of
- {dbg,ok} -> ok;
- Message -> io:format(Out, "(~p) << ~p~n", [From,Message])
- end;
- 'send' ->
- Message = element(4, Trace),
- case element(5, Trace) of
-%%%! This causes messages to disappear when used by ttb (observer). Tests
-%%%! so far show that there is no difference in results with dbg even if I
-%%%! comment it out, so I hope this is only some old code which isn't
-%%%! needed anymore... /siri
-%%%! Self -> ok;
- To -> io:format(Out, "(~p) ~p ! ~p~n", [From,To,Message])
- end;
- call ->
- case element(4, Trace) of
- MFA when Size == 5 ->
- Message = element(5, Trace),
- io:format(Out, "(~p) call ~s (~p)~n",
- [From,ffunc(MFA),Message]);
- MFA ->
- io:format(Out, "(~p) call ~s~n", [From,ffunc(MFA)])
- end;
- return -> %% To be deleted...
- case element(4, Trace) of
- MFA when Size == 5 ->
- Ret = element(5, Trace),
- io:format(Out, "(~p) old_ret ~s -> ~p~n",
- [From,ffunc(MFA),Ret]);
- MFA ->
- io:format(Out, "(~p) old_ret ~s~n", [From,ffunc(MFA)])
- end;
- return_from ->
- MFA = element(4, Trace),
- Ret = element(5, Trace),
- io:format(Out, "(~p) returned from ~s -> ~p~n",
- [From,ffunc(MFA),Ret]);
- return_to ->
- MFA = element(4, Trace),
- io:format(Out, "(~p) returning to ~s~n", [From,ffunc(MFA)]);
- spawn when Size == 5 ->
- Pid = element(4, Trace),
- MFA = element(5, Trace),
- io:format(Out, "(~p) spawn ~p as ~s~n", [From,Pid,ffunc(MFA)]);
- Op ->
- io:format(Out, "(~p) ~p ~s~n", [From,Op,ftup(Trace,4,Size)])
- end,
- Out.
-
-
-%%% These f* functions returns non-flat strings
-
-%% {M,F,[A1, A2, ..., AN]} -> "M:F(A1, A2, ..., AN)"
-%% {M,F,A} -> "M:F/A"
-ffunc({M,F,Argl}) when is_list(Argl) ->
- io_lib:format("~p:~p(~s)", [M, F, fargs(Argl)]);
-ffunc({M,F,Arity}) ->
- io_lib:format("~p:~p/~p", [M,F,Arity]);
-ffunc(X) -> io_lib:format("~p", [X]).
-
-%% Integer -> "Integer"
-%% [A1, A2, ..., AN] -> "A1, A2, ..., AN"
-fargs(Arity) when is_integer(Arity) -> integer_to_list(Arity);
-fargs([]) -> [];
-fargs([A]) -> io_lib:format("~p", [A]); %% last arg
-fargs([A|Args]) -> [io_lib:format("~p,", [A]) | fargs(Args)];
-fargs(A) -> io_lib:format("~p", [A]). % last or only arg
-
-%% {A_1, A_2, ..., A_N} -> "A_Index A_Index+1 ... A_Size"
-ftup(Trace, Index, Index) ->
- io_lib:format("~p", [element(Index, Trace)]);
-ftup(Trace, Index, Size) ->
- [io_lib:format("~p ", [element(Index, Trace)])
- | ftup(Trace, Index+1, Size)].
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Functions handling the trace-port. Copied from dbg.erl
-%% ==============================================================================
-
-trace_port_control(Port, flush) ->
- case trace_port_control(Port, $f, "") of
- {ok, [0]} -> ok;
- {ok, _} -> {error, not_supported_by_trace_driver};
- Other -> Other
- end.
-
-trace_port_control(Port, Command, Arg) when is_port(Port)->
- case catch port_control(Port, Command, Arg) of
- {'EXIT', _} -> {error, {no_trace_driver, node()}};
- Result -> Result
- end.
-
-
-trace_port(file, {Filename, wrap, Tail}) ->
- trace_port(file, {Filename, wrap, Tail, 128*1024});
-trace_port(file, {Filename, wrap, Tail, WrapSize}) ->
- trace_port(file, {Filename, wrap, Tail, WrapSize, 8});
-trace_port(file, {Filename, wrap, Tail, WrapSize, WrapCnt})
- when is_list(Tail),
- is_integer(WrapSize), WrapSize >= 0, WrapSize < (1 bsl 32),
- is_integer(WrapCnt), WrapCnt >= 1, WrapCnt < (1 bsl 32) ->
- trace_port1(file, Filename, {wrap, Tail, WrapSize, WrapCnt, 0});
-trace_port(file, {Filename, wrap, Tail, {time, WrapTime}, WrapCnt})
- when is_list(Tail),
- is_integer(WrapTime), WrapTime >= 1, WrapTime < (1 bsl 32),
- is_integer(WrapCnt), WrapCnt >= 1, WrapCnt < (1 bsl 32) ->
- trace_port1(file, Filename, {wrap, Tail, 0, WrapCnt, WrapTime});
-trace_port(file, Filename) when is_list(Filename) ->
- trace_port1(file, Filename, nowrap);
-
-trace_port(ip, Portno) when is_integer(Portno) ->
- trace_port(ip,{Portno,50});
-
-trace_port(ip, {Portno, Qsiz}) when is_integer(Portno), is_integer(Qsiz) ->
- fun() ->
- Driver = "trace_ip_drv",
- Dir1 = filename:join(code:priv_dir(runtime_tools), "lib"),
- case catch erl_ddll:load_driver(Dir1, Driver) of
- ok ->
- ok;
- _ ->
- Dir2 = filename:join(
- Dir1,
- erlang:system_info(system_architecture)),
- catch erl_ddll:load_driver(Dir2, Driver)
- end,
- L = lists:flatten(
- io_lib:format("~s ~p ~p 2",
- [Driver, Portno, Qsiz])),
- open_port({spawn, L}, [eof])
- end.
-
-trace_port1(file, Filename, Options) ->
- Driver = "trace_file_drv",
- fun() ->
- Name = filename:absname(Filename),
- %% Absname is needed since the driver uses
- %% the supplied name without further investigations,
- %% and if the name is relative the resulting path
- %% might be too long which can cause a bus error
- %% on vxworks instead of a nice error code return.
- %% Also, the absname must be found inside the fun,
- %% in case the actual node where the port shall be
- %% started is on another node (or even another host)
- {Wrap, Tail} =
- case Options of
- {wrap, T, WrapSize, WrapCnt, WrapTime} ->
- {lists:flatten(
- io_lib:format("w ~p ~p ~p ~p ",
- [WrapSize, WrapCnt, WrapTime,
- length(Name)])),
- T};
- nowrap ->
- {"", ""}
- end,
- Command = Driver ++ " " ++ Wrap ++ "n " ++ Name ++ Tail,
- Dir1 = filename:join(code:priv_dir(runtime_tools), "lib"),
- case catch erl_ddll:load_driver(Dir1, Driver) of
- ok ->
- ok;
- _ ->
- Dir2 = filename:join(
- Dir1,
- erlang:system_info(system_architecture)),
- catch erl_ddll:load_driver(Dir2, Driver)
- end,
- if element(1, Options) == wrap ->
- %% Delete all files from any previous wrap log
- Files = wrap_postsort(wrap_presort(Name, Tail)),
- lists:foreach(
- fun(N) -> file:delete(N) end,
- Files);
- true -> ok
- end,
- open_port({spawn, Command}, [eof])
- end.
-
-%% Find all possible wrap log files.
-%% Returns: a list of sort converted filenames.
-%%
-%% The sort conversion is done by extracting the wrap sequence counter
-%% from the filename, and calling wrap_encode/2.
-wrap_presort(Filename, Tail) ->
- Name = filename:basename(Filename),
- Dirname = filename:dirname(Filename),
- case file:list_dir(Dirname) of
- {ok, Files} ->
- lists:zf(
- fun(N) ->
- case match_front(N, Name) of
- false ->
- false;
- X ->
- case match_rear(X, Tail) of
- false ->
- false;
- C -> % Counter
- case match_0_9(C) of
- true ->
- {true,
-% filename:join(Dirname, N)}
- wrap_encode(
- filename:join(Dirname, N),
- C)};
- false ->
- false
- end
- end
- end
- end,
- Files);
- _ ->
- []
- end.
-
-%% Extract the filenames from a list of sort converted ones.
-wrap_postsort(Files) ->
- lists:map(fun wrap_name/1, Files).
-
-wrap_encode(N, C) ->
- {list_to_integer(C), N}.
-
-wrap_name({_C, N}) ->
- N.
-
-%% Returns what is left of ListA when removing all matching
-%% elements from ListB, or false if some element did not match,
-%% or if ListA runs out of elements before ListB.
-match_front(ListA, []) when is_list(ListA) ->
- ListA;
-match_front([], ListB) when is_list(ListB) ->
- false;
-match_front([Hd|TlA], [Hd|TlB]) ->
- match_front(TlA,TlB);
-match_front([_HdA|_], [_HdB|_]) ->
- false.
-
-%% Reversed version of match_front/2
-match_rear(ListA, ListB) when is_list(ListA), is_list(ListB) ->
- case match_front(lists:reverse(ListA), lists:reverse(ListB)) of
- false ->
- false;
- List ->
- lists:reverse(List)
- end.
-
-%% Returns true if the non-empty list arguments contains all
-%% characters $0 .. $9.
-match_0_9([]) ->
- false;
-match_0_9([H]) when is_integer(H), $0 =< H, H =< $9 ->
- true;
-match_0_9([H|T]) when is_integer(H), $0 =< H, H =< $9 ->
- match_0_9(T);
-match_0_9(L) when is_list(L) ->
- false.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Functions working on the tracerdata structure.
-%% -----------------------------------------------------------------------------
-
-%% Tracerdata is the structure which specifies to where tracing is logged at this
-%% runtime component. It may now (and in the future specify) several things.
-%% Currently it can consist of:
-%% LogTD: specifying how trace-log data shall be handled.
-%% TiTD : trace information, specifying how trace information shall be handled.
-%%
-%% Tracerdata may also contain quick or standard forms of LogTD and/or TiTD.
-%% For instance if a standard handler-fun shall be used. The handler fun is not
-%% part of the tracerdata but rather specified by a constant.
-
-
-%% Help function that translates an input-tracerdata to useful internal formats.
-%% This since the tracerdata may consist of specifications which shall be
-%% translated into funs or similar.
-%% Returns {ok,LogTD,TiTD} or {error,Reason}.
-%% Note that TiTD may be 'void' since TiTD is not mandatory.
-translate_td(TracerData) when is_list(TracerData) -> % Both log and ti.
- case translate_td_logtd(get_trace_log_tracerdata(TracerData)) of
- {ok,LogTD} ->
- case translate_td_titd(get_ti_log_tracerdata(TracerData)) of
- {ok,TiTD} ->
- {ok,LogTD,TiTD};
- {error,Reason} ->
- {error,Reason}
- end;
- {error,Reason} ->
- {error,Reason}
- end;
-translate_td(TracerData) -> % The it is just LogTD!?
- case translate_td_logtd(TracerData) of
- {ok,LogTD} ->
- {ok,LogTD,void};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function translating trace-log tracerdata.
-translate_td_logtd(collector) -> % This rt will act as receiver.
- {ok,{fun dhandler/2,user}}; % Simple terminal io.
-translate_td_logtd({relayer,Tracer}) when is_pid(Tracer) ->
- {ok,{fun relay_handler/2,Tracer}}; % Relay trace-msg to Tracer-pid.
-translate_td_logtd({HandlerFun,Data}) when is_function(HandlerFun) ->
- {ok,{HandlerFun,Data}}; % Own invented fun.
-translate_td_logtd({Type,Parameters}) when Type==ip;Type==file ->
- {ok,{Type,Parameters}}; % Built in trace-port
-translate_td_logtd(false) -> % Unusual but no trace log.
- {ok,void};
-translate_td_logtd(Arg) ->
- {error,{bad_log_td,Arg}}.
-%% -----------------------------------------------------------------------------
-
-%% Help function translating ti-log tracerdata.
-translate_td_titd(TiTD={file,FileName}) when is_list(FileName) ->
- {ok,TiTD};
-translate_td_titd({file,FileName,
- {InitPublLDmfa={M1,F1,L1},
- RemovePublLDmf={M2,F2},
- CleanPublLDmf={M3,F3}}})
- when is_list(FileName),is_atom(M1),is_atom(F1),is_atom(M2),is_atom(F2),is_list(L1),is_atom(M3),is_atom(F3) ->
- {ok,{file,FileName,{InitPublLDmfa,RemovePublLDmf,CleanPublLDmf}}};
-translate_td_titd({file,FileName,
- {InitPublLDmfa={M1,F1,L1},
- void,
- CleanPublLDmf={M3,F3}}})
- when is_list(FileName),is_atom(M1),is_atom(F1),is_list(L1),is_atom(M3),is_atom(F3) ->
- {ok,{file,FileName,{InitPublLDmfa,void,CleanPublLDmf}}};
-translate_td_titd(false) -> % Means no ti-tracerdata.
- {ok,void};
-translate_td_titd(TiTD) ->
- {error,{bad_ti_td,TiTD}}.
-%% -----------------------------------------------------------------------------
-
-%% This function retrieves the trace-log part of a TracerData list structure.
-%% Returns TraceLogTD or 'false'.
-get_trace_log_tracerdata(TracerData) ->
- case lists:keysearch(trace,1,TracerData) of
- {value,{_,LogTD}} ->
- LogTD;
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-%% This function retrieves the ti-log part of a TracerData list structure.
-%% Returns TiLogTD or 'false'.
-get_ti_log_tracerdata(TracerData) ->
- case lists:keysearch(ti,1,TracerData) of
- {value,{_,TiTD}} ->
- TiTD;
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function which checks that parameters to the built in trace-port are
-%% sane.
-check_traceport_parameters(Type,Args) ->
- case {Type,Args} of
- {file,{FileName,wrap,Tail}} when is_list(FileName),is_list(Tail) ->
- ok;
- {file,{FileName,wrap,Tail,WrapSize}}
- when is_list(FileName),
- is_list(Tail),
- is_integer(WrapSize),WrapSize>=0,WrapSize< (1 bsl 32) ->
- ok;
- {file,{FileName,wrap,Tail,WrapSize,WrapCnt}}
- when is_list(FileName),is_list(Tail),
- is_integer(WrapSize), WrapSize >= 0, WrapSize < (1 bsl 32),
- is_integer(WrapCnt), WrapCnt >= 1, WrapCnt < (1 bsl 32) ->
- ok;
- {file,{FileName,wrap,Tail,{time,WrapTime},WrapCnt}}
- when is_list(FileName),is_list(Tail),
- is_integer(WrapTime), WrapTime >= 1, WrapTime < (1 bsl 32),
- is_integer(WrapCnt), WrapCnt >= 1, WrapCnt < (1 bsl 32) ->
- ok;
- {file,FileName} when is_list(FileName) ->
- ok;
- {ip,Portno} when is_integer(Portno),Portno=<16#FFFF ->
- ok;
- {ip,{Portno,Qsiz}} when is_integer(Portno),Portno=<16#FFFF,is_integer(Qsiz) ->
- ok;
- _ ->
- {error,{trace_port_args,[Type,Args]}}
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Default overload functionality.
-%% -----------------------------------------------------------------------------
-
-%% A default overload protection function. An overload function must take
-%% one argument and return 'ok' or {suspend,SuspendReason}.
-default_overload_func(_) ->
- case process_info(self(),message_queue_len) of
- {message_queue_len,N} when N > 1000 ->
- {suspend,rt_max_queue_len};
- _ ->
- ok
- end.
-%% -----------------------------------------------------------------------------
-
-%% =============================================================================
-%% Functions working on the internal loopdata structure.
-%% =============================================================================
-
-%% Help function simply adding Fetcher as a fetcher process to the loopdata.
-%% Returns a new loopdata structure.
-add_fetcher_ld(Fetcher,LD) ->
- LD#rt{fetchers=[Fetcher|LD#rt.fetchers]}.
-%% -----------------------------------------------------------------------------
-
-%% Help function investigating if the first argument is a known fetcher process
-%% or not. If it is, it also removed it from the fetchers list in the loopdata
-%% structure.
-%% Returns {true,NewLoopData} or 'false'.
-remove_fetcher_ld(Fetcher,LD) ->
- NewFetchers=lists:delete(Fetcher,LD#rt.fetchers),
- if
- NewFetchers/=LD#rt.fetchers ->
- {true,LD#rt{fetchers=NewFetchers}};
- true -> % No it was not a fetcher process.
- false
- end.
-%% -----------------------------------------------------------------------------
-
-%%% end of file
-
diff --git a/lib/runtime_tools/src/inviso_rt_lib.erl b/lib/runtime_tools/src/inviso_rt_lib.erl
deleted file mode 100644
index 5dfe14068a..0000000000
--- a/lib/runtime_tools/src/inviso_rt_lib.erl
+++ /dev/null
@@ -1,474 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2011. 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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%% ------------------------------------------------------------------------------
-%% File : inviso_rt_lib.erl
-%% Author : Lennart �hman <[email protected]>
-%% Description :
-%%
-%% Created : 27 Sep 2005 by Lennart �hman <[email protected]>
-%% ------------------------------------------------------------------------------
--module(inviso_rt_lib).
-
--export([expand_regexp/2,expand_regexp/3,expand_regexp/4]).
--export([is_tracerdata/1]).
--export([transform/2]).
-
--export([rpc/4,rpc/5,match_modules/2,match_modules/3]).
--export([debug/3]).
-
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Exported API functions.
-%% ==============================================================================
-
-%% ------------------------------------------------------------------------------
-%% expand_regexp(Nodes,RegExpDir,RegExpMod,Opts) = [{Node,Answer},...] | {error,Reason}
-%% expand_regexp(Nodes,RegExpMod,Opts) = [{Node,Answer},...] | {error,Reason}
-%% expand_regexp(RegExpDir,RegExpMod,Opts) = ListOfModules | {error,Reason}
-%% expand_regexp(RegExpMod,Opts) = ListOfModules | {error,Reason}
-%% Nodes=List of all nodes (atoms) where to expand.
-%% RegExpDir=Reg.exp (string) specifying directories.
-%% RegExpMod=Reg.exp (string) specifying module names.
-%% Node=node name (atom).
-%% Opts=[Opt,...]
-%% Opt=only_loaded
-%% Answer=List of modules (atoms) | 'badrpc'
-%%
-%% Expands, concurrently, the regular expression on Nodes and returns a list
-%% of what modules it expanded to on the different nodes. Note that it may
-%% differ between Erlang nodes depending on whether the modules are the same
-%% or not. Also note that all modules becomes loaded as a result.
-%% RegExpDir can further limit the modules. It introduces the requirement that
-%% a module must be loaded from a directory with a path satisfying the RegExpDir.
-%% All regular expression are according to the standard lib regexp module.
-expand_regexp(RegExpMod,Opts) when is_list(RegExpMod),is_list(Opts) ->
- match_modules(RegExpMod,Opts);
-expand_regexp(RegExpMod,Opts) ->
- {error,{badarg,[RegExpMod,Opts]}}.
-expand_regexp(NodesOrRegExpDir,RegExpMod,Opts)
- when is_list(NodesOrRegExpDir),is_list(RegExpMod),is_list(Opts) ->
- case is_list_of_atoms(NodesOrRegExpDir) of
- true -> % Interpret as list of nodes.
- lists:foreach(fun(N)->spawn(?MODULE,rpc,[self(),N,RegExpMod,Opts]) end,
- NodesOrRegExpDir),
- expand_regexp_answers(NodesOrRegExpDir,[]);
- false -> % Interpret as a string.
- match_modules(NodesOrRegExpDir,RegExpMod,Opts)
- end;
-expand_regexp(NodesOrRegExpDir,RegExpMod,Opts) ->
- {error,{badarg,[NodesOrRegExpDir,RegExpMod,Opts]}}.
-expand_regexp(Nodes,RegExpDir,RegExpMod,Opts)
- when is_list(Nodes),is_list(RegExpDir),is_list(RegExpMod),is_list(Opts) ->
- lists:foreach(fun(N)->
- spawn(?MODULE,rpc,[self(),N,RegExpDir,RegExpMod,Opts])
- end,
- Nodes),
- expand_regexp_answers(Nodes,[]);
-expand_regexp(Nodes,RegExpDir,RegExpMod,Opts) ->
- {error,{badarg,[Nodes,RegExpDir,RegExpMod,Opts]}}.
-
-expand_regexp_answers([],Answers) -> Answers; % List of [{Node,Answer},...].
-expand_regexp_answers(Nodes,Answers) ->
- receive
- {?MODULE,Node,Answer} ->
- expand_regexp_answers(lists:delete(Node,Nodes),[{Node,Answer}|Answers])
- end.
-%% ------------------------------------------------------------------------------
-
-%% is_tracerdata(TracerData)=true|false
-%% Answers the question if TracerData is proper tracerdata. Note that true can be
-%% returned if it resembles tracerdata very closely.
-is_tracerdata({Fun,_Data}) when is_function(Fun) -> true;
-is_tracerdata({relayer,To}) when is_pid(To);is_atom(To) -> true;
-is_tracerdata(collector) -> true;
-is_tracerdata({file,Param}) when is_tuple(Param);is_list(Param) -> true;
-is_tracerdata({ip,_Param}) -> true;
-is_tracerdata([{trace,LogTD}|Rest]) ->
- case is_tracerdata(LogTD) of
- true ->
- is_tracerdata(Rest);
- false ->
- false
- end;
-is_tracerdata([{ti,TiData}|Rest]) ->
- case is_tidata(TiData) of
- true ->
- is_tracerdata(Rest);
- false ->
- false
- end;
-is_tracerdata([]) ->
- true;
-is_tracerdata(_) ->
- false.
-
-is_tidata({file,FileName}) when is_list(FileName) -> true;
-is_tidata({file,FileName,{M,F,Args}}) when is_list(FileName),is_atom(M),is_atom(F),is_list(Args) ->
- true;
-is_tidata(_) -> false.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Help functions.
-%% ==============================================================================
-
-%% Help function intended to be run in its own process. Will report with
-%% a message when done.
-%% This function will be spawned on.
-rpc(Parent,Node,RegExpMod,Opts) ->
- case rpc:call(Node,?MODULE,match_modules,[RegExpMod,Opts]) of
- {badrpc,_Reason} -> % The node is probably not healthy.
- Parent ! {?MODULE,Node,badrpc};
- Modules ->
- Parent ! {?MODULE,Node,Modules}
- end.
-
-rpc(Parent,Node,RegExpDir,RegExpMod,Opts) ->
- case rpc:call(Node,?MODULE,match_modules,[RegExpDir,RegExpMod,Opts]) of
- {badrpc,_Reason} -> % The node is probably not healthy.
- Parent ! {?MODULE,Node,badrpc};
- Modules ->
- Parent ! {?MODULE,Node,Modules}
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Exported function which actually shall be in code.erl.
-%% ==============================================================================
-
-%% match_modules(RegExpMod,Actions) = [Module,...] | {error,Reason}
-%% match_modules(RegExpDir,RegExpMod,Actions)=[Module,...] | {error,Reason}
-%% RegExpMod=Erlang regular expression describing module names (string).
-%% RegExpDir=Erlang regular expression describing directory paths(string) |
-%% void
-%% Actions=List of;'only_loaded'.
-%%
-%% Function which matches a regular expresion against module names. The function
-%% can also match the directory from where the module is loaded or will be loaded
-%% against a regular expresion for directory paths.
-%% The function uses the same strategy as code-loading if the same module is
-%% discovered in several places.
-%% (1) An already loaded module shadows all other occurancies.
-%% (2) .beams found in by a path shadows .beams found by paths later in the
-%% code paths.
-%%
-%% Description of actions:
-%% only_loaded: Only consider modules which are loaded.
-match_modules(RegExpMod,Actions) ->
- match_modules(void,RegExpMod,Actions).
-match_modules(RegExpDir,RegExpMod,Actions) ->
- AllLoaded=code:all_loaded(),
- Mods1=handle_expand_regexp_2(AllLoaded,RegExpDir,RegExpMod,[]),
- case lists:member(only_loaded,Actions) of % Shall we do not loaded too?
- false -> % Ok, search all paths too then.
- Paths=code:get_path(),
- handle_expand_regexp_3(Paths,RegExpDir,RegExpMod,AllLoaded,Mods1);
- true -> % Only loaded modules then.
- Mods1
- end.
-
-
-%% Help function which traverses all loaded modules and determines
-%% which shall be returned. First we check that the module satisfies the
-%% module-regexp. Then we, if a dir reg-exp is given, checks that the
-%% module is loaded from an approved path. Note that if it can not be
-%% determined from where it was loaded (like preloaded or cover-compiled
-%% etc), but dir reg-exps are used. That module will be excluded.
-%% Returns a list of modules.
-handle_expand_regexp_2([{Mod,Path}|Rest],RegExpDir,RegExpMod,Result) ->
- ModStr=atom_to_list(Mod),
- ModLen=length(ModStr),
- case re:run(ModStr,RegExpMod) of
- {match,[{0,ModLen}]} -> % Ok, The regexp matches the module.
- if
- is_list(RegExpDir),is_atom(Path) -> % Preloaded or covercompiled...
- handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,Result);
- is_list(RegExpDir),is_list(Path) -> % Dir reg-exp is used!
- PathOnly=filename:dirname(Path), % Must remove beam-file name.
- case re:run(PathOnly,RegExpDir,[{capture,none}]) of
- match -> % Did find a match, that is enough!
- handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,[Mod|Result]);
- _ -> % Either error or nomatch.
- handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,Result)
- end;
- true -> % Otherwise already done!
- handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,[Mod|Result])
- end;
- _ -> % Then Mod is not part of the set.
- handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,Result)
- end;
-handle_expand_regexp_2([],_,_,Result) -> Result.
-
-%% Help function which traverses all paths and looks for modules satisfying
-%% the module reg.exp.
-%% Returns a list of unique module names.
-handle_expand_regexp_3([Path|Rest],RegExpDir,RegExpMod,AllLoaded,Result) ->
- if
- is_list(RegExpDir) -> % We must consider the directory name.
- AbsPath=
- case filename:pathtype(Path) of
- absolute -> % Is already abs.
- Path;
- relative -> % Then it must be made absolute.
- filename:absname(Path);
- volumerelative -> % Only on Windows!?
- filename:absname(Path)
- end,
- case re:run(AbsPath,RegExpDir,[{capture,none}]) of
- match -> % Ok, the directory is allowed.
- NewResult=handle_expand_regexp_3_1(Path,RegExpMod,AllLoaded,Result),
- handle_expand_regexp_3(Rest,RegExpDir,RegExpMod,AllLoaded,NewResult);
- _ -> % This directory does not qualify.
- handle_expand_regexp_3(Rest,RegExpDir,RegExpMod,AllLoaded,Result)
- end;
- true -> % RegExpDir is not used!
- NewResult=handle_expand_regexp_3_1(Path,RegExpMod,AllLoaded,Result),
- handle_expand_regexp_3(Rest,RegExpDir,RegExpMod,AllLoaded,NewResult)
- end;
-handle_expand_regexp_3([],_,_,_,Result) -> Result.
-
-handle_expand_regexp_3_1(Path,RegExpMod,AllLoaded,Result) ->
- case file:list_dir(Path) of
- {ok,FileNames} ->
- handle_expand_regexp_3_2(FileNames,RegExpMod,AllLoaded,Result);
- {error,_Reason} -> % Bad path!? Skip it.
- Result
- end.
-
-handle_expand_regexp_3_2([File|Rest],RegExpMod,AllLoaded,Result) ->
- case filename:extension(File) of
- ".beam" -> % It is a beam-file. Consider it!
- ModStr=filename:basename(File,".beam"),
- Mod=list_to_atom(ModStr),
- case {lists:keysearch(Mod,1,AllLoaded),lists:member(Mod,Result)} of
- {false,false} -> % This module is not tried before.
- ModLen=length(ModStr),
- case re:run(ModStr,RegExpMod) of
- {match,[{0,ModLen}]} -> % This module satisfies the regexp.
- handle_expand_regexp_3_2(Rest,RegExpMod,AllLoaded,[Mod|Result]);
- _ -> % Error or not perfect match.
- handle_expand_regexp_3_2(Rest,RegExpMod,AllLoaded,Result)
- end;
- {_,_} -> % This module is already tested.
- handle_expand_regexp_3_2(Rest,RegExpMod,AllLoaded,Result)
- end;
- _ -> % Not a beam-file, skip it.
- handle_expand_regexp_3_2(Rest,RegExpMod,AllLoaded,Result)
- end;
-handle_expand_regexp_3_2([],_,_,Result) -> Result.
-%% ------------------------------------------------------------------------------
-
-%% Help function which finds out if its argument is a list of zero or more
-%% atoms.
-%% Returns 'true' or 'false'.
-is_list_of_atoms([A|Rest]) when is_atom(A) ->
- is_list_of_atoms(Rest);
-is_list_of_atoms([_|_]) ->
- false;
-is_list_of_atoms([]) ->
- true.
-%% ------------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Functions transforming function calls in trace-case file.
-%% =============================================================================
-
-%% transform(Exprs,Translations)=NewExprs
-%% Exprs=list(); List of abstract format erlang terms, as returned by
-%% io:parse_erl_exprs/2.
-%% Translations=list(); List of translations from function calls to other
-%% function calls. [{Mod,Func,Arity,{NewMod,NewFunc,ParamTransformMF}},...]
-%% Mod can actually be omitted, ParamTransformMF shall be {M,F} where F is
-%% a function taking one argument (the parameter list), and returning the
-%% new parameter list. It can also be anything else should no transformation
-%% of the parameters be the case.
-%%
-%% Function that transforms function calls in a trace-case file. The transform/2
-%% can only transform shallow function calls. I.e where both module and function
-%% name are specified as atoms. Any binding-environment is not examined.
-transform([Expr|Rest],Translations) ->
- [transform_2(Expr,Translations)|transform(Rest,Translations)];
-transform([],_) ->
- [].
-
-%% Help function handling a single expr.
-transform_2({call,L1,{remote,L2,ModExpr,FuncExpr},Params},Translations) ->
- case transform_2(ModExpr,Translations) of
- {atom,L3,M} ->
- case transform_2(FuncExpr,Translations) of
- {atom,L4,F} -> % Now we have a M:F/Arity!
- case do_call_translation(M,F,Params,Translations) of
- {ok,NewM,NewF,NewP} ->
- NewParams=transform(NewP,Translations),
- {call,L1,{remote,L2,{atom,L3,NewM},{atom,L4,NewF}},NewParams};
- false -> % No translation or faulty.
- NewParams=transform(Params,Translations),
- {call,L1,{remote,L2,ModExpr,FuncExpr},NewParams}
- end;
- NewFuncExpr -> % Not translated to a shallow term.
- NewParams=transform(Params,Translations),
- {call,L1,{remote,L2,ModExpr,NewFuncExpr},NewParams}
- end;
- NewModExpr -> % Not translated to a shallow term.
- NewFuncExpr=transform_2(FuncExpr,Translations),
- NewParams=transform(Params,Translations),
- {call,L1,{remote,L2,NewModExpr,NewFuncExpr},NewParams}
- end;
-transform_2({call,L1,FuncExpr,Params},Translations) ->
- case transform_2(FuncExpr,Translations) of
- {atom,L3,F} -> % Now we have a M:F/Arity!
- case do_call_translation(F,Params,Translations) of
- {ok,NewM,NewF,NewP} -> % It is turned into a global call.
- NewParams=transform(NewP,Translations),
- {call,L1,{remote,L1,{atom,L3,NewM},{atom,L3,NewF}},NewParams};
- false -> % No translation or faulty.
- NewParams=transform(Params,Translations),
- {call,L1,FuncExpr,NewParams}
- end;
- NewFuncExpr -> % Not translated to a shallow term.
- NewParams=transform(Params,Translations),
- {call,L1,NewFuncExpr,NewParams}
- end;
-transform_2({match,L,P,E},Translations) ->
- NewPattern=transform_2(P,Translations),
- NewExpr=transform_2(E,Translations),
- {match,L,NewPattern,NewExpr};
-transform_2({op,L,Op,Arg1,Arg2},Translations) ->
- NewArg1=transform_2(Arg1,Translations),
- NewArg2=transform_2(Arg2,Translations),
- {op,L,Op,NewArg1,NewArg2};
-transform_2({op,L,Op,Arg},Translations) ->
- NewArg=transform_2(Arg,Translations),
- {op,L,Op,NewArg};
-transform_2({block,L,Body},Translations) ->
- NewBody=transform(Body,Translations),
- {block,L,NewBody};
-transform_2({'if',L,Clauses},Translations) ->
- NewClauses=transform_clauses(Clauses,Translations),
- {'if',L,NewClauses};
-transform_2({'case',L,Func,Clauses},Translations) ->
- NewFunc=transform_2(Func,Translations),
- NewClauses=transform_clauses(Clauses,Translations),
- {'case',L,NewFunc,NewClauses};
-transform_2({'fun',L,{clauses,Clauses}},Translations) ->
- NewClauses=transform_clauses(Clauses,Translations),
- {'fun',L,NewClauses};
-transform_2({lc,L,Items,GeneratorsFilters},Translations) ->
- NewItem=transform_2(Items,Translations),
- NewGensAndFilters=transform_gensandfilters(GeneratorsFilters,Translations),
- {lc,L,NewItem,NewGensAndFilters};
-transform_2({'catch',L,Expr},Translations) ->
- NewExpr=transform_2(Expr,Translations),
- {'catch',L,NewExpr};
-transform_2({tuple,L,Elements},Translations) ->
- NewElements=transform(Elements,Translations),
- {tuple,L,NewElements};
-transform_2({cons,L,Element,Tail},Translations) ->
- NewElement=transform_2(Element,Translations),
- NewTail=transform_2(Tail,Translations),
- {cons,L,NewElement,NewTail};
-transform_2({nil,L},_) ->
- {nil,L};
-transform_2({bin,L,Elements},Translations) ->
- NewElements=transform_binary(Elements,Translations),
- {bin,L,NewElements};
-transform_2(Expr,_) -> % Can be a var for instance.
- Expr.
-
-transform_binary([{bin_element,L,Val,Size,TSL}|Rest],Translations) ->
- NewVal=transform_2(Val,Translations),
- NewSize=transform_2(Size,Translations),
- [{bin_element,L,NewVal,NewSize,TSL}|transform_binary(Rest,Translations)];
-transform_binary([],_) ->
- [].
-
-transform_clauses([{clause,L,Pattern,Guards,Body}|Rest],Translations) ->
- NewPattern=transform(Pattern,Translations),
- NewBody=transform(Body,Translations),
- [{clause,L,NewPattern,Guards,NewBody}|transform_clauses(Rest,Translations)];
-transform_clauses([],_Translations) ->
- [].
-
-transform_gensandfilters([{generator,L,Pattern,Exprs}|Rest],Translations) ->
- NewExprs=transform(Exprs,Translations),
- [{generator,L,Pattern,NewExprs}|transform_gensandfilters(Rest,Translations)];
-transform_gensandfilters([Expr|Rest],Translations) ->
- [transform_2(Expr,Translations)|transform_gensandfilters(Rest,Translations)];
-transform_gensandfilters([],_) ->
- [].
-%% ------------------------------------------------------------------------------
-
-%% This is the heart of the translation functionality. Here we actually try to
-%% replace calls to certain functions with other calls. This can include removing
-%% arguments.
-do_call_translation(M,F,Params,Translations) ->
- case lists:keysearch({M,F,length(Params)},1,Translations) of
- {value,{_,{NewM,NewF,ArgFun}}} -> % Lets transform the function.
- do_call_translation_2(Params,NewM,NewF,ArgFun);
- _ ->
- false % No translations at all.
- end.
-do_call_translation(F,Params,Translations) ->
- case lists:keysearch({F,length(Params)},1,Translations) of
- {value,{_,{NewM,NewF,ArgFun}}} -> % Lets transform the function.
- do_call_translation_2(Params,NewM,NewF,ArgFun);
- _ ->
- false % No translations at all.
- end.
-
-do_call_translation_2(Params,NewM,NewF,ArgFun) ->
- case ArgFun of
- {M,F} when is_atom(M),is_atom(F) ->
- case catch M:F(Params) of
- {'EXIT',_Reason} ->
- false; % If it does not work, skipp it.
- MungedParams when is_list(MungedParams) ->
- {ok,NewM,NewF,MungedParams};
- _ ->
- false
- end;
- _ -> % No munging of parameters.
- {ok,NewM,NewF,Params}
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Functions for the runtime component internal debugging system.
-%% =============================================================================
-
-%% The debug system is meant to provide tracing of ttb at different levels.
-%%
-%% debug(What,Level,Description) -> nothing significant.
-%% What : controls what kind of event. This can both be certain parts of ttb
-%% as well as certain levels (info to catastrophy).
-%% Level: Determines if What shall be printed or not.
-%% Description: this is what happend.
-debug(off,_What,_Description) ->
- true; % Debug is off, no action.
-debug(On,What,Description) ->
- debug_2(On,What,Description).
-
-debug_2(_,What,Description) ->
- io:format("INVISO DEBUG:~w, ~p~n",[What,Description]).
-%% -----------------------------------------------------------------------------
diff --git a/lib/runtime_tools/src/inviso_rt_meta.erl b/lib/runtime_tools/src/inviso_rt_meta.erl
deleted file mode 100644
index 6865dc2242..0000000000
--- a/lib/runtime_tools/src/inviso_rt_meta.erl
+++ /dev/null
@@ -1,1207 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. 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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%% Author: Lennart �hman, [email protected]
-%%
-%% This module implements the meta tracer process belonging to the
-%% runtime component. Its main purpose is to write the ti-file (traceinformation).
-%% The ti-file contains translations between process id:s and what ever "you"
-%% want to read in the merged and formatted logfile.
-%% This process interacts with the runtime component process.
-%%
-%% Currently it handles the following types of ti-files:
-%% Plain raw, binary log.
-%% Relay to other inviso_rt_meta process on another node.
-%%
-%% The TI file will be on binary format and each entry is:
-%% <<LengthIndicator:32, {Pid,Alias,Op,NowStamp} >>
-%% Pid=pid(), or if OP==unalias pid()|any_other_than_pid()
-%% Op=alias|unalias
-%% -----------------------------------------------------------------------------
--module(inviso_rt_meta).
-
-%% -----------------------------------------------------------------------------
-%% API exports.
-%% -----------------------------------------------------------------------------
-
--export([start/2,start/5]).
--export([stop/1,suspend/1]).
--export([init_tpm/5,init_tpm/8]).
--export([tpm/5,tpm/6,tpm/9,tpm_tracer/5,tpm_tracer/6,tpm_tracer/9]).
--export([tpm_ms/6,tpm_ms_tracer/6,ctpm_ms/5,ctpm/4]).
--export([local_register/1,global_register/1]).
--export([remove_local_register/1,remove_global_register/1]).
-
--export([write_ti/1]).
-
--export([get_tracer/0,tpm_ms/5,tpm_ms_tracer/5,list_tpm_ms/3,ctpm_ms/4]).
-
--export([metacast_call/5,metacast_return_from/6]).
--export([get_state/1]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Internal exports.
-%% -----------------------------------------------------------------------------
-
--export([init/6]).
--export([init_std_publld/2,clean_std_publld/1]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Constants.
-%% -----------------------------------------------------------------------------
-
--define(NAMED_MS_TAB,inviso_rt_meta_named_ms).
-
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Exported API (Meant to be used by a runtime component).
-%% =============================================================================
-
-%% start(TiData,Tracer)={ok,Pid} | {error,Reason}
-%% start(TiData,Tracer,InitPublLDmfa,RemovePublLDmfa,CleanPublLDmf)=
-%% {ok,Pid} | {error,Reason}
-%% TiData={file,FileName}|{relay,Node}
-%% Tracer=pid()|port()
-%% FileName=string()
-%% InitPublLDmfa={Mod,Func,ArgList}
-%% RemovePublLDmf={Mod,Func} | void
-%% RemovePublLDmf(PublLD)->nothing significant.
-%% These functions are called to create and destroy the public loopdata
-%% structure available to the meta-trace CallFunc and ReturnFunc.
-%% CleanPublLDmf={Mod,Func}
-%% This function will periodically be called to clean the public LD from
-%% pending meta-trace messages waiting for a corresponding return_from
-%% message.
-%%
-%% Starts a meta-tracer process, opening the ti-file specified in TiData. PublLD
-%% is used to communicate data, typically between a call and return_from.
-%% If no special initialization function is specified a standard one is used.
-%% Note that the meta tracer function must know "who" is the regular tracer
-%% (process or port). This because it must be possible to append {tracer,Tracer}
-%% in meta match specs.
-start(TiData,Tracer) ->
- Pid=spawn_link(?MODULE,
- init,
- [self(),
- TiData,
- Tracer,
- {?MODULE,init_std_publld,[2,[]]},
- void,
- {?MODULE,clean_std_publld}]),
- wait_for_reply(Pid).
-start(TiData,Tracer,InitPublLDmfa,RemovePublLDmf,CleanPublLDmf) ->
- Pid=spawn_link(?MODULE,
- init,
- [self(),TiData,Tracer,InitPublLDmfa,RemovePublLDmf,CleanPublLDmf]),
- wait_for_reply(Pid).
-
-wait_for_reply(Pid) ->
- receive
- {Pid,ok} ->
- {ok,Pid};
- {Pid,{error,Reason}} ->
- {error,Reason}
- after
- 10000 -> % After very long time.
- exit(Pid,kill), % It must be hanging.
- {error,time_out}
- end.
-%% -----------------------------------------------------------------------------
-
-%% stop(Pid)=ok
-%% Pid=Adders to the meta tracer, pid().
-%% Shutsdown the metatracer.
-stop(Pid) ->
- Pid ! {stop,self()},
- ok.
-%% -----------------------------------------------------------------------------
-
-%% suspend(Pid)=ok
-%% Pid=Adders to the meta tracer, pid().
-%% Suspends the meta tracer by removing all meta trace patterns.
-suspend(Pid) ->
- Pid ! {suspend,self()},
- ok.
-%% -----------------------------------------------------------------------------
-
-%% init_tpm(Pid,Mod,Func,Arity,CallFunc)=
-%% init_tpm(Pid,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc)=ok|{error,Reason}.
-%% Pid=Address to meta tracer process, pid().
-%% Mod,Func=Pointing out the function which shall be meta traced, atom().
-%% Arity=As above, integer().
-%% InitFunc,RemoveFunc={Module,Function}|fun(), functions being called when
-%% to initialize the public loopdata structure, and to reset it.
-%% InitFunc(Mod,Func,Arity,PublLD)->{ok,NewPublLD,Output}
-%% Supposed to initialize whatever needs to be done before
-%% handling any incoming meta-trace message for the Mod:Func/Arity.
-%% RemoveFunc(Mod,Func,Arity,PublLD)->{ok,NewPublLD}
-%% Called when meta tracing of Mod:Func/Arity is stopped. It is supposed
-%% to clear datastructures away from the PublLD.
-%% Initializes the public loopdata for this function. Note that we can not use wildcards
-%% here (even if it is perfectly legal in Erlang). It also sets the CallFunc and
-%% ReturnFunc for the meta traced function. The function is hence ready to be
-%% meta traced with either tpm/5 or tpm_ms/5.
-%% This function is synchronous, waiting for a reply from the meta server.
-init_tpm(Pid,Mod,Func,Arity,CallFunc) ->
- init_tpm(Pid,Mod,Func,Arity,void,CallFunc,void,void).
-init_tpm(Pid,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- send_wait(Pid,
- {init_tpm,{Mod,Func,Arity},InitFunc,CallFunc,ReturnFunc,RemoveFunc}).
-%% -----------------------------------------------------------------------------
-
-%% tpm(Pid,Mod,Func,Arity,MatchSpec)={ok,N}|{error,Reason}
-%% tpm(Pid,Mod,Func,Arity,MatchSpec,CallFunc)={ok,N}|{error,Reason}
-%% tpm(Pid,Mod,Func,Arity,MatchSpec,InitFunc,CallFunc,ReturnFunc,RemoveFunc)=
-%% Pid=Address to meta tracer process, pid().
-%% Mod,Func=Pointing out the function which shall be meta traced, atom().
-%% Arity=As above, integer().
-%% MatchSpec=List of match specification, possibly empty. Remember {return_trace}
-%% if expecting return_from messages.
-%% InitFunc,CallFunc,ReturnFunc,RemoveFunc={Module,Function}|fun(),
-%% functions being called when these functions are called by the meta trace
-%% server at certain events.
-%% CallFunc(CallingPid,ActualArgList,PublLD)->{ok,NewPrivLD,Output}
-%% ReturnFunc(CallingPid,ReturnValue,PublLD)->{ok,NewPrivLD,Output}
-%% When a call respectively return_from trace message arrives for the meta
-%% traced function, the corresponding function is called.
-%% The ReturnFunc must handle the fact that a return_from message arrives
-%% for a call which was never noticed. This because the message queue of the
-%% meta tracer may have been emptied.
-%% Reason=badarg |
-%% Output=Characters to be written to the ti-file, bin() | 'void'
-%% The tpm/5 function simply starts meta tracing for the function. It must
-%% previously have been initialized.
-%% tpm/6 & /9 initializes the function and starts meta tracing.
-tpm(Pid,Mod,Func,Arity,MatchSpec)
- when is_atom(Mod),is_atom(Func),is_integer(Arity),is_list(MatchSpec),Mod/='_',Func/='_'->
- send_wait(Pid,{tpm,{Mod,Func,Arity,MatchSpec}});
-tpm(_,_,_,_,_) ->
- {error,badarg}.
-
-tpm(Pid,Mod,Func,Arity,MatchSpec,CallFunc) ->
- tpm(Pid,Mod,Func,Arity,MatchSpec,void,CallFunc,void,void).
-
-tpm(Pid,Mod,Func,Arity,MatchSpec,InitFunc,CallFunc,ReturnFunc,RemoveFunc)
- when is_atom(Mod),is_atom(Func),is_integer(Arity),is_list(MatchSpec),Mod/='_',Func/='_' ->
- send_wait(Pid,{tpm,{Mod,Func,Arity,MatchSpec},InitFunc,CallFunc,ReturnFunc,RemoveFunc});
-tpm(_,_,_,_,_,_,_,_,_) ->
- {error,badarg}.
-%% -----------------------------------------------------------------------------
-
-%% Same as tpm/X but the meta tracer will automatically append {tracer,Tracer}
-%% to the enable list in a {trace,Disable,Enable} match spec action term.
-tpm_tracer(Pid,Mod,Func,Arity,MatchSpec)
- when is_atom(Mod),is_atom(Func),is_integer(Arity),is_list(MatchSpec),Mod/='_',Func/='_'->
- send_wait(Pid,{tpm_tracer,{Mod,Func,Arity,MatchSpec}});
-tpm_tracer(_,_,_,_,_) ->
- {error,badarg}.
-
-tpm_tracer(Pid,Mod,Func,Arity,MatchSpec,CallFunc) ->
- tpm_tracer(Pid,Mod,Func,Arity,MatchSpec,void,CallFunc,void,void).
-
-tpm_tracer(Pid,Mod,Func,Arity,MatchSpec,InitFunc,CallFunc,ReturnFunc,RemoveFunc)
- when is_atom(Mod),is_atom(Func),is_integer(Arity),is_list(MatchSpec),Mod/='_',Func/='_' ->
- send_wait(Pid,{tpm_tracer,
- {Mod,Func,Arity,MatchSpec},
- InitFunc,CallFunc,ReturnFunc,RemoveFunc});
-tpm_tracer(_,_,_,_,_,_,_,_,_) ->
- {error,badarg}.
-%% -----------------------------------------------------------------------------
-
-%% tpm_ms(Pid,Mod,Func,Arity,MSname,MS)={ok,N}|{error,Reason}
-%% Pid=Address to meta tracer process, pid().
-%% Mod,Func=Pointing out the function to which we shall add a match-spec., atom().
-%% Arity=As above, integer().
-%% MSname=A name to be used if this MS shall be removed later. term().
-%% MatchSpec=List of match specification, Remember {return_trace}
-%% if expecting return_from messages.
-%% This function adds a list of match-specs to the already existing ones. It
-%% uses an internal database to keep track of existing match-specs. If the
-%% match-spec does not result in any meta traced functions (for whatever reason),
-%% the MS is not saved in the database. The previously known match-specs are
-%% not removed.
-tpm_ms(Pid,Mod,Func,Arity,MSname,MS) ->
- send_wait(Pid,{tpm_ms,{Mod,Func,Arity},MSname,MS}).
-%% -----------------------------------------------------------------------------
-
-%% Same as tpm_ms/6 but the meta tracer will automatically append {tracer,Tracer}
-%% to the enable list in a {trace,Disable,Enable} match spec action term.
-tpm_ms_tracer(Pid,Mod,Func,Arity,MSname,MS) ->
- send_wait(Pid,{tpm_ms_tracer,{Mod,Func,Arity},MSname,MS}).
-%% -----------------------------------------------------------------------------
-
-%% ctpm_ms(Pid,Mod,Func,Arity)=ok
-%%
-%% Removes a names match-spec from the meta traced function. Note that is never
-%% a fault to remove an MS. Not even from a function which is non existant.
-ctpm_ms(Pid,Mod,Func,Arity,MSname) ->
- send_wait(Pid,{ctpm_ms,{Mod,Func,Arity},MSname}).
-%% -----------------------------------------------------------------------------
-
-%% Quick versions for erlang:register/2 which also uses a default CallFunc
-%% and a default ReturnFunc.
-local_register(Pid) ->
- Res1=tpm(Pid,
- erlang,register,2,[{'_',[],[{exception_trace}]}],
- fun metafunc_init/4,fun local_register_call/3,
- fun local_register_return/3,void),
- Res2=tpm(Pid,
- erlang,unregister,1,[],
- void,fun local_unregister_call/3,void,void),
- {Res1,Res2}.
-%% -----------------------------------------------------------------------------
-
-%% Quick version for global:register_name/2, /3.
-global_register(Pid) ->
- Res1=tpm(Pid,global,handle_call,3,[{[{register,'_','_','_'},'_','_'],[],[]}],
- void,fun global_register_call/3,void,void),
- Res2=tpm(Pid,global,delete_global_name,2,[],
- void,fun global_unregister_call/3,void,void),
- {Res1,Res2}.
-%% -----------------------------------------------------------------------------
-
-%% ctpm(Pid,Mod,Func,Arity)=ok|{error,bad_mfa}
-%%
-%% Removes the meta trace pattern for the function, means stops generating output
-%% for this function. The public LD may be cleared by the previously entered
-%% RemoveFunc.
-ctpm(Pid,Mod,Func,Arity) ->
- send_wait(Pid,{ctpm,{Mod,Func,Arity}}).
-%% -----------------------------------------------------------------------------
-
-%% remove_local_register(Pid)={Res1,Res2}
-%% Res1,Res2=ok|{error,Reason}
-remove_local_register(Pid) ->
- Res1=ctpm(Pid,erlang,register,2),
- Res2=ctpm(Pid,erlang,unregister,1),
- {Res1,Res2}.
-%% -----------------------------------------------------------------------------
-
-%% remove_global_register(Pid)={Res1,Res2}
-%% Res1,Res2=ok|{error,Reason}
-remove_global_register(Pid) ->
- Res1=ctpm(Pid,global,handle_call,3),
- Res2=ctpm(Pid,global,delete_global_name,2),
- {Res1,Res2}.
-%% -----------------------------------------------------------------------------
-
-%% Exported help functions which may be used in programming CallFunc and/or
-%% ReturnFunc. Useful if the call is done on one node but must trigger the
-%% start of something at other nodes.
-metacast_call(Nodes,OrigPid,M,F,Args) ->
- multicast(Nodes,{trace_ts,OrigPid,call,{M,F,Args},void}),
- ok.
-
-metacast_return_from(Nodes,OrigPid,M,F,Arity,Value) ->
- multicast(Nodes,{trace_ts,OrigPid,return_from,{M,F,Arity},Value,void}),
- ok.
-
-multicast([Node|Rest],Msg) ->
- {?MODULE,Node} ! Msg,
- multicast(Rest,Msg);
-multicast([],_) ->
- true.
-%% -----------------------------------------------------------------------------
-
-%% get_states(Pid)={ok,LD,PubLD}.
-get_state(Pid) ->
- send_wait(Pid,get_state).
-%% -----------------------------------------------------------------------------
-
-
-send_wait(To,Msg) ->
- Ref=make_ref(),
- MRef=erlang:monitor(process,To),
- To ! {Msg,Ref,self()},
- receive
- {inviso_rt_meta_reply,Ref,Reply} ->
- erlang:demonitor(MRef),
- Reply;
- {'DOWN',MRef,_,_To,_Reason} ->
- {error,no_metatracer}
- end.
-
-reply(To,Ref,Reply) ->
- To ! {inviso_rt_meta_reply,Ref,Reply}.
-%% -----------------------------------------------------------------------------
-
-%% =============================================================================
-%% Special API.
-%% =============================================================================
-
-%% write_ti(OutPut)=
-%% OutPut=binary()
-%% Makes an extra entry into the trace information file (ti-file). This is useful
-%% if a pid-alias association is learned in another way than through a meta traced
-%% function call. Note that this API can only be used locally at the node in
-%% question.
-write_ti(OutPut) ->
- catch ?MODULE ! {write_ti,OutPut}.
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% API intended to be used on CallFuncs and RemoveFuncs.
-%% =============================================================================
-
-%% The reason there must be a special API for CallFuncs and RemoveFuncs are is
-%% that those functions are executed inside *this* process context. Hence they
-%% can not make function calls requiering this process to receive messages.
-
-%% Returns the tracer used for regular tracing. The reason this is implemented
-%% in this way is that this function is intended to be used in meta trace call-
-%% back functions. And there we can not have message passing API:s to the meta
-%% trace(!).
-get_tracer() ->
- get(tracer).
-%% -----------------------------------------------------------------------------
-
-%% Function equivalent to inviso_rt:tpm_ms/6. This function can *only* be used
-%% inside a CallFunc or a RemoveFunc.
-tpm_ms(Mod,Func,Arity,MSname,MS) ->
- case check_mfarity_exists(Mod,Func,Arity) of
- yes -> % Ok, and args must be ok then also.
- {ok,h_tpm_ms(Mod,Func,Arity,MSname,MS)};
- no ->
- {error,not_initiated}
- end.
-%% -----------------------------------------------------------------------------
-
-tpm_ms_tracer(Mod,Func,Arity,MSname,MS) ->
- case check_mfarity_exists(Mod,Func,Arity) of
- yes -> % Ok, and args must be ok then also.
- NewMS=add_tracer(MS,get_tracer()),
- {ok,h_tpm_ms(Mod,Func,Arity,MSname,NewMS)};
- no ->
- {error,not_initiated}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function that returns all MSname in use for Mod:Func/Arity
-list_tpm_ms(Mod,Func,Arity) ->
- {ok,h_list_tpm_ms(Mod,Func,Arity)}.
-%% -----------------------------------------------------------------------------
-
-%% Function equivalent to inviso_rt:ctpm_ms/5. This function can *only* be used
-%% inside a CallFunc or a RemoveFunc.
-ctpm_ms(Mod,Func,Arity,MSname) ->
- h_ctpm_ms(Mod,Func,Arity,MSname),
- ok.
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% The server implemenation.
-%% =============================================================================
-
-init(Parent,TiData,Tracer,InitPublLDmfa,RemovePublLDmf,CleanPublLDmf) ->
- process_flag(priority,high), % Since we may receive from many procs.
- register(?MODULE,self()), % So we can act as relay receiver.
- case open_traceinfo_file(TiData) of
- {ok,TI} -> % The ti.-file.
- TId=ets:new(?NAMED_MS_TAB,[named_table,set,protected]),
- PublLD=do_init_publ_ld(InitPublLDmfa),
- Parent ! {self(),ok},
- put(tracer,Tracer), % Uggly quick fix!
- loop(Parent,
- Tracer,
- TI,
- mk_new_ld(InitPublLDmfa,RemovePublLDmf,CleanPublLDmf,TId),
- PublLD,
- now());
- {error,Reason} ->
- Parent ! {self(),{error,Reason}}
- end.
-%% -----------------------------------------------------------------------------
-
-loop(Parent,Tracer,TI,LD,PrevPublLD,PrevCleanTime) ->
- {PublLD,CleanTime}=throw_old_failed(get_cleanpublldmf_ld(LD),PrevPublLD,PrevCleanTime),
- receive
- {{init_tpm,{Mod,Func,Arity},InitFunc,CallFunc,ReturnFunc,RemoveFunc},Ref,Parent} ->
- case check_mfarity_exists(Mod,Func,Arity) of
- no -> % Good then we can add it!
- case check_tpm_args(Mod,Func,Arity) of
- true -> % Args are ok.
- {NewLD,NewPublLD}=
- h_init_tpm(Mod,Func,Arity,
- InitFunc,CallFunc,ReturnFunc,RemoveFunc,
- TI,LD,PublLD),
- reply(Parent,Ref,ok),
- loop(Parent,Tracer,TI,NewLD,NewPublLD,CleanTime);
- false -> % Faulty arguments,
- reply(Parent,Ref,{error,bad_mfa}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- yes -> % If it already exists, cant init again.
- reply(Parent,Ref,{error,already_initiated}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {{tpm,{Mod,Func,Arity,MS},InitFunc,CallFunc,ReturnFunc,RemoveFunc},Ref,Parent} ->
- case check_mfarity_exists(Mod,Func,Arity) of
- no -> % Good then we can add it!
- case check_tpm_args(Mod,Func,Arity) of
- true -> % Args are ok.
- {NewLD,NewPublLD,N}=
- h_tpm(Mod,Func,Arity,MS,
- InitFunc,CallFunc,ReturnFunc,RemoveFunc,
- TI,LD,PublLD),
- reply(Parent,Ref,{ok,N}),
- loop(Parent,Tracer,TI,NewLD,NewPublLD,CleanTime);
- false ->
- reply(Parent,Ref,{error,bad_mfa}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- yes ->
- reply(Parent,Ref,{error,already_initiated}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {{tpm,{Mod,Func,Arity,MS}},Ref,Parent} ->
- case check_mfarity_exists(Mod,Func,Arity) of
- yes -> % Ok, and args must be ok then also.
- {NewLD,N}=h_tpm(Mod,Func,Arity,MS,LD),
- reply(Parent,Ref,{ok,N}),
- loop(Parent,Tracer,TI,NewLD,PublLD,CleanTime);
- no -> % Must be initiated before.
- reply(Parent,Ref,{error,not_initiated}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {{tpm_tracer,{Mod,Func,Arity,MS},InitFunc,CallFunc,ReturnFunc,RemoveFunc},Ref,Parent} ->
- case check_mfarity_exists(Mod,Func,Arity) of
- no -> % Good then we can add it!
- case check_tpm_args(Mod,Func,Arity) of
- true -> % Args are ok.
- NewMS=add_tracer(MS,Tracer),
- {NewLD,NewPublLD,N}=
- h_tpm(Mod,Func,Arity,NewMS,
- InitFunc,CallFunc,ReturnFunc,RemoveFunc,
- TI,LD,PublLD),
- reply(Parent,Ref,{ok,N}),
- loop(Parent,Tracer,TI,NewLD,NewPublLD,CleanTime);
- false ->
- reply(Parent,Ref,{error,bad_mfa}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- yes ->
- reply(Parent,Ref,{error,already_initiated}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {{tpm_tracer,{Mod,Func,Arity,MS}},Ref,Parent} ->
- case check_mfarity_exists(Mod,Func,Arity) of
- yes -> % Ok, and args must be ok then also.
- NewMS=add_tracer(MS,Tracer),
- {NewLD,N}=h_tpm(Mod,Func,Arity,NewMS,LD),
- reply(Parent,Ref,{ok,N}),
- loop(Parent,Tracer,TI,NewLD,PublLD,CleanTime);
- no -> % Must be initiated before.
- reply(Parent,Ref,{error,not_initiated}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {{tpm_ms,{Mod,Func,Arity},MSname,MS},Ref,Parent} ->
- case check_mfarity_exists(Mod,Func,Arity) of
- yes -> % Ok, and args must be ok then also.
- reply(Parent,Ref,{ok,h_tpm_ms(Mod,Func,Arity,MSname,MS)}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime);
- no ->
- reply(Parent,Ref,{error,not_initiated}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {{tpm_ms_tracer,{Mod,Func,Arity},MSname,MS},Ref,Parent} ->
- case check_mfarity_exists(Mod,Func,Arity) of
- yes -> % Ok, and args must be ok then also.
- NewMS=add_tracer(MS,Tracer),
- reply(Parent,Ref,{ok,h_tpm_ms(Mod,Func,Arity,MSname,NewMS)}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime);
- no ->
- reply(Parent,Ref,{error,not_initiated}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {{ctpm_ms,{Mod,Func,Arity},MSname},Ref,Parent} ->
- reply(Parent,Ref,ok),
- h_ctpm_ms(Mod,Func,Arity,MSname),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime);
- {{ctpm,{Mod,Func,Arity}},Ref,Parent} ->
- case get_remove_func_ld(Mod,Func,Arity,LD) of
- false -> % Incorrect Mod:Func/Arity!
- reply(Parent,Ref,{error,bad_mfa}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime); % Do nothing!
- MF -> % {M,F}, Func or 'void'.
- catch erlang:trace_pattern({Mod,Func,Arity},false,[meta]),
- NewPublLD=do_removefunc(MF,Mod,Func,Arity,PublLD),
- NewLD=ctpm_ld(Mod,Func,Arity,LD),
- reply(Parent,Ref,ok),
- loop(Parent,Tracer,TI,NewLD,NewPublLD,CleanTime)
- end;
- {suspend,Parent} -> % Removes all meta trace patterns.
- stop_all_meta_tracing(get_all_meta_funcs_ld(LD),PublLD,LD),
- do_remove_publ_ld(get_removepublldmf_ld(LD),PublLD),
- NewPublLD=do_init_publ_ld(get_initpublldmfa_ld(LD)),
- loop(Parent,Tracer,TI,reset_ld(LD),NewPublLD,CleanTime);
- {stop,Parent} -> % Make a controlled shutdown.
- stop_all_meta_tracing(get_all_meta_funcs_ld(LD),PublLD,LD),
- do_remove_publ_ld(get_removepublldmf_ld(LD),PublLD),
- close_traceinfo_file(TI); % And then simply terminate.
- {trace_ts,Pid,call,{M,F,Args},TS} ->
- case handle_meta(get_call_func_ld(M,F,length(Args),LD),Pid,{call,Args,TS},PublLD) of
- {ok,NewPublLD,Output} when is_binary(Output);is_list(Output) ->
- write_output(TI,Output),
- loop(Parent,Tracer,TI,LD,NewPublLD,CleanTime);
- {ok,NewPublLD,_} -> % No output to the ti-file this time.
- loop(Parent,Tracer,TI,LD,NewPublLD,CleanTime);
- _ -> % Not handled correct, not much to do.
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {trace_ts,Pid,TypeTag,{M,F,Arity},Value,TS}
- when TypeTag==return_from;TypeTag==exception_from ->
- case handle_meta(get_return_func_ld(M,F,Arity,LD),Pid,{TypeTag,Value,TS},PublLD) of
- {ok,NewPublLD,Output} when is_binary(Output);is_list(Output) ->
- write_output(TI,Output),
- loop(Parent,Tracer,TI,LD,NewPublLD,CleanTime);
- {ok,NewPublLD,_} -> % No output to the ti-file this time.
- loop(Parent,Tracer,TI,LD,NewPublLD,CleanTime);
- _ -> % Not handled correct, not much to do.
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {relayed_meta,Bin} ->
- write_output(TI,Bin),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime);
- {write_ti,OutPut} ->
- write_output(TI,OutPut),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime);
- {get_state,Ref,From} -> % Debug function.
- reply(From,Ref,{ok,LD,PublLD}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime);
- _Other ->
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end.
-
-
-%% =============================================================================
-%% First level help functions.
-%% =============================================================================
-
-%% Function which opens the trace-information file(s). It must understand
-%% the tidata specification which is part of the tracerdata given to the
-%% runtime component during init_tracing.
-%% It must return an internal notation of the time of file open and a
-%% useful descriptor the write_output function can use.
-%% Returns {ok,TiDescriptor} or {error,Reason}.
-open_traceinfo_file({file,FileName}) -> % A plain raw binary file.
- case file:open(FileName,[write,raw,binary]) of
- {ok,FD} ->
- {ok,{file,FD}};
- {error,Reason} ->
- {error,{open,[FileName,Reason]}}
- end;
-open_traceinfo_file({relay,ToNode}) -> % Use distributed Erlang.
- {ok,{relay,ToNode}};
-open_traceinfo_file(IncorrectTI) ->
- {error,{badarg,IncorrectTI}}.
-%% -----------------------------------------------------------------------------
-
-close_traceinfo_file({file,FD}) ->
- file:close(FD);
-close_traceinfo_file(_) ->
- ok.
-%% -----------------------------------------------------------------------------
-
-%% Help function handling initializing meta tracing of a function.
-%% Returns {NewLD,NewPublLD}.
-h_init_tpm(Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc,TI,LD,PublLD) ->
- case do_initfunc(InitFunc,Mod,Func,Arity,PublLD) of
- {NewPublLD,Output} ->
- write_output(TI,Output),
- NewLD=init_tpm_ld(Mod,Func,Arity,CallFunc,ReturnFunc,RemoveFunc,LD),
- {NewLD,NewPublLD};
- false -> % The initfunc did not do anything.
- NewLD=init_tpm_ld(Mod,Func,Arity,CallFunc,ReturnFunc,RemoveFunc,LD),
- {NewLD,PublLD}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function handling initializing meta tracing of a function and also
-%% set the meta trace pattern as specified.
-%% Returns {NewLD,NewPublLD,N}.
-h_tpm(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc,TI,LD,PublLD) ->
- {NewLD,NewPublLD}=
- h_init_tpm(Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc,TI,LD,PublLD),
- case set_meta_tracing(Mod,Func,Arity,MS) of
- true -> % Ok, set one pattern.
- {NewLD,NewPublLD,1};
- false ->
- {NewLD,NewPublLD,0}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function handling setting meta trace patter for a function which has
-%% already been intialized. Note that we must remove all potentially stored
-%% match-specs, if this function has been given match-specs before with
-%% tpm_ms.
-%% Returns a {NewLD,N}.
-h_tpm(Mod,Func,Arity,MS,LD) ->
- case set_meta_tracing(Mod,Func,Arity,MS) of
- true ->
- {remove_ms_ld(Mod,Func,Arity,LD),1};
- false ->
- {LD,0}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function that adds a match-spec to Mod:Func/Arity. It is not defined
-%% in which order the match-specs will be given to the BIF.
-%% Note that if an MS with the same name as an exiting is inserted, the previous
-%% match-spec will be removed.
-%% Very important to realise is that the empty meta match spec [] imposes no
-%% restrictions what so ever on the generating of meta trace call messages.
-%% Uncontrolled sending of such messages may quickly drain power from the system.
-%% Since an empty match-spec will "disappear" when added to other match specs,
-%% the empty match is transformed to what it actually is: [{'_',[],[]}].
-%% Returns 0 or 1 indicating failure or success.
-h_tpm_ms(Mod,Func,Arity,MSname,MS) ->
- MSsNames=get_ms_ld(Mod,Func,Arity), % Fetch all previous match-specs.
- TransformedMS=h_tpm_ms_convert_null_ms(MS),
- MSsNames1=lists:keydelete(MSname,1,MSsNames), % If it already existed, it is gone!
- NewMSs=lists:flatten([TransformedMS,lists:map(fun({_Name,MSx})->MSx end,MSsNames1)]),
- case set_meta_tracing(Mod,Func,Arity,NewMSs) of
- true -> % We only save the MS if it was good.
- put_ms_ld(Mod,Func,Arity,MSname,TransformedMS,MSsNames1),
- 1;
- false ->
- 0
- end.
-
-%% Help function converting the null match spec into, still a null match spec,
-%% on a proper match spec format. This because it will otherwise be difficult
-%% to see the difference between no active tpm_ms and all a set of null ms.
-h_tpm_ms_convert_null_ms([]) ->
- [{'_',[],[]}];
-h_tpm_ms_convert_null_ms(MS) ->
- MS.
-%% -----------------------------------------------------------------------------
-
-%% Help function returning a list of all names used for match-functions for
-%% the Mod:Func/Arity in question.
-h_list_tpm_ms(Mod,Func,Arity) ->
- MSsNames=get_ms_ld(Mod,Func,Arity), % A list of {MSname,MS}.
- lists:map(fun({MSname,_})->MSname end,MSsNames).
-%% -----------------------------------------------------------------------------
-
-%% Function that removes a named match-spec. Returns nothing significant.
-%% Note that if we end up with no match-specs, we must remove the meta trace
-%% patten all together. That is bringing the function back to just initiated.
-h_ctpm_ms(Mod,Func,Arity,MSname) ->
- case get_ms_ld(Mod,Func,Arity) of
- [] -> % The name does certainly not exist!
- true; % We don't have to do anything.
- MSsNames ->
- case lists:keysearch(MSname,1,MSsNames) of
- {value,{_,_MS}} -> % Ok, we must do something!
- NewMSsNames=lists:keydelete(MSname,1,MSsNames),
- case lists:flatten(lists:map(fun({_Name,MS})->MS end,NewMSsNames)) of
- [] -> % This means stop meta tracing.
- set_meta_tracing(Mod,Func,Arity,false);
- NewMSs ->
- set_meta_tracing(Mod,Func,Arity,NewMSs)
- end,
- set_ms_ld(Mod,Func,Arity,NewMSsNames);
- false -> % But this name does not exist.
- true % So we do not have to do anything.
- end
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function that checks the arguments to the meta trace pattern. The reason we
-%% must do this is that we can only allow meta tracing on specific functions and
-%% not using wildpatterns. Otherwise the meta trace server will not understand
-%% which callfunc for instance to call when a meta-trace message is generated
-%% for a function.
-%% Returns 'true' or 'false'.
-check_tpm_args(Mod,Func,Arity)
- when is_atom(Mod),is_atom(Func),is_integer(Arity),Mod/='_',Func/='_' ->
- true;
-check_tpm_args(_,_,_) ->
- false.
-%% -----------------------------------------------------------------------------
-
-%% Help function which calls the actual BIF setting meta-trace-patterns.
-%% Returns 'true' or 'false'.
-set_meta_tracing(Mod,Func,Arity,MS) when is_atom(Mod) ->
- case erlang:module_loaded(Mod) of
- true ->
- set_meta_tracing_2(Mod,Func,Arity,MS);
- false -> % The module is not loaded.
- case code:ensure_loaded(Mod) of
- {module,_Mod} ->
- set_meta_tracing_2(Mod,Func,Arity,MS);
- {error,_Reason} -> % Could not load the module.
- false % No use try to trace.
- end
- end;
-set_meta_tracing(_,_,_,_) ->
- false.
-
-set_meta_tracing_2(Mod,Func,Arity,MS) ->
- case catch erlang:trace_pattern({Mod,Func,Arity},MS,[meta]) of
- 0 -> % Hmm, nothing happend :-)
- false;
- N when is_integer(N) -> % The normal case, some functions were hit.
- true;
- {'EXIT',_Reason} ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function which removes all meta trace pattern for the functions mentioned
-%% in the list being first argument. It also executes the remove funcs for each
-%% and every no longer meta traced function. This done since some of the remove
-%% functions may do side-effects (like deleteing ETS tables).
-%% Returns nothing significant.
-stop_all_meta_tracing([{M,F,Arity}|Rest],PublLD,LD) ->
- catch erlang:trace_pattern({M,F,Arity},false,[meta]),
- NewPublLD=do_removefunc(get_remove_func_ld(M,F,Arity,LD),M,F,Arity,PublLD),
- stop_all_meta_tracing(Rest,NewPublLD,LD);
-stop_all_meta_tracing([],_,_) ->
- true.
-%% -----------------------------------------------------------------------------
-
-%% This function calls the function registered to be handler for a certain
-%% meta-traced function. Such a function or fun must take three arguments
-%% and return {ok,NewPrivLD,OutPutBinary} or 'false'. OutPutBinary may be
-%% something else, and is then ignored.
-handle_meta({M,F},Pid,Arg1,PrivLD) ->
- (catch M:F(Pid,Arg1,PrivLD));
-handle_meta(Fun,Pid,Arg1,PrivLD) when is_function(Fun) ->
- (catch Fun(Pid,Arg1,PrivLD));
-handle_meta(_,_,_,_) -> % Don't know how to do this.
- false.
-%% -----------------------------------------------------------------------------
-
-%% Help function writing output from a callback function to the ti-file.
-%% Output can be a binary or a list of binaries.
-write_output(TI,[OutPut|Rest]) ->
- write_output(TI,OutPut),
- write_output(TI,Rest);
-write_output({file,FD},Bin) when is_binary(Bin) -> % Plain direct-binary file
- Size=byte_size(Bin),
- file:write(FD,list_to_binary([<<0,Size:32>>,Bin]));
-write_output({relay,ToNode},Bin) when is_atom(ToNode),is_binary(Bin) ->
- {inviso_rt_meta,ToNode} ! {relayed_meta,Bin};
-write_output(_,_) -> % Don't understand, just skip.
- true.
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Various help functions.
-%% =============================================================================
-
-%% Help function initializing the public loopdata structure. Note that if the
-%% supplied InitPublLDmfa is faulty we let the structure become the error.
-%% The error will most likely turn up in an error report somewhere, eventually.
-do_init_publ_ld({M,F,Args}) when is_atom(M),is_atom(F),is_list(Args) ->
- case catch apply(M,F,Args) of
- {'EXIT',_Reason} ->
- {error,init_publ_ld_func}; % Let the struct be this error!
- InitialPublLD ->
- InitialPublLD
- end;
-do_init_publ_ld(_) ->
- {error,init_publ_ld_func}.
-%% -----------------------------------------------------------------------------
-
-%% Help function which removes the public loopdata structure. The function does
-%% not necessarily have to exist. Returns nothing significant.
-do_remove_publ_ld({M,F},PublLD) when is_atom(M),is_atom(F) ->
- catch M:F(PublLD);
-do_remove_publ_ld(_,_) ->
- true.
-%% -----------------------------------------------------------------------------
-
-%% Hlp function initializing a particular meta traced function into the public
-%% loopdata. Note that the function is not mandatory.
-%% Returns {NewPublLD,Output} or 'false'.
-do_initfunc({M,F},Mod,Func,Arity,PublLD) when is_atom(M),is_atom(F) ->
- case catch M:F(Mod,Func,Arity,PublLD) of
- {ok,NewPublLD,Output} ->
- {NewPublLD,Output};
- _ -> % Everything else is an error.
- false % Act as no initialization function.
- end;
-do_initfunc(Fun,Mod,Func,Arity,PublLD) when is_function(Fun) ->
- case catch Fun(Mod,Func,Arity,PublLD) of
- {ok,NewPublLD,Output} ->
- {NewPublLD,Output};
- _ -> % Everything else is an error.
- false % Act as no initialization function.
- end;
-do_initfunc(_,_,_,_,_) -> % Perhaps too generous, should be 'void' only.
- false.
-%% -----------------------------------------------------------------------------
-
-%% Help function removing a particular meta traced function from the public
-%% loopdata. Note that we do not make much noice should the call back function
-%% be faulty.
-do_removefunc({M,F},Mod,Func,Arity,PublLD) when is_atom(M),is_atom(F) ->
- case catch M:F(Mod,Func,Arity,PublLD) of
- {ok,NewPublLD} ->
- NewPublLD;
- _ -> % Everything else is an error.
- PublLD % Act as no initialization function.
- end;
-do_removefunc(Fun,Mod,Func,Arity,PublLD) when is_function(Fun) ->
- case catch Fun(Mod,Func,Arity,PublLD) of
- {ok,NewPublLD} ->
- NewPublLD;
- _ -> % Everything else is an error.
- PublLD % Act as no initialization function.
- end;
-do_removefunc(_,_,_,_,PublLD) ->
- PublLD.
-%% -----------------------------------------------------------------------------
-
-%% Function that, if the time has come, goes through the priv-ld structure and
-%% cleans away entryn left behind. The usual cause is that the function call
-%% caused an exception and there were therefore no matching return_from.
-%% Returns {NewPrivLD,now()}.
-throw_old_failed({M,F},PrivLD,PrevClean) ->
- case difference_in_now(PrevClean,now(),60) of % We clean once every minute.
- true ->
- case catch apply(M,F,[PrivLD]) of
- {'EXIT',_Reason} -> % Something went wrong, ignore it.
- {PrivLD,now()}; % Just keep the old priv-ld.
- NewPrivLD -> % The function must return a priv-ld.
- {NewPrivLD,now()}
- end;
- false -> % Not time yet!
- {PrivLD,PrevClean}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function comparing two now timestamps. Returns true or false depending
-%% on if S2 is more than DiffS seconds after S1. Only works for differences
-%% less than 1 million seconds.
-difference_in_now({MegaS1,S1,_},{MegaS2,S2,_},DiffS) ->
- if
- MegaS1+1<MegaS2 -> % More than 1 Mega sec. difference.
- true;
- MegaS1==MegaS2,S1+DiffS<S2 ->
- true;
- MegaS1+1==MegaS2,S1+DiffS<S2+1000000 ->
- true;
- true ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-%% This help function adds a {tracer,Tracer} to the enable-list in a 'trace'
-%% match spec action. The reason for this is that the author of the a meta
-%% match spec meant to turn tracing on for the process executing the match spec
-%% can not know the tracer. This since the match spec is most likely authored
-%% at the control component's node, and not here.
-%% Note the double tuple necessary to make it just precise a tuple!
-%% Returns a new match spec.
-add_tracer([MS1|Rest],Tracer) ->
- [add_tracer_2(MS1,Tracer)|add_tracer(Rest,Tracer)];
-add_tracer([],_) ->
- [];
-add_tracer(NotList,_Tracer) -> % Can be 'false', but also an error.
- NotList.
-
-add_tracer_2({Head,Cond,Body},Tracer) ->
- {Head,Cond,add_tracer_3(Body,Tracer)};
-add_tracer_2(Faulty,_Tracer) ->
- Faulty.
-
-add_tracer_3([{trace,Disable,Enable}|Rest],Tracer) when is_list(Enable) ->
- [{trace,Disable,Enable++[{{tracer,Tracer}}]}|Rest];
-add_tracer_3([ActionTerm|Rest],Tracer) ->
- [ActionTerm|add_tracer_3(Rest,Tracer)];
-add_tracer_3([],_Tracer) ->
- [];
-add_tracer_3(FaultyBody,_Tracer) ->
- FaultyBody.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Help functions handling internal loopdata.
-%% -----------------------------------------------------------------------------
-
--record(ld,{init_publ_ld_mfa, % {M,F,Args}
- remove_publ_ld_mf, % {M,F} | void
- clean_publ_ld_mf, % {Mod,Func}
- ms_mfarities=notable, % ETS holding names match functions.
- call_mfarities=[], % [{{M,F,Arity},2-TupleOrFun},...]
- return_mfarities=[], % [{{M,F,Arity},2-TupleOrFun},...]
- remove_mfarities=[]
- }).
-
-mk_new_ld(InitPublLDmfa,RemovePublLDmf,CleanPublLDmf,TId) ->
- #ld{
- init_publ_ld_mfa=InitPublLDmfa,
- remove_publ_ld_mf=RemovePublLDmf,
- clean_publ_ld_mf=CleanPublLDmf,
- ms_mfarities=TId
- }.
-%% -----------------------------------------------------------------------------
-
-%% Function which restores the internal loop data to somekind of initial state.
-%% This is useful when tracing has been suspended.
-reset_ld(#ld{init_publ_ld_mfa=InitPublLDmfa,
- remove_publ_ld_mf=RemovePublLDmf,
- clean_publ_ld_mf=CleanPublLDmf,
- ms_mfarities=TId}) ->
- ets:match_delete(TId,{'_','_'}), % Empty the table.
- #ld{init_publ_ld_mfa=InitPublLDmfa,
- remove_publ_ld_mf=RemovePublLDmf,
- clean_publ_ld_mf=CleanPublLDmf,
- ms_mfarities=TId}.
-%% -----------------------------------------------------------------------------
-
-get_initpublldmfa_ld(#ld{init_publ_ld_mfa=InitPublLDmfa}) ->
- InitPublLDmfa.
-%% -----------------------------------------------------------------------------
-
-get_removepublldmf_ld(#ld{remove_publ_ld_mf=RemovePublLDmf}) ->
- RemovePublLDmf.
-%% -----------------------------------------------------------------------------
-
-get_cleanpublldmf_ld(#ld{clean_publ_ld_mf=CleanPublLDmf}) ->
- CleanPublLDmf.
-%% -----------------------------------------------------------------------------
-
-%% Help function adding data associated with a meta traced function to the
-%% internal loopdata. Called when meta tracing is activated for M:F/Arity.
-init_tpm_ld(M,F,Arity,CallFunc,ReturnFunc,RemoveFunc,LD) ->
- ets:insert(LD#ld.ms_mfarities,{{M,F,Arity},[]}),
- CallFuncs=LD#ld.call_mfarities,
- ReturnFuncs=LD#ld.return_mfarities,
- RemoveFuncs=LD#ld.remove_mfarities,
- LD#ld{call_mfarities=[{{M,F,Arity},CallFunc}|CallFuncs],
- return_mfarities=[{{M,F,Arity},ReturnFunc}|ReturnFuncs],
- remove_mfarities=[{{M,F,Arity},RemoveFunc}|RemoveFuncs]}.
-%% -----------------------------------------------------------------------------
-
-%% Help function which answers the question if we have already initiated the
-%% function. It is done by looking in the ETS-table with named match-functions.
-%% If there is an entry in the set-type table for M:F/Arity, the function is
-%% initiated.
-%% Returns 'yes' or 'no'.
-check_mfarity_exists(M,F,Arity) ->
- case ets:lookup(?NAMED_MS_TAB,{M,F,Arity}) of
- [] ->
- no;
- [_] ->
- yes
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function adding an entry with [{MSname,MSlist}|MSsNames] for M:F/Arity.
-%% Note that any already existing entry is removed.
-%% Returns nothing significant.
-put_ms_ld(M,F,Arity,MSname,MS,MSsNames) ->
- ets:insert(?NAMED_MS_TAB,{{M,F,Arity},[{MSname,MS}|MSsNames]}).
-%% -----------------------------------------------------------------------------
-
-%% Help function taking a list of {MSname,MSs} and storing them in the
-%% internal loop data structure. The storage is actually implemented as an ETS
-%% table. Any previous list of {MSname,MSs} associated with this {M,F,Arity} will
-%% be lost. Returns nothing significant.
-set_ms_ld(M,F,Arity,MSsNames) ->
- ets:insert(?NAMED_MS_TAB,{{M,F,Arity},MSsNames}).
-%% -----------------------------------------------------------------------------
-
-%% Help function fetching a list of {MSname,MatchSpecs} for a M:F/Arity. The
-%% match-functions are stored in an ETS table searchable on {M,F,Arity}.
-get_ms_ld(M,F,Arity) ->
- case ets:lookup(?NAMED_MS_TAB,{M,F,Arity}) of
- [{_MFArity,MSsNames}] ->
- MSsNames;
- [] ->
- []
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function removing all saved match-specs for a certain M:F/Arity.
-%% Returns a new loopdata structure.
-remove_ms_ld(M,F,Arity,LD) ->
- ets:delete(LD#ld.ms_mfarities,{M,F,Arity}),
- LD.
-%% -----------------------------------------------------------------------------
-
-%% Help function which removes all information about a meta traced function from
-%% the internal loopdata. Returns a new loopdata structure.
-ctpm_ld(M,F,Arity,LD) ->
- ets:delete(LD#ld.ms_mfarities,{M,F,Arity}),
- NewCallFuncs=lists:keydelete({M,F,Arity},1,LD#ld.call_mfarities),
- NewReturnFuncs=lists:keydelete({M,F,Arity},1,LD#ld.return_mfarities),
- NewRemoveFuncs=lists:keydelete({M,F,Arity},1,LD#ld.remove_mfarities),
- LD#ld{call_mfarities=NewCallFuncs,
- return_mfarities=NewReturnFuncs,
- remove_mfarities=NewRemoveFuncs}.
-%% -----------------------------------------------------------------------------
-
-get_call_func_ld(M,F,Arity,#ld{call_mfarities=CallFuncs}) ->
- case lists:keysearch({M,F,Arity},1,CallFuncs) of
- {value,{_,MF}} ->
- MF;
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-get_return_func_ld(M,F,Arity,#ld{return_mfarities=CallFuncs}) ->
- case lists:keysearch({M,F,Arity},1,CallFuncs) of
- {value,{_,MF}} ->
- MF;
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-get_remove_func_ld(M,F,Arity,#ld{remove_mfarities=RemoveFuncs}) ->
- case lists:keysearch({M,F,Arity},1,RemoveFuncs) of
- {value,{_,MF}} ->
- MF;
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function returning a list of all {Mod,Func,Arity} which are currently meta
-%% traced. It does do by listifying the call_mfarities field in the internal
-%% loopdata.
-get_all_meta_funcs_ld(#ld{call_mfarities=CallFuncs}) ->
- lists:map(fun({MFArity,_})->MFArity end,CallFuncs).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Functions for the standard PublLD structure.
-%%
-%% It is tuple {Part1,GlobalData} where Part1 is of length at least 2.
-%% Where each field is a list of tuples. The last item in each tuple shall be
-%% a now tuple, making it possible to clean it away should it be too old to be
-%% relevant (there was no return_from message due to a failure).
-%% Other fields can be used for other functions.
-%% The GlobalData is not cleaned but instead meant to store data must be passed
-%% to each CallFunc when a meta trace message arrives.
-%% =============================================================================
-
-%% Function returning our standard priv-loopdata structure.
-init_std_publld(Size,GlobalData) ->
- {list_to_tuple(lists:duplicate(Size,[])),GlobalData}.
-%% -----------------------------------------------------------------------------
-
-%% Function capable of cleaning out a standard publ-ld. The last element of each
-%% tuple must be the now item.
-%% Returns a new publ-ld structure.
-clean_std_publld({Part1,GlobalData}) ->
- {clean_std_publld_2(Part1,now(),tuple_size(Part1),[]),GlobalData}.
-
-clean_std_publld_2(_,_,0,Accum) ->
- list_to_tuple(Accum);
-clean_std_publld_2(PublLD,Now,Index,Accum) ->
- NewTupleList=clean_std_publld_3(element(Index,PublLD),Now),
- clean_std_publld_2(PublLD,Now,Index-1,[NewTupleList|Accum]).
-
-clean_std_publld_3([Tuple|Rest],Now) ->
- PrevNow=element(tuple_size(Tuple),Tuple), % Last item shall be the now item.
- case difference_in_now(PrevNow,Now,30) of
- true -> % Remove it then!
- clean_std_publld_3(Rest,Now);
- false -> % Keep it!
- [Tuple|clean_std_publld_3(Rest,Now)]
- end;
-clean_std_publld_3([],_) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% =============================================================================
-%% Functions used as handling functions (as funs) for registered process names.
-%% (Given that we use the standard priv-ld, otherwise you must do your own!).
-%% =============================================================================
-
-%% Call-back for initializing the meta traced functions there are quick functions
-%% for. Returns a new public loop data structure.
-metafunc_init(erlang,register,2,{Part1,GlobalData}) ->
- {setelement(1,Part1,[]),GlobalData}.
-%% -----------------------------------------------------------------------------
-
-%% Call-function for erlang:register/2.
-%% This function adds the call to register/2 to a standard priv-ld structure.
-%% Note that we *must* search for previous entries from the same process. If such
-%% still in structure it means a failed register/2 call. It must first be removed
-%% so it can not be mixed up with this one. Since meta-trace message will arrive
-%% in order, there was no return_from message for that call if we are here now.
-local_register_call(CallingPid,{call,[Alias,Pid],TS},{Part1,GlobalData}) ->
- TupleList=element(1,Part1), % The register/2 entry in a std. priv-ld.
- NewTupleList=lists:keydelete(CallingPid,1,TupleList), % If present, remove previous call.
- {ok,
- {setelement(1,Part1,[{CallingPid,{Alias,Pid},TS}|NewTupleList]),GlobalData},
- void}.
-
-%% Return-function for the erlang:register/2 BIF.
-%% This function formulates the output and removes the corresponding call entry
-%% from the standard priv-ld structure.
-local_register_return(CallingPid,{return_from,_Val,_TS},PublLD={Part1,GlobalData}) ->
- TupleList=element(1,Part1), % The register/2 entry in a std. priv-ld.
- case lists:keysearch(CallingPid,1,TupleList) of
- {value,{_,{Alias,Pid},NowTS}} ->
- NewTupleList=lists:keydelete(CallingPid,1,TupleList),
- {ok,
- {setelement(1,Part1,NewTupleList),GlobalData},
- term_to_binary({Pid,Alias,alias,NowTS})};
- false -> % Strange, then don't know what to do.
- {ok,PublLD,void} % Do nothing seems safe.
- end;
-local_register_return(CallingPid,{exception_from,_Val,_TS},{Part1,GlobalData}) ->
- TupleList=element(1,Part1), % The register/2 entry in a std. priv-ld.
- NewTupleList=lists:keydelete(CallingPid,1,TupleList),
- {ok,{setelement(1,Part1,NewTupleList),GlobalData},void}; % No association then.
-local_register_return(_,_,PublLD) -> % Don't understand this.
- {ok,PublLD,void}.
-
-%% When unregister/1 us called we simply want a unalias entry in the ti-file.
-%% We can unfortunately not connect it with a certain pid.
-local_unregister_call(_CallingPid,{_TypeTag,[Alias],TS},PublLD) ->
- {ok,PublLD,term_to_binary({undefined,Alias,unalias,TS})}.
-%% -----------------------------------------------------------------------------
-
-%% Call-function for global:register_name/2,/3.
-%% This function is actually the call function for the handle_call/3 in the
-%% global server. Note that we must check that we only do this on the node
-%% where Pid actually resides.
-global_register_call(_CallingPid,{call,[{register,Alias,P,_},_,_],TS},PublLD)
- when node(P)==node()->
- {ok,PublLD,term_to_binary({P,{global,Alias},alias,TS})};
-global_register_call(_CallingPid,_,PublLD) ->
- {ok,PublLD,void}.
-
-%% Call-function for global:unregister_name. It acutally checks on the use of
-%% global:delete_global_name/2 which is called when ever a global name is removed.
-global_unregister_call(_CallingPid,{call,[Alias,P],TS},PublLD) when node(P)==node()->
- {ok,PublLD,term_to_binary({P,{global,Alias},unalias,TS})};
-global_unregister_call(_CallingPid,_,PublLD) ->
- {ok,PublLD,void}.
-%% -----------------------------------------------------------------------------
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
diff --git a/lib/runtime_tools/src/runtime_tools.app.src b/lib/runtime_tools/src/runtime_tools.app.src
index 1152f7259d..60be9ed7c9 100644
--- a/lib/runtime_tools/src/runtime_tools.app.src
+++ b/lib/runtime_tools/src/runtime_tools.app.src
@@ -17,16 +17,13 @@
%% %CopyrightEnd%
%%
{application, runtime_tools,
- [{description, "RUNTIME_TOOLS version 1"},
+ [{description, "RUNTIME_TOOLS"},
{vsn, "%VSN%"},
{modules, [dbg,observer_backend,percept_profile,
- inviso_rt,inviso_rt_lib,inviso_rt_meta,
- inviso_as_lib,inviso_autostart,inviso_autostart_server,
runtime_tools,runtime_tools_sup,erts_alloc_config,
ttb_autostart,dyntrace]},
- {registered, [runtime_tools_sup,inviso_rt,inviso_rt_meta]},
+ {registered, [runtime_tools_sup]},
{applications, [kernel, stdlib]},
-% {env, [{inviso_autostart_mod,your_own_autostart_module}]},
{env, []},
{mod, {runtime_tools, []}}]}.
diff --git a/lib/runtime_tools/src/runtime_tools_sup.erl b/lib/runtime_tools/src/runtime_tools_sup.erl
index 913719c449..c8dab250dd 100644
--- a/lib/runtime_tools/src/runtime_tools_sup.erl
+++ b/lib/runtime_tools/src/runtime_tools_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. 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
@@ -31,15 +31,11 @@
%% =============================================================================
%% The runtime tools top most supervisor starts:
-%% -The inviso runtime component. This is the only way to get the runtime component
-%% started automatically (if for instance autostart is wanted).
-%% Note that it is not impossible that the runtime component terminates it self
-%% should it discover that no autostart is configured.
+%% -The ttb_autostart component. This is used for tracing at startup
+%% using observer/ttb.
init(AutoModArgs) ->
Flags = {one_for_one, 0, 3600},
- Children = [{inviso_rt, {inviso_rt, start_link_auto, [AutoModArgs]},
- temporary, 3000, worker, [inviso_rt]},
- {ttb_autostart, {ttb_autostart, start_link, []},
+ Children = [{ttb_autostart, {ttb_autostart, start_link, []},
temporary, 3000, worker, [ttb_autostart]}],
{ok, {Flags, Children}}.
%% -----------------------------------------------------------------------------
diff --git a/lib/runtime_tools/test/Makefile b/lib/runtime_tools/test/Makefile
index 4979b9c7b1..bcabdf13ed 100644
--- a/lib/runtime_tools/test/Makefile
+++ b/lib/runtime_tools/test/Makefile
@@ -5,8 +5,6 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
MODULES = \
dyntrace_SUITE \
runtime_tools_SUITE \
- inviso_testmodule1_foo \
- inviso_SUITE \
dbg_SUITE \
erts_alloc_config_SUITE
diff --git a/lib/runtime_tools/test/dbg_SUITE.erl b/lib/runtime_tools/test/dbg_SUITE.erl
index bd908c1f3a..4071b159a1 100644
--- a/lib/runtime_tools/test/dbg_SUITE.erl
+++ b/lib/runtime_tools/test/dbg_SUITE.erl
@@ -394,41 +394,36 @@ file_port2(suite) ->
file_port2(doc) ->
["Test tracing to file port with 'follow_file'"];
file_port2(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {skipped, "VxWorks NFS cache ruins it all."};
- _ ->
- ?line stop(),
- ?line {A,B,C} = erlang:now(),
- ?line FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++
- "-" ++ integer_to_list(B) ++ "-" ++ integer_to_list(C),
- ?line FName = filename:join([?config(data_dir, Config), FTMP]),
- %% Ok, lets try with flush and follow_file, not a chance on VxWorks
- %% with NFS caching...
- ?line Port2 = dbg:trace_port(file, FName),
- ?line {ok, _} = dbg:tracer(port, Port2),
- try
- ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
- ?line {ok, _} = dbg:tp(dbg, ltp,[{'_',[],[{message, {self}}]}]),
- ?line {ok, _} = dbg:tp(dbg, ln, [{'_',[],[{message, hej}]}]),
- ?line ok = dbg:ltp(),
- ?line ok = dbg:flush_trace_port(),
- ?line dbg:trace_client(follow_file, FName,
- {fun myhandler/2, self()}),
- ?line S = self(),
- ?line [{trace,S,call,{dbg,ltp,[]},S}] = flush(),
- ?line ok = dbg:ln(),
- ?line ok = dbg:flush_trace_port(),
- ?line receive after 1000 -> ok end, %% Polls every second...
- ?line [{trace,S,call,{dbg,ln,[]},hej}] = flush(),
- ?line stop(),
- ?line [] = flush()
- after
- ?line stop(),
- ?line file:delete(FName)
- end,
- ok
- end.
+ stop(),
+ {A,B,C} = erlang:now(),
+ FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++
+ "-" ++ integer_to_list(B) ++ "-" ++ integer_to_list(C),
+ FName = filename:join([?config(data_dir, Config), FTMP]),
+ %% Ok, lets try with flush and follow_file, not a chance on VxWorks
+ %% with NFS caching...
+ Port2 = dbg:trace_port(file, FName),
+ {ok, _} = dbg:tracer(port, Port2),
+ try
+ {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ {ok, _} = dbg:tp(dbg, ltp,[{'_',[],[{message, {self}}]}]),
+ {ok, _} = dbg:tp(dbg, ln, [{'_',[],[{message, hej}]}]),
+ ok = dbg:ltp(),
+ ok = dbg:flush_trace_port(),
+ dbg:trace_client(follow_file, FName,
+ {fun myhandler/2, self()}),
+ S = self(),
+ [{trace,S,call,{dbg,ltp,[]},S}] = flush(),
+ ok = dbg:ln(),
+ ok = dbg:flush_trace_port(),
+ receive after 1000 -> ok end, %% Polls every second...
+ [{trace,S,call,{dbg,ln,[]},hej}] = flush(),
+ stop(),
+ [] = flush()
+ after
+ stop(),
+ file:delete(FName)
+ end,
+ ok.
file_port_schedfix(suite) ->
[];
diff --git a/lib/runtime_tools/test/inviso_SUITE.erl b/lib/runtime_tools/test/inviso_SUITE.erl
deleted file mode 100644
index c64c40b945..0000000000
--- a/lib/runtime_tools/test/inviso_SUITE.erl
+++ /dev/null
@@ -1,2838 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2012. 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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%% Description:
-%% Test suite for inviso (basic parts, i.e not inviso tools). Note that
-%% inviso basic parts have modules in both the runtime_tools and
-%% inviso applications.
-%%
-%% Authors:
-%% Ann-Marie L�f, [email protected]
-%% Lennart �hman, [email protected]
-%% -----------------------------------------------------------------------------
-
--module(inviso_SUITE).
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
--include_lib("kernel/include/file.hrl").
-
--define(l,?line).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
-
-all() ->
- [basic_dist_trace_1, basic_dist_trace_2,
- basic_dist_trace_3, basic_dist_trace_ti_1,
- basic_dist_trace_ti_2, basic_dist_trace_ti_3,
- suspend_dist_trace_ti_1, suspend_dist_trace_ti_2,
- meta_cleanfunc_dist_1, basic_handlerfun_dist_1,
- delete_log_dist_1, autostart_dist_1, autostart_dist_2,
- autostart_dist_3, running_alone_dist_1,
- running_alone_dist_2, running_alone_dist_3,
- running_alone_dist_4, running_alone_dist_5,
- overload_dist_1, overload_dist_2, overload_dist_3,
- overload_dist_4, overload_dist_5, subscribe_dist_1,
- lfm_trace_dist_1, lfm_trace_ti_dist_2,
- handle_logfile_sort_wrapset, fetch_log_dist_trace_1,
- fetch_log_dist_trace_2, fetch_log_dist_trace_3,
- fetch_log_dist_error_1, fetch_log_dist_error_2,
- expand_regexp_dist_1, only_loaded_dist_1].
-
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-
-init_per_suite(Config) ->
- case test_server:is_native(lists) of
- true ->
- {skip,"Native libs -- tracing doesn't work"};
- false ->
- %% We never know who messed up this node before this suite! :-)
- erlang:trace_pattern({'_','_','_'},[],[local]),
- erlang:trace_pattern({'_','_','_'},[],[global]),
- erlang:trace(all,false,[all]),
-
- ok=application:start(runtime_tools),
- Config
- end.
-
-end_per_suite(_Config) ->
- ?l ok=application:stop(runtime_tools).
-
-
-%% For each distributed testcase, we need two other distributed nodes to run the
-%% runtime components on. Since they are freshly started every time there is no
-%% need to clean them up first.
-init_per_testcase(_Case,Config) ->
- ?l TH=test_server:timetrap(100000),
- ?l {ok,Node1}=test_server:start_node(inviso1,peer,[]),
- ?l {ok,Node2}=test_server:start_node(inviso2,peer,[]),
- ?l SuiteDir=filename:dirname(code:which(?MODULE)),
-
- %% Otherwise peer nodes will not find this module!
- ?l true=rpc:call(Node1,code,add_patha,[SuiteDir]),
- ?l true=rpc:call(Node2,code,add_patha,[SuiteDir]),
-
- ?l start_side_effect_logger(node()),
- ?l start_side_effect_logger(Node1),
- ?l start_side_effect_logger(Node2),
-
-
- %% SPECIAL FOR MY PRIVATE TEST ENVIROMENT
-% ?l rpc:call(Node1,code,add_patha,["/clearcase/otp/tools/runtime_tools/ebin"]),
-% ?l rpc:call(Node1,code,add_patha,["/clearcase/otp/tools/inviso/ebin"]),
-% ?l rpc:call(Node2,code,add_patha,["/clearcase/otp/tools/runtime_tools/ebin"]),
-% ?l rpc:call(Node2,code,add_patha,["/clearcase/otp/tools/inviso/ebin"]),
-
-% %% SPECIAL FOR MY PRIVATE TEST ENVIROMENT, windows.
-% ?l rpc:call(Node1,code,add_patha,["Z:/DATA/PROJECTS/inviso_project/runtime_tools/ebin"]),
-% ?l rpc:call(Node1,code,add_patha,["Z:/DATA/PROJECTS/inviso_project/inviso/ebin"]),
-% ?l rpc:call(Node2,code,add_patha,["Z:/DATA/PROJECTS/inviso_project/runtime_tools/ebin"]),
-% ?l rpc:call(Node2,code,add_patha,["Z:/DATA/PROJECTS/inviso_project/inviso/ebin"]),
-
- ?l ok=rpc:call(Node1,application,start,[runtime_tools]),
- ?l ok=rpc:call(Node2,application,start,[runtime_tools]),
- ?l timer:sleep(100), % Problem with autostarted runtime.
- %% The following is a test that the inviso_rt processes which are autostarted
- %% are now gone.
-
- ?l ok=poll(rpc,call,[Node1,erlang,whereis,[inviso_rt]],undefined,20),
- ?l ok=poll(rpc,call,[Node2,erlang,whereis,[inviso_rt]],undefined,20),
-
-% ?l ok=poll(rpc,call,[Node1,supervisor,which_children,[runtime_tools_sup]],[],20),
-% ?l ok=poll(rpc,call,[Node2,supervisor,which_children,[runtime_tools_sup]],[],20),
- NewConfig1=insert_remotenode_config(inviso1,Node1,Config),
- NewConfig2=insert_remotenode_config(inviso2,Node2,NewConfig1),
- insert_timetraphandle_config(TH,NewConfig2).
-%% -----------------------------------------------------------------------------
-
-end_per_testcase(Case,Config) ->
- ?l test_server:stop_node(get_remotenode_config(inviso1,Config)),
- ?l test_server:stop_node(get_remotenode_config(inviso2,Config)),
-
- case whereis(inviso_c) of
- undefined -> % Should not exist.
- true;
- Pid when is_pid(Pid) -> % But if it exists...
- exit(Pid,kill), % Remove it!
- io:format("Had to kill the control component in end_per_testcase,~p.~n",[Case])
- end,
- case whereis(inviso_rt) of
- undefined -> % Should not exist.
- true;
- Pid2 when is_pid(Pid2) -> % But if it exists...
- exit(Pid2,kill), % Remove it!
- io:format("Had to kill local runtime component in end_per_testcase,~p.~n",[Case])
- end,
- ?l process_killer([inviso_test_proc,
- inviso_tab_proc,
- inviso_collector_proc,
- global_inviso_test_proc]),
- ?l test_server:timetrap_cancel(get_timetraphandle_config(Config)),
-
- NewConfig1=remove_remotenode_config(inviso1,Config),
- NewConfig2=remove_remotenode_config(inviso2,NewConfig1),
- remove_timetraphandle_config(NewConfig2).
-%% -----------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Testcases.
-%% ==============================================================================
-
-%% TEST CASE: Basic, distributed, trace only.
-basic_dist_trace_1(suite) -> [];
-basic_dist_trace_1(doc) ->
- ["Basic case, start of distributed tracing, using only trac."];
-basic_dist_trace_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=lists:map(fun(N)->{N,{file,filename:join([PrivDir,
- "tf1_"++
- atom_to_list(N)
- ])}} end,
- Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
- activate_local_tracing(Nodes),
- deactivate_local_tracing(Nodes),
- stop_tracing(Nodes),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-
-%% TEST CASE: Basic, distributed, activate global tracing for functions in modules
-%% pointed out using a regexp. No tracing will be done.
-basic_dist_trace_2(suite) -> [];
-basic_dist_trace_2(doc) ->
- [""];
-basic_dist_trace_2(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=lists:map(fun(N)->{N,{file,filename:join([PrivDir,
- "tf1a_"++
- atom_to_list(N)
- ])}} end,
- Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
- Funcs1=activate_global_tracing_regexp(Nodes),
- deactivate_global_tracing_regexp(Nodes,Funcs1),
- stop_tracing(Nodes),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Basic, distributed, activate global tracing for functions in modules
-%% pointed out using a dir-regexp. No tracing will be done.
-basic_dist_trace_3(suite) -> [];
-basic_dist_trace_3(doc) ->
- [""];
-basic_dist_trace_3(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=lists:map(fun(N)->{N,{file,filename:join([PrivDir,
- "tf1b_"++
- atom_to_list(N)
- ])}} end,
- Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
- Funcs1=activate_global_tracing_regexp_dir(Nodes),
- deactivate_global_tracing_regexp_dir(Nodes,Funcs1),
- stop_tracing(Nodes),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Basic, distributed, trace and ti.
-basic_dist_trace_ti_1(suite) -> [];
-basic_dist_trace_ti_1(doc) ->
- [""];
-basic_dist_trace_ti_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf2_"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"tf2_"++atom_to_list(N)++".ti"])}}]}
- end,
- TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- activate_local_tracing(Nodes),
- activate_meta_tracing(Nodes),
- ?l true=(is_pid(whereis(inviso_rt))),
- ?l true=(is_pid(whereis(inviso_rt_meta))),
- deactivate_meta_tracing(Nodes),
- deactivate_local_tracing(Nodes),
- stop_tracing(Nodes),
- ?l true=(is_pid(whereis(inviso_rt))), % Shall still be running.
- ?l ok=poll(erlang,whereis,[inviso_rt_meta],undefined,3),
- stop(Nodes),
- timer:sleep(200), % Give it time to terminate.
- ?l ok=poll(erlang,whereis,[inviso_rt],undefined,3),% Shall be gone now.
- ?l undefined=whereis(inviso_rt_meta), % Still gone.
- ok.
-%% -----------------------------------------------------------------------------
-
-%% Test CASE: Testing that the tpm_tracer functionality works. That is appending
-%% {tracer,Tracer} to a meta match spec.
-basic_dist_trace_ti_2(suite) -> [];
-basic_dist_trace_ti_2(doc) ->
- [""];
-basic_dist_trace_ti_2(Config) when is_list(Config) ->
- case erlang:system_info(version) of
- "5.4"++_ -> % Perhaps not perfect, but work now :-)
- {skip,"Old emulator"};
- _ ->
- basic_dist_trace_ti_2_do(Config)
- end.
-
-basic_dist_trace_ti_2_do(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf3_"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"tf3_"++atom_to_list(N)++".ti"])}}]}
- end,
- TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- activate_deactivate_meta_tracing_tracer(Nodes),
- stop_tracing(Nodes),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Basic, distributed, trace and ti, where we try to use ctp_all to
-%% check that all global and local patterns are removed but that meta patterns
-%% remain.
-%% This test also checks that if the meta tracer is terminated an error value
-%% is generated when trying to do meta tracing at that node.
-basic_dist_trace_ti_3(suite) -> [];
-basic_dist_trace_ti_3(doc) ->
- [""];
-basic_dist_trace_ti_3(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf4_"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"tf4_"++atom_to_list(N)++".ti"])}}]}
- end,
- TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- activate_local_tracing(Nodes),
- activate_global_tracing(Nodes),
- activate_meta_tracing(Nodes),
- ?l true=(is_pid(whereis(inviso_rt))),
- ?l true=(is_pid(whereis(inviso_rt_meta))),
- ?l {ok,NodeResults1}=inviso:ctp_all(Nodes), % Removes local and global patterns.
- ?l true=check_noderesults(Nodes,ok,NodeResults1),
- ?l true=check_on_nodes(Nodes,erlang,trace_info,[{code,which,1},traced],{traced,false}),
- ?l true=check_on_nodes(Nodes,erlang,trace_info,[{code,get_path,0},traced],{traced,false}),
- %% But meta patters shall remain.
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{lists,module_info,0},meta_match_spec],
- fun({meta_match_spec,L})when length(L)>0 ->true end),
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{lists,module_info,0},meta],
- fun({meta,P})when is_pid(P) ->
- P=rpc:call(node(P),erlang,whereis,[inviso_rt_meta]),
- true
- end),
- %% Now kill the meta tracer somewhere and try to activate meta tracing.
- ?l [ANode|_]=Nodes,
- ?l AMetaPid=rpc:call(ANode,erlang,whereis,[inviso_rt_meta]),
- ?l rpc:call(ANode,erlang,exit,[AMetaPid,kill]),
- ?l {ok,NodeResults2}=inviso:tpm(Nodes,math,pi,0,[],void),
- ?l {value,{ANode,{error,_}}}=lists:keysearch(ANode,1,NodeResults2),
-
- ?l stop_tracing(Nodes),
- ?l stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Test cases for SUSPEND
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: In this test case a trace with ti is started. Trace flags are set,
-%% trace patterns are set and meta trace patterns. We then check that the trace
-%% flags and the meta patterns are removed when tracing suspended.
-%% The suspension is cancelled and we check that it is possible to reactivate
-%% tracing by setting the process flags and meta patterns again.
-suspend_dist_trace_ti_1(suite) -> [];
-suspend_dist_trace_ti_1(doc) ->
- [""];
-suspend_dist_trace_ti_1(Config) when is_list(Config) ->
- ?l RemoteNodes=get_remotenodes_config(Config),
- ?l Nodes=[node()|RemoteNodes],
- ?l PrivDir=filename:join(?config(priv_dir,Config),""),
- ?l TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_suspend1_"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"tf_suspend1_"++atom_to_list(N)++".ti"])}}]}
- end,
- ?l TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- activate_local_tracing(Nodes),
- activate_meta_tracing(Nodes),
- ?l true=(is_pid(whereis(inviso_rt))),
- ?l true=(is_pid(whereis(inviso_rt_meta))),
- %% Set some trace flags on some newly started test procs.
- activate_traceflags(Nodes),
-
- %% Now suspend the tracing on all nodes. That shall result in the removal
- %% of trace flags and meta trace patterns, but not local trace patterns.
- ?l {ok,NodeResults1}=inviso:suspend(Nodes,test),
- ?l true=check_noderesults(Nodes,ok,NodeResults1),
- %% Trace flags gone?
- ?l TestProcs=lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_test_proc]) end,Nodes),
- ?l lists:foreach(fun(P)->
- {flags,[]}=
- rpc:call(node(P),erlang,trace_info,[P,flags])
- end,
- TestProcs),
- %% Meta patterns shall be gone too, but local functions still there.
- ?l lists:foreach(fun(N)->
- {meta,false}=
- rpc:call(N,
- erlang,
- trace_info,
- [{math,module_info,1},meta]),
- {traced,local}=
- rpc:call(N,
- erlang,
- trace_info,
- [{code,which,1},traced])
- end,
- Nodes),
-
- %% Try to activate trace flags, trace patterns and meta tracing while
- %% suspended. Should not succeed of course!
- ?l ThisNode=node(),
- ?l {ok,[{ThisNode,{error,suspended}}]}=
- inviso:tf([ThisNode],inviso_test_proc,[call]),
- ?l {ok,[{ThisNode,{error,suspended}}]}=
- inviso:tpl([ThisNode],math,module_info,1,[]),
- ?l {ok,[{ThisNode,{error,suspended}}]}=
- inviso:init_tpm([ThisNode],
- math,
- module_info,
- 1,
- {?MODULE,tpm_init_func2}, % Does not exist on purpose.
- {?MODULE,tpm_call_func2}, % Does not exist on purpose.
- {?MODULE,tpm_return_func2}, % Does not exist on purpose.
- {?MODULE,tpm_remove_func2}), % Does not exist on purpose.
-
- %% Now we want to cancel suspension and see that we can reactivate tracing.
- ?l {ok,NodeResults2}=inviso:cancel_suspension(Nodes),
- ?l true=check_noderesults(Nodes,ok,NodeResults2),
-
- ?l {ok,NodeResults3}=
- inviso:init_tpm(math,
- module_info,
- 1,
- {?MODULE,tpm_init_func2}, % Does not exist on purpose.
- {?MODULE,tpm_call_func2}, % Does not exist on purpose.
- {?MODULE,tpm_return_func2}, % Does not exist on purpose.
- {?MODULE,tpm_remove_func2}), % Does not exist on purpose.
- ?l true=check_noderesults(Nodes,ok,NodeResults3),
- ?l {ok,NodeResults5}=
- inviso:tpm_ms(math,module_info,1,ms1,[{'_',[],[{return_trace}]}]),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults5),
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{math,module_info,1},meta_match_spec],
- {meta_match_spec,[{'_',[],[{return_trace}]}]}),
- ?l {ok,NodeResults6}=inviso:tf(Nodes,inviso_test_proc,[call]),
- ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults6),
-
- %deactivate_meta_tracing(Nodes),
- %deactivate_local_tracing(Nodes),
- stop_tracing(Nodes),
- ?l true=(is_pid(whereis(inviso_rt))), % Shall still be running.
- ?l ok=poll(erlang,whereis,[inviso_rt_meta],undefined,3),
- stop(Nodes),
- ?l timer:sleep(200), % Give it time to terminate.
- ?l ok=poll(erlang,whereis,[inviso_rt],undefined,3),% Shall be gone now.
- ?l undefined=whereis(inviso_rt_meta), % Still gone.
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: In this test case a trace with ti is started. Trace flags are set,
-%% trace patterns are set and meta trace patterns. We then suspend tracing at
-%% all nodes, then stop tracing which shall be allowed. We then try to initiate
-%% tracing again which shall not be possible.
-suspend_dist_trace_ti_2(suite) -> [];
-suspend_dist_trace_ti_2(doc) ->
- [""];
-suspend_dist_trace_ti_2(Config) when is_list(Config) ->
- ?l RemoteNodes=get_remotenodes_config(Config),
- ?l Nodes=[node()|RemoteNodes],
- ?l PrivDir=filename:join(?config(priv_dir,Config),""),
- ?l TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_suspend2_"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"tf_suspend2_"++atom_to_list(N)++".ti"])}}]}
- end,
- ?l TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- activate_local_tracing(Nodes),
- activate_meta_tracing(Nodes),
- ?l true=(is_pid(whereis(inviso_rt))),
- ?l true=(is_pid(whereis(inviso_rt_meta))),
- %% Set some trace flags on some newly started test procs.
- activate_traceflags(Nodes),
-
- %% Now suspend the tracing on all nodes. That shall result in the removal
- %% of trace flags and meta trace patterns, but not local trace patterns.
- ?l {ok,NodeResults1}=inviso:suspend(Nodes,test),
- ?l true=check_noderesults(Nodes,ok,NodeResults1),
-
- %% Now stop tracing.
- ?l {ok,NodeResults3}=inviso:stop_tracing(Nodes),
- ?l true=check_noderesults(Nodes,{ok,idle},NodeResults3),
- %% Now try to initiate tracing again.
- ThisNode=node(),
- ?l {ok,[{ThisNode,{error,suspended}}]}=
- inviso:init_tracing([ThisNode],
- [{trace,{file,filename:join([PrivDir,"tf_suspend3_"++
- atom_to_list(ThisNode)])}},
- {ti,{file,{filename:join([PrivDir,"tf_suspend3_"++
- atom_to_list(ThisNode)])}}}]),
-
- %% Cancel the suspension and initiate tracing again.
- ?l {ok,NodeResults2}=inviso:cancel_suspension(Nodes),
- ?l true=check_noderesults(Nodes,ok,NodeResults2),
- ?l TracerDataFun2=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_suspend4_"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"tf_suspend4_"++atom_to_list(N)++".ti"])}}]}
- end,
- ?l TracerDataList2=lists:map(TracerDataFun2,Nodes),
- ?l {ok,NodeResults4}=inviso:init_tracing(TracerDataList2),
- ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok},{ti_log,ok}]},NodeResults4),
- stop_tracing(Nodes),
- ?l true=(is_pid(whereis(inviso_rt))), % Shall still be running.
- stop(Nodes),
- ?l timer:sleep(200), % Give it time to terminate.
- ?l ok=poll(erlang,whereis,[inviso_rt],undefined,3),% Shall be gone now.
- ok.
-%% -----------------------------------------------------------------------------
-
-
-
-%% TEST CASE: This test case tests that the clean function removes (prosumed)
-%% expired data from the internal public-loopdata structure in the inviso_rt_meta
-%% process.
-meta_cleanfunc_dist_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"mcf1_"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"mcf1_"++atom_to_list(N)++".ti"])}}]}
- end,
- TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- %% Now initialize meta tracing, but the call_func is a bit "fixed".
- ?l {ok,NodeResults1}=
- inviso:tpm(Nodes,math,module_info,1,[],
- {?MODULE,meta_cleanfunc_initfunc_1},
- {?MODULE,meta_cleanfunc_callfunc_1},
- void,void),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults1),
- %% Nothing in the "our" part of the public loop data.
- ?l true=check_on_nodes(Nodes,
- inviso_rt_meta,get_state,[inviso_rt_meta],
- fun({ok,_LD,{{_,[]},_}})->true end),
- ?l lists:foreach(fun(N)->rpc:call(N,math,module_info,[exports]) end,Nodes),
- %% Check that it has been added to the public loopdata structure.
- ?l true=check_on_nodes(Nodes,
- ?MODULE,poll,[inviso_rt_meta,
- get_state,
- [inviso_rt_meta],
- fun({ok,_LD,{{_,[{meta_cleanfunc_test1,_Now}]},_}})->
- true;
- (_)->false
- end,
- 20],
- ok),
- %% While we wait for 60 seconds to pass, we test a few other things.
- ?l {ok,NodeResults2}=
- inviso:tpm(Nodes,?MODULE,slowfunction2,0,[{'_',[],[{return_trace}]}],
- {?MODULE,meta_cleanfunc_initfunc_2},
- {?MODULE,meta_cleanfunc_callfunc_2},
- {?MODULE,meta_cleanfunc_returnfunc_2},
- void),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults2),
- ?l lists:foreach(fun(N)->rpc:call(N,?MODULE,slowfunction,[]) end,Nodes),
- %% Believe it or not but slowfunction is still running, in its own process,
- %% we are therefore free now to examine the meta tracer.
- ?l true=check_on_nodes(Nodes,
- ?MODULE,poll,[inviso_rt_meta,
- get_state,
- [inviso_rt_meta],
- fun({ok,_LD,{{[],Tuples},_}})->
- {value,_}=
- lists:keysearch(meta_cleanfunc_test2,
- 1,
- Tuples),
- {value,_}=
- lists:keysearch(meta_cleanfunc_test1,
- 1,
- Tuples),
- true;
- (_)->
- false
- end,
- 20],
- ok),
- %% Now we wait for slowfunction to return and that the meta_cleanfunc_test2
- %% to be removed from public loopdata strucuture.
- ?l timer:sleep(10000),
- %% The only thing remaining should be the meta_cleanfunc_test1 which will not
- %% go away for less than that the clean functionality removes it.
- ?l true=check_on_nodes(Nodes,
- ?MODULE,poll,[inviso_rt_meta,
- get_state,
- [inviso_rt_meta],
- fun({ok,_LD,{{_,[{meta_cleanfunc_test1,_Now}]},_}})->
- true;
- (_)->
- false
- end,
- 20],
- ok),
- %% Wait for the clean function to clean meta_cleanfunc_test1 away.
- ?l timer:sleep(51000), % Shall be gone after 5 seconds.
- ?l true=check_on_nodes(Nodes,
- ?MODULE,poll,[inviso_rt_meta,
- get_state,
- [inviso_rt_meta],
- fun({ok,_LD,{{_,[]},_}})->true;
- (_)->false
- end,
- 20],
- ok),
- stop_tracing(Nodes),
- stop(Nodes),
- ok.
-
-%% This function acts as tpm initialization function when we are going to test
-%% that the clean function works. Note that we here assume standard public loop
-%% datastructure.
-meta_cleanfunc_initfunc_1(_M,_F,_Arity,{E1,_E2}) ->
- {ok,{E1,[]},void}.
-%% Function that is supposed to be called when the meta traced function is
-%% called.
-meta_cleanfunc_callfunc_1(_Pid,_Args,{{E1,E2},Global}) ->
- {ok,{{E1,[{meta_cleanfunc_test1,now()}|E2]},Global},void}.
-
-meta_cleanfunc_initfunc_2(_M,_F,_Arity,PublLD) ->
- {ok,PublLD,void}.
-meta_cleanfunc_callfunc_2(_Pid,_Args,{{E1,E2},Global}) ->
- {ok,{{E1,[{meta_cleanfunc_test2,now()}|E2]},Global},void}.
-meta_cleanfunc_returnfunc_2(_Pid,_,{{E1,E2},Global}) ->
- {value,_}=lists:keysearch(meta_cleanfunc_test2,1,E2),
- {ok,{{E1,lists:keydelete(meta_cleanfunc_test2,1,E2)},Global},void}.
-
-slowfunction() ->
- spawn(?MODULE,slowfunction1,[]).
-slowfunction1() ->
- slowfunction2(). % Meta trace on this function call.
-slowfunction2() ->
- timer:sleep(2000),
- true.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Testing that a runtime component can be started instructing it
-%% to use a handler fun. Checks that the handler fun is called if a trace
-%% message comes in.
-basic_handlerfun_dist_1(suite) -> [];
-basic_handlerfun_dist_1(doc) ->
- [""];
-basic_handlerfun_dist_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l lists:foreach(fun(N)->rpc:call(N,ets,insert,[inviso_sideeffect_tab,{bhf1,0}]) end,
- Nodes),
- TracerDataFun=
- fun(N)->{N,{fun basic_handlerfun_dist_1_fun/2,inviso_sideeffect_tab}} end,
- TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
- activate_local_tracing(Nodes),
- activate_traceflags(Nodes),
- ?l lists:foreach(fun(N)->[{bhf1,0}]=
- rpc:call(N,ets,lookup,[inviso_sideeffect_tab,bhf1])
- end,
- Nodes),
- ?l inviso_test_proc ! {apply,code,which,[lists]},
- ok=poll(ets,lookup,[inviso_sideeffect_tab,bhf1],[{bhf1,1}],20),
- deactivate_traceflags(Nodes),
- deactivate_local_tracing(Nodes),
- stop_tracing(Nodes),
- timer:sleep(100),
- ?l [{bhf1,1}]=ets:lookup(inviso_sideeffect_tab,bhf1),
- stop(Nodes),
- ok.
-
-%% Function used as handler fun for testcase above.
-basic_handlerfun_dist_1_fun(_Msg,TId) ->
- ets:update_counter(TId,bhf1,1),
- TId.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Here we test that delete_log removes the files at the involved
-%% runtime nodes. In this case we test that we remove logs according to last
-%% used tracer data.
-delete_log_dist_1(suite) -> [];
-delete_log_dist_1(doc) -> [""];
-delete_log_dist_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"dl1_"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"dl1_"++atom_to_list(N)++".ti"])}}]}
- end,
- TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- ?l Files=lists:map(fun({N,TD})->
- ?l {value,{_,{_,TraceFile}}}=lists:keysearch(trace,1,TD),
- ?l {value,{_,{_,TiFile}}}=lists:keysearch(ti,1,TD),
- ?l {N,{TraceFile,TiFile}}
- end,
- TracerDataList),
- io:format("The Files is:~w~n",[Files]),
- ?l {ok,NodeResults1}=inviso:delete_log(Nodes), % Should not work!
- ?l true=check_noderesults(Nodes,{error,tracing},NodeResults1),
- stop_tracing(Nodes),
- %% Files still here.
- ?l lists:foreach(fun({N,{F1,F2}})->
- ?l {ok,_}=rpc:call(N,file,read_file_info,[F1]),
- ?l {ok,_}=rpc:call(N,file,read_file_info,[F2])
- end,
- Files),
- ?l {ok,NodeResults2}=inviso:delete_log(Nodes),
- ?l true=check_noderesults(Nodes,
- fun({_N,{ok,LogInfos}})->
- ?l {value,{_,[{ok,_FName1}]}}=
- lists:keysearch(trace_log,1,LogInfos),
- ?l {value,{_,[{ok,_FName2}]}}=
- lists:keysearch(ti_log,1,LogInfos),
- true
- end,
- NodeResults2),
- %% The files shall be gone now.
- ?l lists:foreach(fun({N,{F1,F2}})->
- ?l {error,enoent}=rpc:call(N,file,read_file_info,[F1]),
- ?l {error,enoent}=rpc:call(N,file,read_file_info,[F2])
- end,
- Files),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-
-%% TEST CASE: Test of the autostart behaviour of the runtime component.
-%% Here we test that a runtime component is started according to the autostart.conf
-%% file. Note that the repeat parameter is set to 2.
-autostart_dist_1(suite) -> [];
-autostart_dist_1(doc) ->
- [""];
-autostart_dist_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
- AutoConfFile=filename:join(PrivDir,"autostart1.conf"),
- [RNode|_]=RemoteNodes,
- ?l ok=rpc:call(RNode,application,stop,[runtime_tools]),
- ?l ok=rpc:call(RNode,application,set_env,[runtime_tools,
- inviso_autostart_conf,
- AutoConfFile]),
- ?l {ok,FD}=file:open(AutoConfFile,[write]),
- ?l ok=io:format(FD,"~w.~n~w.~n",[{repeat,2},{tag,c_ref}]),
- ?l file:close(FD),
- ?l ok=rpc:call(RNode,application,start,[runtime_tools]),
- timer:sleep(1000),
- ?l P1=rpc:call(RNode,erlang,whereis,[inviso_rt]),
- ?l true=is_pid(P1),
- ?l rpc:call(RNode,erlang,exit,[P1,kill]),
- ?l ok=rpc:call(RNode,application,stop,[runtime_tools]),
- ?l ok=rpc:call(RNode,application,start,[runtime_tools]),
- timer:sleep(1000),
- ?l P2=rpc:call(RNode,erlang,whereis,[inviso_rt]),
- ?l true=is_pid(P2),
- ?l rpc:call(RNode,erlang,exit,[P2,kill]),
- ?l ok=rpc:call(RNode,application,stop,[runtime_tools]),
- ?l ok=rpc:call(RNode,application,start,[runtime_tools]),
- timer:sleep(1000),
- ?l undefined=rpc:call(RNode,erlang,whereis,[inviso_rt]),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Test of autostart. Here we focus on that an autostarted
-%% runtime component actually follows the trace case command file and
-%% initiates tracing.
-autostart_dist_2(suite) -> [];
-autostart_dist_2(doc) ->
- [""];
-autostart_dist_2(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
- AutoConfFile=filename:join(PrivDir,"autostart2.conf"),
- [RNode|_]=RemoteNodes,
- ?l ok=rpc:call(RNode,application,stop,[runtime_tools]),
- ?l ok=rpc:call(RNode,application,set_env,[runtime_tools,
- inviso_autostart_conf,
- AutoConfFile]),
- ?l CmdFileName=filename:join(PrivDir,"autostart_cmd_as1"),
- ?l {ok,FD}=file:open(CmdFileName,[write]),
- ?l ok=io:format(FD,
- "inviso:tpl(Nodes,M,F,Arity,[]).~n"
- "inviso:tf(Nodes,inviso_test_proc,[call]).~n",
- []),
- ?l file:close(FD),
- ?l TraceFileName=filename:join([PrivDir,"as1_"++atom_to_list(RNode)]),
- ?l TiFileName=filename:join([PrivDir,"as1_"++atom_to_list(RNode)++".ti"]),
- ?l inviso_as_lib:setup_autostart(RNode,
- 2,
- [],
- [{trace,{file,TraceFileName}},
- {ti,{file,TiFileName}}],
- [[CmdFileName]],
- [{'M',code},{'F',which},{'Arity',1}],
- [{{inviso,tpl,5},{inviso_rt,tpl,{erlang,tl}}},
- {{inviso,tf,3},{inviso_rt,tf,{erlang,tl}}}]),
- ?l TestP=spawn(RNode,?MODULE,test_proc_init,[]),
- ?l ok=rpc:call(RNode,application,start,[runtime_tools]),
- ?l timer:sleep(1000),
- ?l {ok,_}=file:read_file_info(TraceFileName),
- ?l {ok,_}=file:read_file_info(TiFileName),
- ?l true=is_pid(P=rpc:call(RNode,erlang,whereis,[inviso_rt])),
- ?l ok=poll(rpc,call,[RNode,erlang,trace_info,[{code,which,1},traced]],{traced,local},10),
- ?l {flags,[call]}=rpc:call(RNode,erlang,trace_info,[TestP,flags]),
- ?l rpc:call(RNode,erlang,exit,[P,kill]),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Here we test that an autostarted runtime component with a dependency
-%% to a specific control component tries to connect to that control component
-%% during its start-up.
-autostart_dist_3(suite) -> [];
-autostart_dist_3(doc) ->
- [""];
-autostart_dist_3(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
- AutoConfFile=filename:join(PrivDir,"autostart3.conf"),
- [RNode|_]=RemoteNodes,
- ?l ok=rpc:call(RNode,application,stop,[runtime_tools]),
- ?l ok=rpc:call(RNode,application,set_env,[runtime_tools,
- inviso_autostart_conf,
- AutoConfFile]),
- ?l {ok,FD}=file:open(AutoConfFile,[write]),
- ?l ok=io:format(FD,"~w.~n~w.~n~w.~n",
- [{options,[{dependency,{infinity,node()}}]},{repeat,2},{tag,c_ref}]),
- ?l file:close(FD),
- %% Now start inviso at this node here for the runtime to connect.
- ?l {ok,_Pid}=inviso:start(),
- ?l ok=poll(erlang,whereis,[inviso_c],fun(P) when is_pid(P)->true;(_)->false end,10),
- %% Make the runtime component start.
- ?l ok=rpc:call(RNode,application,start,[runtime_tools]),
- ?l ok=poll(rpc,call,[RNode,erlang,whereis,[inviso_rt]],
- fun(P) when is_pid(P)->true;(_)->false end,10),
- %% Check that the runtime component started.
- ?l ok=poll(inviso,get_status,[[RNode]],{ok,[{RNode,{ok,{new,running}}}]},20),
-% ?l {ok,[{RNode,{ok,{new,running}}}]}=inviso:get_status([RNode]),
- stop([RNode]),
- ok.
-%% -----------------------------------------------------------------------------
-
-
-
-%% TEST CASE: Test of the dependency mechanism in the runtime component.
-%% Default behaviour is dependency=infinity, i.e the runtime components remains.
-%% We also test here that we can reconnect to the runtime.
-running_alone_dist_1(suite) -> [];
-running_alone_dist_1(doc) ->
- [""];
-running_alone_dist_1(Config) when is_list(Config) ->
- ?l {ok,_Pid1}=inviso:start(), % Start a control component.
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,[]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l shutdown=inviso:stop(), % Stop the control component!
- ?l undefined=whereis(inviso_c),
- timer:sleep(3000), % How long shall we wait? :-)
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
- Nodes),
- ?l {ok,_Pid2}=inviso:start(),
- ?l {ok,NodeResults2}=inviso:add_nodes(Nodes,b_ref,[]),
- ?l true=check_noderesults(Nodes,{ok,{adopted,new,running,a_ref}},NodeResults2),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Test of the dependency mechanism in the runtime component.
-%% Test that the runtime components terminates after the specified 5000 ms.
-running_alone_dist_2(suite) -> [];
-running_alone_dist_2(doc) ->
- [""];
-running_alone_dist_2(Config) when is_list(Config) ->
- ?l {ok,_Pid1}=inviso:start(), % Start a control component.
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,[{dependency,5000}]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l shutdown=inviso:stop(), % Stop the control component!
- ?l undefined=whereis(inviso_c),
- timer:sleep(2000),
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
- Nodes),
- timer:sleep(4000), % Now they shall be dead!
- ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt]) end,
- Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Test of the dependency mechanism in the runtime component.
-%% Test that the runtime components terminates after the specified 5000 ms.
-running_alone_dist_3(suite) -> [];
-running_alone_dist_3(doc) ->
- [""];
-running_alone_dist_3(Config) when is_list(Config) ->
- ?l {ok,_Pid1}=inviso:start(), % Start a control component.
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,[{dependency,1000}]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l {ok,NodeResults2}=inviso:change_options(Nodes,[{dependency,5000}]),
- ?l true=check_noderesults(Nodes,ok,NodeResults2),
- ?l shutdown=inviso:stop(), % Stop the control component!
- ?l undefined=whereis(inviso_c),
- timer:sleep(3000),
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
- Nodes),
- timer:sleep(3000), % Now they shall be dead!
- ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt]) end,
- Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Test of the dependency mechanism in the runtime component.
-%% Test that the runtime components terminates after the specified 5000 ms,
-%% like we did in running_alone_dist_2. But now we also start tracing and checks
-%% that all inviso processes actually disappears when the time-out is reached.
-running_alone_dist_4(suite) -> [];
-running_alone_dist_4(doc) ->
- [""];
-running_alone_dist_4(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- %% Start some tracing!
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_ra4"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"tf_ra4_"++atom_to_list(N)++".ti"])}}]}
- end,
- TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,
- [{dependency,5000}],
- TracerDataList,
- {ok,[{trace_log,ok},{ti_log,ok}]}),
-
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
- Nodes),
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt_meta])) end,
- Nodes),
- %% Stop control component and wait for the runtimes to terminate after
- %% running alone timer has expired.
- ?l shutdown=inviso:stop(), % Stop the control component!
- ?l undefined=whereis(inviso_c),
- timer:sleep(2000),
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
- Nodes),
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt_meta])) end,
- Nodes),
- timer:sleep(4000), % Now they shall be dead!
- ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt]) end,
- Nodes),
- ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt_meta]) end,
- Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Test of the dependency mechanism in the runtime component.
-%% Test that the runtime components terminates imeediately when the control
-%% component is stopped. Check that all processes are gone.
-running_alone_dist_5(suite) -> [];
-running_alone_dist_5(doc) ->
- [""];
-running_alone_dist_5(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- %% Start some tracing!
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_ra5"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"tf_ra5_"++atom_to_list(N)++".ti"])}}]}
- end,
- TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,
- [{dependency,0}],
- TracerDataList,
- {ok,[{trace_log,ok},{ti_log,ok}]}),
-
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
- Nodes),
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt_meta])) end,
- Nodes),
- %% Stop control component and check that all runtime component processes have
- %% terminate more or less immediately afterwards, since dependency==0.
- ?l shutdown=inviso:stop(), % Stop the control component!
- timer:sleep(100),
- ?l undefined=whereis(inviso_c),
- timer:sleep(500),
- ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt]) end,
- Nodes),
- ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt_meta]) end,
- Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Test of the overload protection mechanism. The mechanism checks
-%% for overload using the callback approximately at the interval specified.
-%% Check that it does not start protection until start of tracing.
-overload_dist_1(suite) -> [];
-overload_dist_1(doc) ->
- [""];
-overload_dist_1(Config) when is_list(Config) ->
- ?l {ok,_Pid1}=inviso:start(), % Start a control component.
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l lists:foreach(fun(N)->true=rpc:call(N,ets,insert,[inviso_sideeffect_tab,{ovl1,0}]) end,
- Nodes), % Initiate the counter.
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,
- a_ref,
- [{overload,{{?MODULE,overload1},500}}]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- timer:sleep(1000), % Give the loadcheck time to perform.
- ?l [{_,0}]=ets:lookup(inviso_sideeffect_tab,ovl1), % Nothing should have happened.
-
- %% Overload check shall not start until we start tracing.
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=lists:map(fun(N)->{N,[{trace,
- {file,filename:join([PrivDir,
- "tf_ovl1."++atom_to_list(N)
- ])}}]}
- end,
- Nodes),
- ?l {ok,NodeResults2}=inviso:init_tracing(TracerDataList),
- ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults2),
- timer:sleep(1500), % Give the loadcheck time to perform.
- ?l [{_,N}]=ets:lookup(inviso_sideeffect_tab,ovl1),
- ?l true=(N>=2), % After 1,5 seconds, at least 2 checks.
-
- %% Now change options and remove overload checking!
- ?l {ok,NodeResults3}=inviso:change_options(Nodes,[overload]),
- ?l true=check_noderesults(Nodes,ok,NodeResults3),
- ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl1),
- timer:sleep(1000),
- ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl1), % No more loadchecks!
-
- stop_tracing(Nodes),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Test of the overload protection mechanism. In this case we focus
-%% in that the init and remove functions are carried out at change_options and
-%% when starting and stoping the runtime component.
-overload_dist_2(suite) -> [];
-overload_dist_2(doc) ->
- [""];
-overload_dist_2(Config) when is_list(Config) ->
- ?l {ok,_Pid1}=inviso:start(), % Start a control component.
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,
- a_ref,
- [{overload,{{?MODULE,overload2},
- 500,
- {?MODULE,overload2i,[]},
- {?MODULE,overload2r,[]}}}]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l [{_,0}]=ets:lookup(inviso_sideeffect_tab,ovl2),
-
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=lists:map(fun(N)->{N,[{trace,
- {file,filename:join([PrivDir,
- "tf_ovl2."++atom_to_list(N)
- ])}}]}
- end,
- Nodes),
- ?l {ok,NodeResults2}=inviso:init_tracing(TracerDataList),
- ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults2),
- timer:sleep(1500), % Give the loadcheck time to perform.
- ?l [{_,N}]=ets:lookup(inviso_sideeffect_tab,ovl2),
- io:format("� is:~p~n",[N]),
- ?l true=(N>=2), % After 1,5 seconds, at least 2 checks.
- ?l {ok,NodeResults3}=inviso:change_options(Nodes,[{overload,{{?MODULE,overload3},
- 500,
- {?MODULE,overload3i,[]},
- {?MODULE,overload3r,[]}}}]),
- ?l true=check_noderesults(Nodes,ok,NodeResults3),
- ?l []=ets:lookup(inviso_sideeffect_tab,ovl2),
- timer:sleep(1500),
- ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl3),
- ?l true=(N2>=2), % After 1,5 seconds, at least 2 checks.
- stop_tracing(Nodes),
- ?l []=ets:lookup(inviso_sideeffect_tab,ovl3r), % Remove function shall not be called.
- ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl3),
- timer:sleep(1000), % Check that overloadchecking has stopped.
- ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl3),
- stop(Nodes),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,ovl3r],[{ovl3r,done}],20),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Test of the overload protections mechanism. Here we focus on testing
-%% that if overload is reached tracing is really suspended.
-overload_dist_3(suite) -> [];
-overload_dist_3(doc) ->
- [""];
-overload_dist_3(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=
- lists:map(fun(N)->{N,[{trace,{file,filename:join([PrivDir,
- "tf_ovl3."++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,
- "tf_ovl3_ti."++atom_to_list(N)])}}]}
- end,
- Nodes),
- ?l lists:foreach(fun(N)->
- true=rpc:call(N,ets,insert,[inviso_sideeffect_tab,{ovl4,0}])
- end,
- Nodes),
- start_and_init_tracing2(Nodes,
- [{overload,{{?MODULE,overload4},500}}],
- TracerDataList,
- {ok,[{trace_log,ok},{ti_log,ok}]}),
- activate_local_tracing(Nodes),
- activate_meta_tracing(Nodes),
- activate_traceflags(Nodes),
- timer:sleep(600),
- ?l [{_,N1}]=ets:lookup(inviso_sideeffect_tab,ovl4),
- ?l true=(N1>=1), % Overload check has been done!
- ?l Node=node(),
- ?l {ok,[{Node,{ok,{tracing,running}}}]}=inviso:get_status([node()]),
- ?l true=ets:insert(inviso_sideeffect_tab,{ovl4_suspend,true}),
- timer:sleep(600),
- ?l {ok,[{Node,{ok,{tracing,{suspended,test}}}}]}=inviso:get_status([node()]),
- ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl4),
- ?l {flags,[]}=erlang:trace_info(whereis(inviso_test_proc),flags),
- ?l {meta,false}=erlang:trace_info({lists,module_info,0},meta),
- ?l {traced,local}=erlang:trace_info({code,which,1},traced),
- ?l true=(is_pid(whereis(inviso_rt_meta))),
- ?l true=ets:delete(inviso_sideeffect_tab,ovl4_suspend),
- timer:sleep(600),
- ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl4), % No checking while suspended!
- ?l {ok,[{Node,ok}]}=inviso:cancel_suspension([node()]),
- ?l {ok,NodeResults1}=inviso:get_status(Nodes),
- ?l true=check_noderesults(Nodes,{ok,{tracing,running}},NodeResults1),
- timer:sleep(600),
- ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl4),
- ?l true=(N3>N2),
- ?l deactivate_local_tracing(Nodes),
- ?l stop_tracing(Nodes),
- ?l stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE. Test that the overload mechanism is triggered by to the runtime
-%% component incomming messages, and nothing else.
-overload_dist_4(suite) -> [];
-overload_dist_4(doc) ->
- [""];
-overload_dist_4(Config) when is_list(Config) ->
- ?l {ok,_Pid1}=inviso:start(), % Start a control component.
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,
- a_ref,
- [{overload,{{?MODULE,overload5},
- infinity,
- {?MODULE,overload5i,[]},
- {?MODULE,overload5r,[]}}}]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l [{_,0}]=ets:lookup(inviso_sideeffect_tab,ovl5),
-
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=lists:map(fun(N)->{N,[{trace,
- {file,filename:join([PrivDir,
- "tf_ovl4."++atom_to_list(N)
- ])}}]}
- end,
- Nodes),
- ?l {ok,NodeResults2}=inviso:init_tracing(TracerDataList),
- ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults2),
- timer:sleep(2000), % Give the loadcheck time to perform.
- ?l [{_,N}]=ets:lookup(inviso_sideeffect_tab,ovl5),
- ?l true=(N==0), % And nothing shall have happend!
- %% Now we send a message to the inviso_rt, then the load check function
- %% shall be called.
- ?l whereis(inviso_rt) ! test_of_loadcheck,
- timer:sleep(200), % Make sure the inviso_rt gets scheduled.
- ?l [{_,1}]=ets:lookup(inviso_sideeffect_tab,ovl5),
- stop_tracing(Nodes),
- ?l []=ets:lookup(inviso_sideeffect_tab,ovl5r), % Remove function shall not be called.
- ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl5),
- ?l whereis(inviso_rt) ! test_of_loadcheck,
- timer:sleep(1000), % Check that overloadchecking has stopped.
- ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl5),
- stop(Nodes),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,ovl5r],[{ovl5r,done}],20),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE. Test that the overload mechanism correctly calculates remaining time
-%% to next load check if a message comes into the runtime component "interupting"
-%% the waiting for loadcheck timeout. (Loadcheck timeout is implemented as an after
-%% in the receive).
-overload_dist_5(suite) -> [];
-overload_dist_5(doc) ->
- [""];
-overload_dist_5(Config) when is_list(Config) ->
- ?l {ok,_Pid1}=inviso:start(), % Start a control component.
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l lists:foreach(fun(N)->true=rpc:call(N,ets,insert,[inviso_sideeffect_tab,{ovl6,0}]) end,
- Nodes), % Initiate the counter.
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,
- a_ref,
- [{overload,{{?MODULE,overload6},1000}}]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- %% Overload check shall not start until we start tracing.
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=lists:map(fun(N)->{N,[{trace,
- {file,filename:join([PrivDir,
- "tf_ovl5."++atom_to_list(N)
- ])}}]}
- end,
- Nodes),
- ?l {ok,NodeResults2}=inviso:init_tracing(TracerDataList),
- ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults2),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,ovl6],[{ovl6,2}],25),
- %% Now we know that exactly 2 checks have been made. Try to Distract the runtime :-)
- ?l inviso_rt:state(whereis(inviso_rt)), % Make it have to receive a message.
- timer:sleep(500),
- ?l [{_,2}]=ets:lookup(inviso_sideeffect_tab,ovl6), % Should still be 2.
- timer:sleep(600),
- ?l [{_,3}]=ets:lookup(inviso_sideeffect_tab,ovl6), % We expect yet one check.
- timer:sleep(1100),
- ?l [{_,4}]=ets:lookup(inviso_sideeffect_tab,ovl6),
-
- stop_tracing(Nodes),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-
-%% TEST CASE: Test of the subscription mechanism.
-subscribe_dist_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- Pid=spawn(?MODULE,inviso_msg_collector,[]),
- CtrlPid=whereis(inviso_c),
-
- ?l {ok,_Pid}=inviso:start(), % Start a control component.
- ?l ok=inviso:subscribe(Pid),
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,[]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l {ok,NodeResults2}=inviso:get_status(Nodes),
- ?l true=check_noderesults(Nodes,{ok,{new,running}},NodeResults2),
- check_msg_collector(Nodes,
- fun({inviso_event,CP,_,{connected,N,{_Tag,{idle,running}}}})
- when CP==CtrlPid ->
- {true,N};
- (_) ->
- false
- end,
- 13),
- TracerDataList=lists:map(fun(N)->{N,{file,
- filename:join([PrivDir,
- "tf_sub1"++atom_to_list(N)])}}
- end,
- Nodes),
- ?l {ok,NodeResults3}=inviso:init_tracing(TracerDataList),
- ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults3),
- check_msg_collector(Nodes,
- fun({inviso_event,CP,_,{state_change,N,{tracing,running}}})
- when CP==CtrlPid ->
- {true,N};
- (_) ->
- false
- end,
- 13),
- ?l {ok,NodeResults4}=inviso:suspend(Nodes,test),
- ?l true=check_noderesults(Nodes,ok,NodeResults4),
- check_msg_collector(Nodes,
- fun({inviso_event,CP,_,{state_change,N,{tracing,{suspended,test}}}})
- when CP==CtrlPid ->
- {true,N};
- (_) ->
- false
- end,
- 13),
- ?l [RNode|_]=RemoteNodes,
- ?l RInvisoPid=rpc:call(RNode,erlang,whereis,[inviso_rt]),
- ?l rpc:call(RNode,erlang,exit,[RInvisoPid,kill]),
- check_msg_collector([RNode],
- fun({inviso_event,CP,_,{disconnected,N,_Info}})
- when CP==CtrlPid ->
- {true,N};
- (_) ->
- false
- end,
- 11),
-
- ?l {ok,_NodeResults5}=inviso:stop_tracing(Nodes),
- ?l {ok,_NodeResults6}=inviso:stop_nodes(Nodes),
- ?l shutdown=inviso:stop(),
- ok.
-%% -----------------------------------------------------------------------------
-
-
-%% TEST CASE: fetch_log test of single straight trace_log file in distributed
-%% environment.
-fetch_log_dist_trace_1(suite) -> [];
-fetch_log_dist_trace_1(doc) ->
- ["fetch_log test of single straight trace_log file in distributed"
- "environment."];
-fetch_log_dist_trace_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=lists:map(fun(N)->{N,[{trace,{file,filename:join([PrivDir,
- "testfile1."++
- atom_to_list(N)
- ])}}]} end,
- Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
-
- %% Put some output in the logs.
- ?l inviso:tp(Nodes,math,module_info,0,[]),
- ?l inviso:tf(Nodes,all,[call]),
- ?l lists:foreach(fun(N)->rpc:call(N,math,module_info,[]) end,Nodes),
-
- stop_tracing(Nodes),
- {H,M,S}=time(),
- FetchToDir=filename:join([PrivDir,
- "fetch_log_test1_"++integer_to_list(H)++"_"++
- integer_to_list(M)++"_"++integer_to_list(S)]),
- ?l ok=file:make_dir(FetchToDir),
- ?l {ok,NodeResults}=inviso:fetch_log(RemoteNodes,FetchToDir,"p1"),
- io:format("~p~n",[NodeResults]),
- ?l true=check_noderesults(RemoteNodes,
- fun({N,{complete,[{trace_log,[{ok,File}]},{ti_log,[]}]}}) ->
- ?l File="p1testfile1."++atom_to_list(N),
- true;
- (_)->
- false
- end,
- NodeResults),
- ?l ON=filename:join(PrivDir,"testfile1."),
- ?l FN=filename:join(FetchToDir,"p1testfile1."),
- ?l lists:foreach(fun(N)->
- {ok,#file_info{size=Size}}=
- file:read_file_info(ON++atom_to_list(N)),
- {ok,#file_info{size=Size}}=
- file:read_file_info(FN++atom_to_list(N))
- end,
- RemoteNodes),
- %% Now we wish to see that we get an incomplete if we try to fetch to a
- %% directory that does not exist.
- ?l FetchToErrorDir=filename:join([PrivDir,nonexistingingdir]),
- ?l {ok,NodeResults2}=inviso:fetch_log(RemoteNodes,FetchToErrorDir,"p1"),
- ?l io:format("NodeResults2:~w~n",[NodeResults2]),
- ?l true=check_noderesults(RemoteNodes,
- fun({_,{incomplete,_}}) ->
- true;
- (_)->
- false
- end,
- NodeResults2),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-fetch_log_dist_trace_2(suite) -> [];
-fetch_log_dist_trace_2(doc) ->
- [""];
-fetch_log_dist_trace_2(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
-
- {H,M,S}=time(),
- ?l Name="wrap"++integer_to_list(H)++"_"++integer_to_list(M)++"_"++integer_to_list(S),
- ?l BaseName=filename:join(PrivDir,Name),
- Fun=fun(N)->{N,[{trace,{file,{BaseName++atom_to_list(N),wrap,".log",512,2}}},
- {ti,{file,BaseName++"_ti_"++atom_to_list(N)++".ti"}}]}
- end,
- ?l TracerDataList=lists:map(Fun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- fill_and_reach_two_wrapfiles(PrivDir,"^"++Name,Nodes),
-
- stop_tracing(Nodes),
- FetchToDir=filename:join([PrivDir,
- "fetch_log_test2_"++integer_to_list(H)++"_"++
- integer_to_list(M)++"_"++integer_to_list(S)]),
- ?l ok=file:make_dir(FetchToDir),
- ?l {ok,NodeResults}=inviso:fetch_log(RemoteNodes,FetchToDir,"p1"),
- io:format("~p~n",[NodeResults]),
- CheckFun=fun({N,{complete,[{trace_log,FileResults1},{ti_log,[{ok,TiFile}]}]}}) ->
- Fun2=fun({ok,File}) ->
- match=
- re:run(File,
- "^"++"p1"++Name++atom_to_list(N),
- [{capture,none}]),
- true;
- (_) ->
- false
- end,
- ?l true=lists:all(Fun2,FileResults1),
- ?l TiFile="p1"++Name++"_ti_"++atom_to_list(N)++".ti",
- true;
- (_)->
- false
- end,
- ?l true=check_noderesults(RemoteNodes,CheckFun,NodeResults),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-fetch_log_dist_trace_3(suite) -> [];
-fetch_log_dist_trace_3(doc) ->
- [""];
-fetch_log_dist_trace_3(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
-
- {H,M,S}=time(),
- ?l Name="wrap2_"++integer_to_list(H)++"_"++integer_to_list(M)++"_"++integer_to_list(S),
- ?l BaseName=filename:join(PrivDir,Name),
- Fun=fun(N)->{N,[{trace,{file,{BaseName++atom_to_list(N),wrap,".log",512,2}}},
- {ti,{file,BaseName++"_ti_"++atom_to_list(N)++".ti"}}]}
- end,
- ?l TracerDataList=lists:map(Fun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- fill_and_reach_two_wrapfiles(PrivDir,"^"++Name,Nodes),
-
- stop_tracing(Nodes),
- FetchToDir=filename:join([PrivDir,
- "fetch_log_test3_"++integer_to_list(H)++"_"++
- integer_to_list(M)++"_"++integer_to_list(S)]),
- ?l ok=file:make_dir(FetchToDir),
- ?l {ok,NodeResults1}=inviso:list_logs(Nodes),
- CheckFun=fun({N,{ok,[{trace_log,PrivDir2,[F1,F2]},{ti_log,PrivDir2,[F3]}]}})->
- PrivDir2=PrivDir,
- RegExp="^"++Name++atom_to_list(N)++"[0-9]+"++"\.log",
- match=re:run(F1,RegExp,[{capture,none}]),
- match=re:run(F2,RegExp,[{capture,none}]),
- F3=Name++"_ti_"++atom_to_list(N)++".ti",
- true;
- (_) ->
- false
- end,
- ?l true=check_noderesults(Nodes,CheckFun,NodeResults1),
- ?l NodeFileSpecList=lists:map(fun({N,{ok,L}})->{N,L} end,
- lists:keydelete(node(),1,NodeResults1)),
- ?l {ok,NodeResults2}=inviso:fetch_log(NodeFileSpecList,FetchToDir,"p1"),
-io:format("~p~n",[NodeResults2]),
- CheckFun2=fun({N,{complete,[{trace_log,FileResults1},{ti_log,[{ok,TiFile}]}]}}) ->
- Fun2=fun({ok,File}) ->
- match=
- re:run(File,
- "^"++"p1"++Name++atom_to_list(N),
- [{capture,none}]),
- true;
- (_) ->
- false
- end,
- ?l true=lists:all(Fun2,FileResults1),
- ?l TiFile="p1"++Name++"_ti_"++atom_to_list(N)++".ti",
- true;
- (_)->
- false
- end,
- ?l true=check_noderesults(RemoteNodes,CheckFun2,NodeResults2),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-fetch_log_dist_error_1(suite) -> [];
-fetch_log_dist_error_1(doc) ->
- [""];
-fetch_log_dist_error_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l {ok,_Pid}=inviso:start(), % Start a control component.
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l {ok,NodeResults2}=inviso:fetch_log(RemoteNodes,"foo","bar"),
-io:format("~p~n",[NodeResults2]),
- ?l true=check_noderesults(RemoteNodes,
- fun({_N,{error,no_tracerdata}})->true;
- (_)->false
- end,
- NodeResults2),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-fetch_log_dist_error_2(suite) -> [];
-fetch_log_dist_error_2(doc) ->
- [""];
-fetch_log_dist_error_2(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- ?l {ok,_Pid}=inviso:start(), % Start a control component.
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l NodeLogList=lists:map(fun(N)->{N,[{trace_log,
- PrivDir,
- ["f1,fil","f2.fil"]},
- {ti_log,
- PrivDir,
- ["f.ti"]}]}
- end,
- RemoteNodes),
- ?l {ok,NodeResults2}=inviso:fetch_log(NodeLogList,"foo","bar"),
- io:format("~p~n",[NodeResults2]),
- ?l true=check_noderesults(RemoteNodes,
- fun({_N,{incomplete,_}}) ->
- true;
- (_) ->
- false
- end,
- NodeResults2),
- ?l NodeTracerData=lists:map(fun(N)->{N,
- [{trace,{file,filename:join(PrivDir,"foo")}},
- {ti,{file,filename:join(PrivDir,"bar.ti")}}]}
- end,
- RemoteNodes),
- {ok,NodeResults3}=inviso:fetch_log(NodeTracerData,"foo","bar"),
- io:format("~p~n",[NodeResults3]),
-%% This should work this way. Now it says complete [], which is not entirely
-%% incorrect. But to follow the sematics of when fetching named files should
-%% say incomplete.
-%% Must do some rework to make that work. No real danger leaving it this way
-%% for now.
-% ?l true=check_noderesults(RemoteNodes,
-% fun({_N,{incomplete,_}}) ->
-% true;
-% (_) ->
-% false
-% end,
-% NodeResults3),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: This case tests that the log file merger merges files in the
-%% correct order, based on the timestamps.
-lfm_trace_dist_1(suite) -> [];
-lfm_trace_dist_1(doc) ->
- [""];
-lfm_trace_dist_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- [RNode1,RNode2|_]=RemoteNodes,
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=
- lists:map(fun(N)->{N,{file,filename:join([PrivDir,"lfm1_"++atom_to_list(N)])}} end,
- Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
- activate_local_tracing(Nodes),
- activate_traceflags(Nodes),
-
- {inviso_test_proc,RNode2} ! {apply,code,which,[lists]},
- timer:sleep(300),
- {inviso_test_proc,RNode1} ! {apply,code,which,[lists]},
- timer:sleep(300),
- {inviso_test_proc,RNode1} ! {apply,code,which,[lists]},
- timer:sleep(300),
- inviso_test_proc ! {apply,code,which,[lists]},
- timer:sleep(300),
- {inviso_test_proc,RNode2} ! {apply,code,which,[lists]},
- timer:sleep(300),
- inviso_test_proc ! {apply,code,which,[lists]},
-
- deactivate_traceflags(Nodes),
- deactivate_local_tracing(Nodes),
- stop_tracing(Nodes),
- stop(Nodes),
-
- DestFile=filename:join(PrivDir,"lfm1_out.txt"),
- ?l {ok,6}=
- inviso_lfm:merge([{node(),
- [{trace_log,
- [filename:join(PrivDir,"lfm1_"++atom_to_list(node()))]}]},
- {RNode1,
- [{trace_log,
- [filename:join(PrivDir,"lfm1_"++atom_to_list(RNode1))]}]},
- {RNode2,
- [{trace_log,
- [filename:join(PrivDir,"lfm1_"++atom_to_list(RNode2))]}]}],
- DestFile),
- ?l {ok,FD}=file:open(DestFile,[read]),
- ?l S1=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode2),S1),
- ?l S2=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode1),S2),
- ?l S3=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode1),S3),
- ?l S4=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(node()),S4),
- ?l S5=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode2),S5),
- ?l S6=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(node()),S6),
- ?l file:close(FD),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Testing to the full extent that pid-mappings work with both
-%% local and global registration. Also checks that pidmappings can be removed
-%% and that consequently the mappings in the resulting merged file stops.
-lfm_trace_ti_dist_2(suite) -> [];
-lfm_trace_ti_dist_2(doc) ->
- [""];
-lfm_trace_ti_dist_2(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- [RNode1,RNode2|_]=RemoteNodes,
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=
- lists:map(fun(N)->{N,[{trace,{file,filename:join(PrivDir,"lfm2_"++atom_to_list(N))}},
- {ti,{file,filename:join(PrivDir,"lfm2_ti_"++atom_to_list(N))}}]}
- end,
- Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- activate_local_tracing(Nodes),
- activate_meta_tracing(Nodes),
- activate_traceflags(Nodes),
-
- {inviso_test_proc,RNode2} ! {apply,code,which,[lists]},
- timer:sleep(300),
- {inviso_test_proc,RNode1} ! {apply,code,which,[lists]},
- timer:sleep(300),
- {inviso_test_proc,RNode1} ! {apply,code,which,[lists]},
- timer:sleep(300),
- inviso_test_proc ! {apply,code,which,[lists]},
- timer:sleep(300),
-
- P2=spawn(RNode2,?MODULE,test_proc_loop,[]),
- P1=spawn(RNode1,?MODULE,test_proc_loop,[]),
- P0=spawn_link(?MODULE,test_proc_loop,[]),
- ThisNode=node(),
- ?l {ok,[{ThisNode,{ok,[1]}}]}=inviso:tf([node()],P0,[call,timestamp]),
- ?l {ok,[{RNode1,{ok,[1]}}]}=inviso:tf([RNode1],P1,[call,timestamp]),
- ?l {ok,[{RNode2,{ok,[1]}}]}=inviso:tf([RNode2],P2,[call,timestamp]),
- P2 ! {apply,code,which,[lists]},
- timer:sleep(300),
- P1 ! {apply,code,which,[lists]},
- timer:sleep(300),
- P0 ! {apply,code,which,[lists]},
- timer:sleep(300),
-
- P3=spawn(RNode2,?MODULE,test_proc_loop,[]),
- ?l yes=global:register_name(inviso_test_proc_globalname,P3),
- ?l {ok,[{RNode2,{ok,[1]}}]}=inviso:tf([RNode2],P3,[call,timestamp]),
- timer:sleep(300),
- P3 ! {apply,code,which,[lists]},
- timer:sleep(300),
-
- P4=rpc:call(RNode1,erlang,whereis,[inviso_test_proc]),
- ?l true=rpc:call(RNode1,erlang,unregister,[inviso_test_proc]),
- timer:sleep(300),
- P4 ! {apply,code,which,[lists]},
- timer:sleep(300),
-
- ?l true=rpc:call(RNode1,erlang,register,[inviso_test_proc,P4]),
-
- ?l global:unregister_name(inviso_test_proc_globalname),
- timer:sleep(300),
- ?l P3 ! {apply,code,which,[lists]},
- timer:sleep(300),
-
- deactivate_traceflags(Nodes),
- deactivate_local_tracing(Nodes),
- stop_tracing(Nodes),
- stop(Nodes),
-
- DestFile=filename:join(PrivDir,"lfm2_out.txt"),
- ?l {ok,10}=
- inviso_lfm:merge([
- {node(),
- [{trace_log,
- [filename:join(PrivDir,"lfm2_"++atom_to_list(node()))]},
- {ti_log,
- [filename:join(PrivDir,"lfm2_ti_"++atom_to_list(node()))]}]},
- {RNode1,
- [{trace_log,
- [filename:join(PrivDir,"lfm2_"++atom_to_list(RNode1))]},
- {ti_log,
- [filename:join(PrivDir,"lfm2_ti_"++atom_to_list(RNode1))]}]},
- {RNode2,
- [{trace_log,
- [filename:join(PrivDir,"lfm2_"++atom_to_list(RNode2))]},
- {ti_log,
- [filename:join(PrivDir,"lfm2_ti_"++atom_to_list(RNode2))]}]}
- ],
- DestFile),
- ?l {ok,FD}=file:open(DestFile,[read]),
- ?l S1=io:get_line(FD,""),
-io:format("S1 is:~p~n",[S1]),
- ?l true=lists:prefix(atom_to_list(RNode2)++" [inviso_test_proc",S1),
- ?l S2=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode1)++" [inviso_test_proc",S2),
- ?l S3=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode1)++" [inviso_test_proc",S3),
- ?l S4=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(node())++" [inviso_test_proc",S4),
- ?l S5=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode2)++" []",S5),
- ?l S6=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode1)++" []",S6),
- ?l S7=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(node())++" []",S7),
- ?l S8=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode2)++" [{global,inviso_test_proc_globalname}]",S8),
- ?l S9=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode1)++" []",S9),
- ?l S10=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode2)++" []",S10),
- ?l file:close(FD),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: This tests that the wrapset sorter works.
-handle_logfile_sort_wrapset(suite) -> [];
-handle_logfile_sort_wrapset(doc) ->
- [""];
-handle_logfile_sort_wrapset(Config) when is_list(Config) ->
- File0="prefix10.fil",
- File1="prefix11.fil",
- File2="prefix12.fil",
- File3="prefix13.fil",
- ?l [File0,File1,File2,File3]=
- inviso_lfm_tpfreader:handle_logfile_sort_wrapset([File2,File1,File0,File3]),
- File5="prefix15.fil",
- ?l [File5,File0,File1,File2,File3]=
- inviso_lfm_tpfreader:handle_logfile_sort_wrapset([File2,File5,File1,File0,File3]),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: This case tests that the regexp mechanism in the inviso_rt_lib can
-%% find modules using regexps and that its only_loaded mechanism works.
-%% This test case can not be run when using cover because cover will make the
-%% modules no longer loaded from the path containing "runtime_tools".
-expand_regexp_dist_1(suite) -> [];
-expand_regexp_dist_1(doc) ->
- [""];
-expand_regexp_dist_1(Config) when is_list(Config) ->
- case ?t:is_cover() of
- true ->
- {skip,"Cover is running"};
- false ->
- expand_regexp_dist_1_nocover(Config)
- end.
-
-expand_regexp_dist_1_nocover(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- [RNode1|_]=RemoteNodes,
- ?l NodeResults1=inviso_rt_lib:expand_regexp(Nodes,"^inviso_rt.*",[]),
- ?l L1=length(Nodes),
- ?l L1=length(NodeResults1),
- ?l true=lists:all(fun({_,Mods})->
- ?l 3=length(Mods),
- ?l true=lists:member(inviso_rt,Mods),
- ?l true=lists:member(inviso_rt_lib,Mods),
- ?l true=lists:member(inviso_rt_meta,Mods),
- true;
- (_) ->
- false
- end,
- NodeResults1),
- %% Check the dir-option. In the following inviso_tool_lib shall not be found.
- ?l NodeResults2=inviso_rt_lib:expand_regexp(Nodes,"runtime_tools","invi.*lib.*",[]),
-?l io:format("NodeResults2:~w~n",[NodeResults2]),
- ?l L1=length(NodeResults2), % Same number of nodes replying.
- ?l true=lists:all(fun({_,Mods})->
- 2=length(Mods),
- true=lists:member(inviso_as_lib,Mods),
- true=lists:member(inviso_rt_lib,Mods),
- true;
- (_) ->
- false
- end,
- NodeResults2),
- ?l [{RNode1,[]}]=
- inviso_rt_lib:expand_regexp([RNode1],"^inviso_testmodule1.*",[only_loaded]),
- ?l [{RNode1,[inviso_testmodule1_foo]}]=
- inviso_rt_lib:expand_regexp([RNode1],"^inviso_testmodule1.*",[]),
- ok.
-%% -----------------------------------------------------------------------------
-
-
-only_loaded_dist_1(suite) -> [];
-only_loaded_dist_1(doc) ->
- [""];
-only_loaded_dist_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- [RNode1|_]=RemoteNodes,
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=
- lists:map(fun(N)->{N,[{trace,{file,filename:join(PrivDir,"ol_1_"++atom_to_list(N))}}]}
- end,
- Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
- ?l false=rpc:call(RNode1,erlang,module_loaded,[inviso_testmodule1_foo]),
- ?l {ok,[{RNode1,{ok,[0]}}]}=
- inviso:tpl([RNode1],inviso_testmodule1_foo,'_','_',[],[only_loaded]),
- ?l false=rpc:call(RNode1,erlang,module_loaded,[inviso_testmodule1_foo]),
- ?l {ok,[{RNode1,{ok,[3]}}]}=
- inviso:tpl([RNode1],inviso_testmodule1_foo,'_','_',[],[]),
- stop_tracing(Nodes),
- stop(Nodes),
- ok.
-
-
-%% ==============================================================================
-%% Common functions setting up inviso.
-%% ==============================================================================
-
-%% Starts controlcomponent and adds runtime components on the nodes specified.
-%% Also initiates tracing on the nodes.
-start_and_init_tracing1(Nodes,Options,TracerData,Reply) when is_list(Nodes) ->
- ?l {ok,_Pid}=inviso:start(), % Start a control component.
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,Options),
- io:format("~p~n",[NodeResults1]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l {ok,NodeResults2}=inviso:get_status(Nodes),
- ?l true=check_noderesults(Nodes,{ok,{new,running}},NodeResults2),
- ?l {ok,NodeResults3}=inviso:init_tracing(Nodes,TracerData),
- ?l true=check_noderesults(Nodes,Reply,NodeResults3),
- ok.
-start_and_init_tracing2(Nodes,Options,TracerDataList,Reply) ->
- ?l {ok,_Pid}=inviso:start(), % Start a control component.
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,Options),
- io:format("~p~n",[NodeResults1]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l {ok,NodeResults2}=inviso:get_status(Nodes),
- ?l true=check_noderesults(Nodes,{ok,{new,running}},NodeResults2),
- ?l {ok,NodeResults4}=inviso:get_tracerdata(Nodes),
- ?l true=check_noderesults(Nodes,{ok,no_tracerdata},NodeResults4),
- ?l {ok,NodeResults3}=inviso:init_tracing(TracerDataList),
- io:format("Tracerdatalist:~p~n",[TracerDataList]),
- ?l true=check_noderesults(Nodes,Reply,NodeResults3),
-
- ?l Fun1=fun({N,{ok,TD}}) when is_list(TD)->
- ?l {value,{trace,Trace}}=lists:keysearch(trace,1,TD),
- ?l {value,{N,TD2}}=lists:keysearch(N,1,TracerDataList),
- ?l true=lists:member({trace,Trace},TD2),
- %% Check that the trace file really exists.
- ?l case Trace of % Trace={file,FilePortParameters}
- {file,FileName1} when is_list(FileName1) ->
- ?l {ok,_}=rpc:call(N,file,read_file_info,[FileName1]);
- _ -> % This should be extended with more cases.
- true
- end,
- ?l case lists:keysearch(ti,1,TD2) of
- {value,{_,Ti}} -> % Ok, we have ti too.
- ?l {value,{_,Ti}}=lists:keysearch(ti,1,TD),
- ?l FileName2=element(2,Ti),
- ?l {ok,_}=rpc:call(N,file,read_file_info,[FileName2]),
- true;
- false -> % No ti, we are done now.
- true
- end;
- ({N,{ok,{file,FileName}}}) ->
- ?l {value,{N,{file,FileName}}}=lists:keysearch(N,1,TracerDataList),
- ?l {ok,_}=rpc:call(N,file,read_file_info,[FileName]),
- true;
- ({N,{ok,LogTD}}) -> % The case using a fun.
- ?l {value,{N,LogTD}}=lists:keysearch(N,1,TracerDataList),
- true
- end,
- ?l {ok,NodeResults5}=inviso:get_tracerdata(Nodes),
- ?l true=check_noderesults(Nodes,Fun1,NodeResults5),
- ok.
-%% ------------------------------------------------------------------------------
-
-%% Stops tracing on Nodes.
-stop_tracing(Nodes) when is_list(Nodes) ->
- ?l {ok,NodeResults1}=inviso:stop_tracing(Nodes),
- ?l true=check_noderesults(Nodes,{ok,idle},NodeResults1),
- ?l {ok,NodeResults2}=inviso:get_status(Nodes),
- ?l true=check_noderesults(Nodes,{ok,{idle,running}},NodeResults2),
- %% The implementation says that the meta tracer shall be stopped when
- %% tracing is stopped. Check that.
- ?l lists:foreach(fun(N)->
- ok=poll(erlang,whereis,[inviso_rt_meta],undefined,20)
- end,
- Nodes).
-%% ------------------------------------------------------------------------------
-
-%% Stops the runtime components on Nodes and stops the control component at this
-%% Erlang node.
-stop(Nodes) when is_list(Nodes) ->
- ?l true=check_on_nodes(Nodes,erlang,whereis,[inviso_rt],fun(P) when is_pid(P)->true end),
- ?l {ok,NodeResults}=inviso:stop_nodes(Nodes),
- ?l true=check_noderesults(Nodes,ok,NodeResults),
- ?l true=check_on_nodes(Nodes,erlang,whereis,[inviso_rt],fun(undefined)->true end),
- ?l true=is_pid(whereis(inviso_c)),
- ?l shutdown=inviso:stop(),
- ?l ok=poll(erlang,whereis,[inviso_c],undefined,20).
-%% ------------------------------------------------------------------------------
-
-%% Help function activating local tracing.
-activate_local_tracing(Nodes) when is_list(Nodes) ->
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{code,which,1},traced],
- {traced,false}),
- ?l {ok,NodeResults}=inviso:tpl(Nodes,code,which,1,[]),
- ?l true=check_noderesults(Nodes,fun({_,{ok,[1]}})->true end,NodeResults),
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{code,which,1},traced],
- {traced,local}).
-%% ------------------------------------------------------------------------------
-
-%% Help function activating global tracing.
-activate_global_tracing(Nodes) when is_list(Nodes) ->
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{code,get_path,0},traced],
- {traced,false}),
- ?l {ok,NodeResults}=inviso:tp(Nodes,code,get_path,0,[]),
- ?l true=check_noderesults(Nodes,fun({_,{ok,[1]}})->true end,NodeResults),
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{code,get_path,0},traced],
- {traced,global}).
-%% ------------------------------------------------------------------------------
-
-
-%% Help function activating local tracing and using a regexp to point out modules.
-%% Returns the structure of modules and functions that were activated. Must be used
-%% when deactivating.
-activate_global_tracing_regexp(Nodes) when is_list(Nodes) ->
- %% First find out which modules will be effected.
- ?l Mods1=inviso_rt_lib:expand_regexp("application.*",[]),
- ?l true=(length(Mods1)>1), % Should find more than one module!
- ?l Funcs1=lists:foldl(fun(M,Acc)->[{M,M:module_info(exports)}|Acc] end,[],Mods1),
- %% Check that these functions are not traced.
- io:format("Modules:~w~n",[Mods1]),
- ?l {ok,NodeResults}=inviso:tp(Nodes,"application.*",'_','_',[],[]),
- io:format("Here 2~w~n",[NodeResults]),
- ?l N=lists:foldl(fun({_,L1},A1)->lists:foldl(fun(_,A2)->A2+1 end,A1,L1) end,0,Funcs1),
- ?l true=check_noderesults(Nodes,fun({_,{ok,L}})-> N==lists:sum(L) end,NodeResults),
- io:format("Here 3~n",[]),
- %% Check again!
- ?l lists:foreach(fun({M,Funcs})->
- lists:foreach(fun({F,Arity})->
- true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{M,F,Arity},traced],
- {traced,global})
- end,
- Funcs)
- end,
- Funcs1),
- Funcs1.
-%% ------------------------------------------------------------------------------
-
-%% Help function as above but uses the dir feature as well.
-activate_global_tracing_regexp_dir(Nodes) when is_list(Nodes) ->
- %% First find out which modules will be effected.
- ?l Mods1=inviso_rt_lib:expand_regexp(".*kernel.*","application.*",[]),
- ?l true=(length(Mods1)>1), % Should find more than one module!
- ?l Funcs1=lists:foldl(fun(M,Acc)->[{M,M:module_info(exports)}|Acc] end,[],Mods1),
- %% Check that these functions are not traced.
- io:format("Modules:~w~n",[Mods1]),
- ?l {ok,NodeResults}=inviso:tp(Nodes,{".*kernel.*","application.*"},'_','_',[],[]),
- io:format("Here 2~w~n",[NodeResults]),
- ?l N=lists:foldl(fun({_,L1},A1)->lists:foldl(fun(_,A2)->A2+1 end,A1,L1) end,0,Funcs1),
- ?l true=check_noderesults(Nodes,fun({_,{ok,L}})-> N==lists:sum(L) end,NodeResults),
- io:format("Here 3~n",[]),
- %% Check again!
- ?l lists:foreach(fun({M,Funcs})->
- lists:foreach(fun({F,Arity})->
- true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{M,F,Arity},traced],
- {traced,global})
- end,
- Funcs)
- end,
- Funcs1),
- Funcs1.
-%% ------------------------------------------------------------------------------
-
-deactivate_local_tracing(Nodes) when is_list(Nodes) ->
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{code,which,1},traced],
- {traced,local}),
- ?l {ok,NodeResults}=inviso:ctpl(Nodes,code,'_','_'),
- ?l true=check_noderesults(Nodes,fun({_,{ok,[N]}})when is_integer(N)->true end,NodeResults),
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{code,which,1},traced],
- {traced,false}).
-%% ------------------------------------------------------------------------------
-
-deactivate_global_tracing(Nodes) when is_list(Nodes) ->
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{code,get_path,0},traced],
- {traced,global}),
- ?l {ok,NodeResults}=inviso:ctp(Nodes,code,'_','_'),
- ?l true=check_noderesults(Nodes,fun({_,{ok,[N]}})when is_integer(N)->true end,NodeResults),
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{code,get_path,0},traced],
- {traced,false}).
-%% ------------------------------------------------------------------------------
-
-
-%% Function deactivating the functions activated by activate_global_tracing_regexp/1.
-deactivate_global_tracing_regexp(Nodes,Funcs1) ->
- ?l lists:foreach(fun({M,Funcs})->
- lists:foreach(fun({F,Arity})->
- true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{M,F,Arity},traced],
- {traced,global})
- end,
- Funcs)
- end,
- Funcs1),
- ?l {ok,NodeResults}=inviso:ctp(Nodes,"application.*",'_','_'),
- ?l N=lists:foldl(fun({_,L1},A1)->lists:foldl(fun(_,A2)->A2+1 end,A1,L1) end,0,Funcs1),
- io:format("Noderesult from deactivate;~w~n",[NodeResults]),
- ?l true=check_noderesults(Nodes,fun({_,{ok,L}})-> N==lists:sum(L) end,NodeResults),
- ?l lists:foreach(fun({M,Funcs})->
- lists:foreach(fun({F,Arity})->
- true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{M,F,Arity},traced],
- {traced,false})
- end,
- Funcs)
- end,
- Funcs1).
-%% ------------------------------------------------------------------------------
-
-%% Function deactivating the functions activated by activate_global_tracing_regexp_dir/1.
-deactivate_global_tracing_regexp_dir(Nodes,Funcs1) ->
- ?l lists:foreach(fun({M,Funcs})->
- lists:foreach(fun({F,Arity})->
- true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{M,F,Arity},traced],
- {traced,global})
- end,
- Funcs)
- end,
- Funcs1),
- ?l {ok,NodeResults}=inviso:ctp(Nodes,{".*kernel.*","application.*"},'_','_'),
- ?l N=lists:foldl(fun({_,L1},A1)->lists:foldl(fun(_,A2)->A2+1 end,A1,L1) end,0,Funcs1),
- io:format("Noderesult from deactivate;~w~n",[NodeResults]),
- ?l true=check_noderesults(Nodes,fun({_,{ok,L}})-> N==lists:sum(L) end,NodeResults),
- ?l lists:foreach(fun({M,Funcs})->
- lists:foreach(fun({F,Arity})->
- true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{M,F,Arity},traced],
- {traced,false})
- end,
- Funcs)
- end,
- Funcs1).
-%% ------------------------------------------------------------------------------
-
-%% Help function which starts the inviso_test_proc on all nodes and then sets
-%% the call flag on that process.
-activate_traceflags(Nodes) ->
- ?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes),
- ?l lists:foreach(fun(N)->
- P=rpc:call(N,erlang,whereis,[inviso_test_proc]),
- {flags,[]}=rpc:call(N,erlang,trace_info,[P,flags])
- end,
- Nodes),
- ?l {ok,NodeResults}=inviso:tf(Nodes,inviso_test_proc,[call,timestamp]),
- ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults),
- ?l lists:foreach(fun(N)->
- P=rpc:call(N,erlang,whereis,[inviso_test_proc]),
- {flags,Flags}=rpc:call(N,erlang,trace_info,[P,flags]),
- true=lists:member(call,Flags),
- true=lists:member(timestamp,Flags)
- end,
- Nodes),
- %% Now try a globally registered process.
- ?l [ANode|_]=Nodes,
- ?l GPid=spawn(ANode,?MODULE,global_test_proc_init,[]),
- ?l ok=poll(global,whereis_name,[global_inviso_test_proc],
- fun(P) when is_pid(P)->true;(_)->false end,
- 10),
- ?l {ok,NodeResults2}=
- inviso:tf(Nodes,{global,global_inviso_test_proc},[call,timestamp]),
- ?l true=check_noderesults(Nodes,
- fun({N,{ok,[1]}}) when N==ANode->true;
- ({_,{ok,[0]}})->true;
- (_)->false
- end,
- NodeResults2),
- ?l {flags,Flags2}=rpc:call(ANode,erlang,trace_info,[GPid,flags]),
- ?l 2=length(Flags2),
- ?l true=lists:member(call,Flags2),
- ?l true=lists:member(timestamp,Flags2),
- true.
-%% ------------------------------------------------------------------------------
-
-deactivate_traceflags(Nodes) ->
- ?l lists:foreach(fun(N)->
- P=rpc:call(N,erlang,whereis,[inviso_test_proc]),
- {flags,Flags}=rpc:call(N,erlang,trace_info,[P,flags]),
- true=lists:member(call,Flags),
- true=lists:member(timestamp,Flags)
- end,
- Nodes),
- ?l {ok,NodeResults}=inviso:ctf(Nodes,inviso_test_proc,[call,timestamp]),
- ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults),
- ?l lists:foreach(fun(N)->
- P=rpc:call(N,erlang,whereis,[inviso_test_proc]),
- {flags,[]}=rpc:call(N,erlang,trace_info,[P,flags])
- end,
- Nodes),
- ?l GPid=global:whereis_name(global_inviso_test_proc),
- ?l ANode=node(GPid),
- ?l {flags,Flags2}=rpc:call(ANode,erlang,trace_info,[GPid,flags]),
- ?l 2=length(Flags2),
- ?l {ok,NodeResults2}=inviso:ctf(Nodes,{global,global_inviso_test_proc},[call,timestamp]),
- ?l true=check_noderesults(Nodes,
- fun({N,{ok,[1]}}) when N==ANode->true;
- ({_,{ok,[0]}})->true;
- (_)->false
- end,
- NodeResults2).
-%% ------------------------------------------------------------------------------
-
-
-activate_meta_tracing(Nodes) ->
- ?l {ok,NodeResults1}=inviso:tpm_localnames(),
- ?l true=check_noderesults(Nodes,{{ok,1},{ok,1}},NodeResults1),
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,erlang,trace_info,[{erlang,register,2},meta])
- end,
- Nodes),
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,erlang,trace_info,[{erlang,unregister,1},meta])
- end,
- Nodes),
- ?l {ok,NodeResults2}=inviso:tpm_globalnames(),
- ?l true=check_noderesults(Nodes,{{ok,1},{ok,1}},NodeResults2),
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,
- erlang,
- trace_info,
- [{global,handle_call,3},meta])
- end,
- Nodes),
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,
- erlang,
- trace_info,
- [{global,delete_global_name,2},meta])
- end,
- Nodes),
-
- ?l lists:foreach(fun(N)->true=rpc:call(N,
- ets,
- insert,
- [inviso_sideeffect_tab,{tpm_init_func1,0}]),
- true=rpc:call(N,
- ets,
- insert,
- [inviso_sideeffect_tab,{tpm_call_func1,0}]),
- true=rpc:call(N,
- ets,
- insert,
- [inviso_sideeffect_tab,{tpm_return_func1,0}])
- end,
- Nodes),
- ?l {ok,NodeResults3}=
- inviso:init_tpm(lists,
- module_info,
- 0,
- {?MODULE,tpm_init_func1},
- {?MODULE,tpm_call_func1},
- {?MODULE,tpm_return_func1},
- {?MODULE,tpm_remove_func1}),
- ?l true=check_noderesults(Nodes,ok,NodeResults3),
- ?l [{_,1}]=ets:lookup(inviso_sideeffect_tab,tpm_init_func1),
- ?l {ok,NodeResults3a}=
- inviso:init_tpm(lists,
- module_info,
- 0,
- {?MODULE,tpm_init_func1},
- {?MODULE,tpm_call_func1},
- {?MODULE,tpm_return_func1},
- {?MODULE,tpm_remove_func1}),
- ?l true=check_noderesults(Nodes,{error,already_initiated},NodeResults3a),
-% %% Try more forbidden things. Wildcards not allowed in meta tracing!
-% ?l {ok,NodeResults3b}=inviso:tpm(Nodes,lists,'_',0,[{'_',[],[{return_trace}]}]),
-% io:format("The noderesults3b is:~w~n",[NodeResults3b]),
-% ?l true=check_noderesults(Nodes,{error,bad_mfa},NodeResults3b),
- ?l {ok,NodeResults3c}=inviso:tpm(Nodes,lists,module_info,0,[{'_',[],[{return_trace}]}]),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults3c),
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta])
- end,
- Nodes),
- ?l lists:foreach(fun(N)->rpc:call(N,lists,module_info,[]) end,Nodes),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_call_func1],[{tpm_call_func1,1}],20),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_return_func1],[{tpm_return_func1,1}],20),
- ?l lists:foreach(fun(N)->rpc:call(N,lists,module_info,[]) end,Nodes),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_call_func1],[{tpm_call_func1,2}],20),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_return_func1],[{tpm_return_func1,2}],20),
-
- ?l {ok,NodeResults4}=
- inviso:init_tpm(math,
- module_info,
- 1,
- {?MODULE,tpm_init_func2}, % Does not exist on purpose.
- {?MODULE,tpm_call_func2}, % Does not exist on purpose.
- {?MODULE,tpm_return_func2}, % Does not exist on purpose.
- {?MODULE,tpm_remove_func2}), % Does not exist on purpose.
- ?l true=check_noderesults(Nodes,ok,NodeResults4),
- ?l {ok,NodeResults5}=
- inviso:tpm_ms(math,module_info,1,ms1,[{'_',[],[{return_trace}]}]),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults5),
- ?l lists:foreach(fun(N)->{meta_match_spec,[{'_',[],[{return_trace}]}]}=
- rpc:call(N,erlang,trace_info,[{math,module_info,1},
- meta_match_spec])
- end,
- Nodes),
-
- ?l {ok,NodeResults6}=inviso:tpm_ms(math,module_info,1,ms2,[{[exports],[],[]}]),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults6),
- ?l lists:foreach(fun(N)->{meta_match_spec,[{[exports],[],[]},{'_',[],[{return_trace}]}]}=
- rpc:call(N,erlang,trace_info,[{math,module_info,1},
- meta_match_spec])
- end,
- Nodes),
- ?l {ok,NodeResults7}=inviso:tpm_ms(math,module_info,1,ms3,[{[attributes],[],[]}]),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults7),
- ?l lists:foreach(fun(N)->{meta_match_spec,[{[attributes],[],[]},
- {[exports],[],[]},
- {'_',[],[{return_trace}]}]}=
- rpc:call(N,erlang,trace_info,[{math,module_info,1},
- meta_match_spec])
- end,
- Nodes),
- ?l {ok,NodeResults8}=inviso:ctpm_ms(math,module_info,1,ms2),
- ?l true=check_noderesults(Nodes,ok,NodeResults8),
- ?l lists:foreach(fun(N)->{meta_match_spec,[{[attributes],[],[]},
- {'_',[],[{return_trace}]}]}=
- rpc:call(N,erlang,trace_info,[{math,module_info,1},
- meta_match_spec])
- end,
- Nodes),
- ?l io:format("whereis:~w~n",[lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_rt_meta]) end,Nodes)]),
- ?l {ok,NodeResults8}=inviso:ctpm_ms(math,module_info,1,ms3),
- ?l io:format("whereis:~w~n",[lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_rt_meta]) end,Nodes)]),
- ?l {ok,NodeResults8}=inviso:ctpm_ms(math,module_info,1,ms1),
- ?l lists:foreach(fun(N)->{meta_match_spec,false}=
- rpc:call(N,erlang,trace_info,[{math,module_info,1},
- meta_match_spec])
- end,
- Nodes),
-
- %% Now try to do this with exception tracing instead.
- %% Reset the side effect tables.
- ?l lists:foreach(fun(N)->true=rpc:call(N,
- ets,
- insert,
- [inviso_sideeffect_tab,{tpm_init_func1,0}]),
- true=rpc:call(N,
- ets,
- insert,
- [inviso_sideeffect_tab,{tpm_call_func1,0}]),
- true=rpc:call(N,
- ets,
- insert,
- [inviso_sideeffect_tab,{tpm_return_func1,0}])
- end,
- Nodes),
- ?l {ok,NodeResults9}=
- inviso:init_tpm(?MODULE,
- failing_function,
- 1,
- {?MODULE,tpm_init_func1},
- {?MODULE,tpm_call_func1},
- {?MODULE,tpm_return_func1},
- {?MODULE,tpm_remove_func1}),
- ?l true=check_noderesults(Nodes,ok,NodeResults9),
- ?l [{_,1}]=ets:lookup(inviso_sideeffect_tab,tpm_init_func1),
- ?l {ok,NodeResults10}=inviso:tpm(Nodes,?MODULE,failing_function,1,[{'_',[],[{exception_trace}]}]),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults10),
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,erlang,trace_info,[{?MODULE,failing_function,1},meta])
- end,
- Nodes),
- ?l lists:foreach(fun(N)->rpc:call(N,?MODULE,failing_function,[nofailure]) end,Nodes),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_call_func1],[{tpm_call_func1,1}],20),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_return_func1],[{tpm_return_func1,1}],20),
- ?l lists:foreach(fun(N)->rpc:call(N,?MODULE,failing_function,[failure]) end,Nodes),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_call_func1],[{tpm_call_func1,2}],20),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_return_func1],[{tpm_return_func1,3}],20),
-
- ok.
-%% ------------------------------------------------------------------------------
-
-%% This function is for testing that appending the tracer to a trace action term
-%% works.
-activate_deactivate_meta_tracing_tracer(Nodes) ->
- ?l {ok,NodeResults}=
- inviso:tpm_tracer(Nodes,lists,module_info,0,[{'_',[],[{trace,[all],[call]}]}],void),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults),
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta]),
- {meta_match_spec,[{'_',[],[{trace,[all],Enable}]}]}=
- rpc:call(N,erlang,trace_info,[{lists,module_info,0},
- meta_match_spec]),
- true=list_search(Enable,fun({{tracer,P}}) when is_port(P)->true;
- (_) -> false
- end)
- end,
- Nodes),
- ?l {ok,NodeResults2}=
- inviso:ctpm(Nodes,lists,module_info,0),
- ?l true=check_noderesults(Nodes,ok,NodeResults2),
- ?l lists:foreach(fun(N)->{meta,false}=
- rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta])
- end,
- Nodes),
- ok.
-%% ------------------------------------------------------------------------------
-
-deactivate_meta_tracing(Nodes) ->
- ?l lists:foreach(fun(N)->{meta,P}=
- rpc:call(N,erlang,trace_info,[{erlang,register,2},meta]),
- true=is_pid(P)
- end,
- Nodes),
- ?l lists:foreach(fun(N)->{meta,P}=
- rpc:call(N,erlang,trace_info,[{erlang,unregister,1},meta]),
- true=is_pid(P)
- end,
- Nodes),
- ?l {ok,NodeResults1}=inviso:ctpm_localnames(),
- ?l lists:foreach(fun(N)->{meta,false}=
- rpc:call(N,erlang,trace_info,[{erlang,register,2},meta]) end,
- Nodes),
- ?l lists:foreach(fun(N)->{meta,false}=
- rpc:call(N,erlang,trace_info,[{erlang,unregister,1},meta])
- end,
- Nodes),
- ?l true=check_noderesults(Nodes,{ok,ok},NodeResults1),
-
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,
- erlang,
- trace_info,
- [{global,handle_call,3},meta])
- end,
- Nodes),
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,
- erlang,
- trace_info,
- [{global,delete_global_name,2},meta])
- end,
- Nodes),
- ?l {ok,NodeResults1b}=inviso:ctpm_globalnames(),
- ?l true=check_noderesults(Nodes,{ok,ok},NodeResults1b),
- ?l lists:foreach(fun(N)->
- {meta,false}=rpc:call(N,
- erlang,
- trace_info,
- [{global,handle_call,3},meta])
- end,
- Nodes),
- ?l lists:foreach(fun(N)->
- {meta,false}=rpc:call(N,
- erlang,
- trace_info,
- [{global,delete_global_name,2},meta])
- end,
- Nodes),
-
- ?l lists:foreach(fun(N)->{meta,P}=
- rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta]),
- true=is_pid(P)
- end,
- Nodes),
- ?l {ok,NodeResults2}=inviso:ctpm(lists,module_info,0),
- ?l true=check_noderesults(Nodes,ok,NodeResults2),
- ?l lists:foreach(fun(N)->{meta,false}=
- rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta]) end,
- Nodes),
- ?l [{_,0}]=ets:lookup(inviso_sideeffect_tab,tpm_init_func1),
- ?l {ok,NodeResults3}=inviso:ctpm(math,module_info,1),
- ?l true=check_noderesults(Nodes,ok,NodeResults3),
- ok.
-%% ------------------------------------------------------------------------------
-
-%% Functions acting as callbacks for testing the meta tracing mechanisms.
-tpm_init_func1(_M,_F,_Arity,PublLD) ->
- ets:update_counter(inviso_sideeffect_tab,tpm_init_func1,1),
- {ok,PublLD,void}.
-tpm_call_func1(_Pid,{call,_Args,_TS},PublLD) ->
- ets:update_counter(inviso_sideeffect_tab,tpm_call_func1,1),
- {ok,PublLD,void}.
-tpm_return_func1(_Pid,{return_from,_ReturnVal,_TS},PublLD) ->
- ets:update_counter(inviso_sideeffect_tab,tpm_return_func1,1),
- {ok,PublLD,void};
-tpm_return_func1(_Pid,{exception_from,_ReturnVal,_TS},PublLD) ->
- ets:update_counter(inviso_sideeffect_tab,tpm_return_func1,1),
- ets:update_counter(inviso_sideeffect_tab,tpm_return_func1,1),
- {ok,PublLD,void}.
-tpm_remove_func1(_M,_F,_Arity,PublLD) ->
- ets:update_counter(inviso_sideeffect_tab,tpm_init_func1,-1),
- {ok,PublLD}.
-%% ------------------------------------------------------------------------------
-
-
-%% Help function which traces on a function and makes function calls until there
-%% are two files in the wrap-set.
-fill_and_reach_two_wrapfiles(PrivDir,RegExp,Nodes) ->
- ?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes),
- ?l {ok,NodeResults1}=inviso:tpl(Nodes,?MODULE,test_function,0,[]),
- ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults1),
- ?l {ok,NodeResults2}=inviso:tf(Nodes,inviso_test_proc,[call]),
- ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults2),
- fill_and_reach_two_wrapfiles_2(PrivDir,RegExp,Nodes),
- ?l {ok,NodeResults3}=inviso:ctf(Nodes,inviso_test_proc,[call]),
- ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults3),
- ?l {ok,NodeResults4}=inviso:ctpl(Nodes,?MODULE,test_function,0),
- ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults4),
- ok.
-
-fill_and_reach_two_wrapfiles_2(PrivDir,RegExp,[Node|Rest]) ->
- ?l ok=rpc:call(Node,?MODULE,fill_and_reach_two_wrapfiles_3,[PrivDir,RegExp]),
- fill_and_reach_two_wrapfiles_2(PrivDir,RegExp,Rest);
-fill_and_reach_two_wrapfiles_2(_,_,[]) ->
- ok.
-
-fill_and_reach_two_wrapfiles_3(Dir,RegExp) ->
- ok=send_to_test_proc({apply,?MODULE,test_function,[]},
- fun reach_two_wraps_stopfun/1,
- {Dir,RegExp++atom_to_list(node())},
- 100).
-
-%% Help function intended to be used as fun in a send_to_test_proc/4 call.
-%% The function lists the content of Dir and looks for occurancies of String.
-%% If two files containing the string String are found, 'done' is returned.
-%% Otherwise 'continue'.
-reach_two_wraps_stopfun({Dir,RegExp}) ->
- case file:list_dir(Dir) of
- {ok,FileNames} ->
- case how_many_files_regexp(FileNames,RegExp,0) of
- {ok,2} ->
- done;
- _ ->
- continue
- end;
- {error,_Reason} ->
- error
- end.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Help function for the overload tests. These functions are used as callbacks.
-%% ------------------------------------------------------------------------------
-
-overload1(_) ->
- ets:update_counter(inviso_sideeffect_tab,ovl1,1),
- ok.
-%% This function is used when timeout occurs inside the runtime component.
-%% That is it is time to check for overload.
-overload2({timeout,overload2i_data}) ->
- ets:update_counter(inviso_sideeffect_tab,ovl2,1),
- ok.
-overload2i() ->
- ets:insert(inviso_sideeffect_tab,{ovl2,0}),
- {ok,overload2i_data}.
-overload2r(overload2i_data) ->
- ets:delete(inviso_sideeffect_tab,ovl2).
-
-%% This function is used when timeout occurs inside the runtime component.
-%% That is it is time to check for overload.
-overload3({timeout,overload3i_data}) ->
- ets:update_counter(inviso_sideeffect_tab,ovl3,1),
- ok;
-overload3(_) -> % Must handle garbage too.
- ignore.
-overload3i() ->
- ets:insert(inviso_sideeffect_tab,{ovl3,0}),
- {ok,overload3i_data}.
-overload3r(overload3i_data) ->
- ets:insert(inviso_sideeffect_tab,{ovl3r,done}),
- ets:delete(inviso_sideeffect_tab,ovl3).
-
-overload4(_) ->
- case ets:lookup(inviso_sideeffect_tab,ovl4_suspend) of
- [] -> % We are supposed to be running.
- ets:update_counter(inviso_sideeffect_tab,ovl4,1),
- ok;
- [_] ->
- {suspend,test}
- end.
-
-%% This function is used when overload check is done by icomming message.
-overload5({msg,{test_of_loadcheck,overload5i_data}}) ->
- ets:update_counter(inviso_sideeffect_tab,ovl5,1),
- ok;
-overload5(_) ->
- ignore.
-overload5i() ->
- ets:insert(inviso_sideeffect_tab,{ovl5,0}),
- {ok,overload5i_data}.
-overload5r(overload5i_data) ->
- ets:delete(inviso_sideeffect_tab,ovl5),
- ets:insert(inviso_sideeffect_tab,{ovl5r,done});
-overload5r(X) ->
- erlang:display({'***',overload5r,X}).
-
-overload6(_) ->
- ets:update_counter(inviso_sideeffect_tab,ovl6,1),
- ok.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Help function for the subscription tests. These function implements a collector
-%% process which will subscribe to inviso_events from the control component.
-%% ------------------------------------------------------------------------------
-
-%% Function which can be used to check if an inviso_event has arrived. The function
-%% takes a fun which tests the messages.
-check_msg_collector([],_,_) ->
- true;
-check_msg_collector(_,_,0) ->
- false;
-check_msg_collector(Nodes,Fun,T) ->
- Ref=make_ref(),
- inviso_collector_proc ! {fetch_message,self(),Ref,Fun},
- receive
- {inviso,Ref,{true,Node}} ->
- check_msg_collector(lists:delete(Node,Nodes),Fun,T-1);
- {inviso,Ref,false} ->
- timer:sleep(100),
- check_msg_collector(Nodes,Fun,T-1)
- end.
-
-%% Spawn on this function to get a subscriber.
-inviso_msg_collector() ->
- register(inviso_collector_proc,self()),
- inviso_msg_collector_loop([]).
-
-inviso_msg_collector_loop(Msgs) ->
- receive
- {fetch_message,From,Ref,Fun} ->
- {NewMsgs,Reply}=inviso_msg_collector_selector(Msgs,Fun,[]),
- From ! {inviso,Ref,Reply},
- inviso_msg_collector_loop(NewMsgs);
- Msg ->
- inviso_msg_collector_loop([Msg|Msgs])
- end.
-
-inviso_msg_collector_selector([M|Rest],Fun,Accum) ->
- case Fun(M) of
- {true,X} ->
- {Rest++Accum,{true,X}};
- _ ->
- inviso_msg_collector_selector(Rest,Fun,[M|Accum])
- end;
-inviso_msg_collector_selector([],_,Accum) ->
- {Accum,false}.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Help functions
-%% ==============================================================================
-
-list_search([E|Rest],Fun) ->
- case Fun(E) of
- true ->
- true;
- false ->
- list_search(Rest,Fun)
- end;
-list_search([],_Fun) ->
- false.
-%% ------------------------------------------------------------------------------
-
-%% Help function checking that there is a Result for each node in Nodes.
-%% Returns 'true' if successful.
-check_noderesults(Nodes,Fun,[{Node,Result}|Rest]) when is_function(Fun) ->
- case Fun({Node,Result}) of
- true ->
- case lists:member(Node,Nodes) of
- true ->
- check_noderesults(lists:delete(Node,Nodes),Fun,Rest);
- false -> % Not good.
- unknown_node_in_returnvalue
- end;
- _ ->
- illegal_result
- end;
-check_noderesults(Nodes,Result,[{Node,Result}|Rest]) ->
- case lists:member(Node,Nodes) of
- true ->
- check_noderesults(lists:delete(Node,Nodes),Result,Rest);
- false -> % Not good.
- unknown_node_in_returnvalue
- end;
-check_noderesults([],_,[]) ->
- true;
-check_noderesults(X,Y,Z) ->
- io:format("Bad arguments to check noderesults:~w~n~w~n~w~n",[X,Y,Z]),
- false.
-%% ------------------------------------------------------------------------------
-
-%% Help function doing rpc on all nodes in Nodes calling M:F. Returns 'true' if
-%% successful.
-check_on_nodes([Node|Rest],M,F,Args,Result) when Node==node() ->
- if
- is_function(Result) ->
- ?l true=Result(apply(M,F,Args));
- true ->
- ?l Result=apply(M,F,Args)
- end,
- check_on_nodes(Rest,M,F,Args,Result);
-check_on_nodes([Node|Rest],M,F,Args,Result) ->
- if
- is_function(Result) ->
- ?l true=Result(rpc:call(Node,M,F,Args));
- true ->
- ?l Result=rpc:call(Node,M,F,Args)
- end,
- check_on_nodes(Rest,M,F,Args,Result);
-check_on_nodes([],_,_,_,_) ->
- true.
-%% ------------------------------------------------------------------------------
-
-%% Help function which given a list of files searches through it and returns
-%% how many satisfies the RegExp.
-%% Returns {ok,N}.
-how_many_files_regexp([],_,N) ->
- {ok,N};
-how_many_files_regexp([FName|Rest],RegExp,N) ->
- case re:run(FName,RegExp,[{capture,none}]) of
- match ->
- how_many_files_regexp(Rest,RegExp,N+1);
- nomatch ->
- how_many_files_regexp(Rest,RegExp,N);
- {error,Reason} ->
- test_server:fail(Reason)
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function killing a bunch of registered processes.
-process_killer([RegName|Rest]) ->
- case whereis(RegName) of
- undefined ->
- case global:whereis_name(RegName) of
- undefined ->
- process_killer(Rest);
- P when is_pid(P) ->
- if
- node()==node(P) ->
- exit(P,kill);
- true ->
- true
- end,
- process_killer(Rest)
- end;
- P when is_pid(P) ->
- exit(P,kill),
- process_killer(Rest)
- end;
-process_killer([]) ->
- true.
-%% ------------------------------------------------------------------------------
-
-%% Help function which waits for a function call to become Result. This is useful
-%% if what we are waiting for can happend independantly of indications we have
-%% access to.
-poll(_,_,_,_,0) ->
- error;
-poll(M,F,Args,Result,Times) ->
- try apply(M,F,Args) of
- What when is_function(Result) ->
- case Result(What) of
- true ->
- ok;
- _ ->
- timer:sleep(100),
- poll(M,F,Args,Result,Times-1)
- end;
- Result ->
- ok;
- _ ->
- timer:sleep(100),
- poll(M,F,Args,Result,Times-1)
- catch
- error:Reason ->
- io:format("Apply in suite-function poll/5 failed, ~w~n",[Reason]),
- timer:sleep(100),
- poll(M,F,Args,Result,Times-1)
- end.
-%% ------------------------------------------------------------------------------
-
-insert_remotenode_config(Name,Node,Config) ->
- [{remotenode,{Name,Node}}|Config].
-%% ------------------------------------------------------------------------------
-
-insert_timetraphandle_config(Handle,Config) ->
- [{timetraphandle,Handle}|Config].
-%% ------------------------------------------------------------------------------
-
-get_remotenode_config(Name, [{remotenode, {Name, Node}}| _Cs]) ->
- Node;
-get_remotenode_config(Name, [_ | Cs]) ->
- get_remotenode_config(Name, Cs);
-get_remotenode_config(Name, []) ->
- exit({no_remotenode, Name}).
-
-%% ------------------------------------------------------------------------------
-
-get_timetraphandle_config(Config) ->
- {value,{_,Handle}}=lists:keysearch(timetraphandle,1,Config),
- Handle.
-%% ------------------------------------------------------------------------------
-
-get_remotenodes_config([{remotenode,{_Name,Node}}|Config]) ->
- [Node|get_remotenodes_config(Config)];
-get_remotenodes_config([_|Config]) ->
- get_remotenodes_config(Config);
-get_remotenodes_config([]) ->
- [].
-%% ------------------------------------------------------------------------------
-
-remove_remotenode_config(Name, [{remotenode, {Name, _}} | Cs]) ->
- Cs;
-remove_remotenode_config(Name, [C | Cs]) ->
- [C | remove_remotenode_config(Name, Cs)];
-remove_remotenode_config(_Name, []) ->
- [].
-
-%% ------------------------------------------------------------------------------
-
-remove_timetraphandle_config(Config) ->
- lists:keydelete(timetraphandle,1,Config).
-%% ------------------------------------------------------------------------------
-
-%% This function can be meta traced in order to check that exception_trace works.
-%% Must be exported.
-failing_function(nofailure) ->
- true;
-failing_function(failure) ->
- exit(failure).
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Code for a test process which can be started.
-%% ==============================================================================
-
-test_proc_init() ->
- register(inviso_test_proc,self()),
- test_proc_loop().
-
-test_proc_loop() ->
- receive
- {apply,M,F,Args} ->
- apply(M,F,Args),
- test_proc_loop();
- X ->
- io:format("Got ~w~n",[X]),
- test_proc_loop()
- end.
-
-global_test_proc_init() ->
- global:register_name(global_inviso_test_proc,self()),
- test_proc_loop().
-%% ------------------------------------------------------------------------------
-
-send_to_test_proc(_,_,_,0) ->
- error;
-send_to_test_proc(Msg,Fun,FunArg,N) ->
- inviso_test_proc ! Msg,
- case Fun(FunArg) of
- done ->
- ok;
- error ->
- test_server:fail(send_to_test_proc);
- _ ->
- send_to_test_proc(Msg,Fun,FunArg,N-1)
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% This function is here to be traced on by the inviso_test_proc. Must be exported.
-test_function() ->
- 1+1.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Code for a test side effect table process.
-%% ==============================================================================
-
-%% The side effect logger is a process owning a public ETS table. The idea is that
-%% various callback functions can write in the table when called. In that way
-%% correct calling of the call-backs can be verified.
-start_side_effect_logger(Node) ->
- ?l true=is_pid(spawn(Node,?MODULE,side_effect_logger_proc,[])),
- ?l ok=poll(rpc,call,[Node,ets,lookup,[inviso_sideeffect_tab,foo]],[],20).
-
-%% This one must be exported.
-side_effect_logger_proc() ->
- register(inviso_tab_proc,self()), % So we can kill it later.
- ets:new(inviso_sideeffect_tab,[public,named_table]),
- side_effect_logger_proc_2().
-
-side_effect_logger_proc_2() ->
- receive
- _X -> % This process is not expecting anything!
- side_effect_logger_proc_2()
- end.
-%% ------------------------------------------------------------------------------
diff --git a/lib/runtime_tools/test/inviso_testmodule1_foo.erl b/lib/runtime_tools/test/inviso_testmodule1_foo.erl
deleted file mode 100644
index a7a22cad39..0000000000
--- a/lib/runtime_tools/test/inviso_testmodule1_foo.erl
+++ /dev/null
@@ -1,9 +0,0 @@
--module(inviso_testmodule1_foo).
-
--compile(export_all).
-
-%% The purpose of this module is simply to have a module that is
-%% guaranteed not loaded.
-
-foo() ->
- true.
diff --git a/lib/runtime_tools/test/runtime_tools_SUITE.erl b/lib/runtime_tools/test/runtime_tools_SUITE.erl
index b26f3dd881..62497ab527 100644
--- a/lib/runtime_tools/test/runtime_tools_SUITE.erl
+++ b/lib/runtime_tools/test/runtime_tools_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -25,7 +25,7 @@
-export([init_per_testcase/2, end_per_testcase/2]).
%% Test cases
--export([app_file/1]).
+-export([app_file/1, start_stop_app/1]).
%% Default timetrap timeout (set in init_per_testcase)
-define(default_timeout, ?t:minutes(1)).
@@ -42,7 +42,8 @@ end_per_testcase(_Case, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app_file].
+ [app_file,
+ start_stop_app].
groups() ->
[].
@@ -60,10 +61,14 @@ end_per_group(_GroupName, Config) ->
Config.
-app_file(suite) ->
- [];
-app_file(doc) ->
- ["Testing .app file"];
-app_file(Config) when is_list(Config) ->
+app_file(_Config) ->
?line ok = ?t:app_test(runtime_tools),
ok.
+
+start_stop_app(_Config) ->
+ ok = application:start(runtime_tools),
+ Sup = whereis(runtime_tools_sup),
+ true = is_pid(Sup),
+ Ref = erlang:monitor(process,Sup),
+ ok = application:stop(runtime_tools),
+ receive {'DOWN', Ref, process, Sup, shutdown} -> ok end.
diff --git a/lib/runtime_tools/vsn.mk b/lib/runtime_tools/vsn.mk
index c9b5cc1a41..534c7508d8 100644
--- a/lib/runtime_tools/vsn.mk
+++ b/lib/runtime_tools/vsn.mk
@@ -1 +1 @@
-RUNTIME_TOOLS_VSN = 1.8.8
+RUNTIME_TOOLS_VSN = 1.8.9
diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml
index e4a2c98db7..1333e375b1 100644
--- a/lib/sasl/doc/src/notes.xml
+++ b/lib/sasl/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/sasl/examples/src/Makefile b/lib/sasl/examples/src/Makefile
index 47c8626205..c0d73aab1d 100644
--- a/lib/sasl/examples/src/Makefile
+++ b/lib/sasl/examples/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2011. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. 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/lib/sasl/src/Makefile b/lib/sasl/src/Makefile
index cae8146ebc..de0c45e6ae 100644
--- a/lib/sasl/src/Makefile
+++ b/lib/sasl/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl
index 61e660e918..e8b28998c1 100644
--- a/lib/sasl/src/systools_make.erl
+++ b/lib/sasl/src/systools_make.erl
@@ -673,7 +673,7 @@ check_item({_,{registered,Regs}},I) ->
_ -> throw({bad_param, I})
end;
check_item({_,{modules,Mods}},I) ->
- case mod_list_p(Mods) of
+ case a_list_p(Mods) of
true -> Mods;
_ -> throw({bad_param, I})
end;
@@ -900,11 +900,10 @@ find_pos(N, Name, [_OtherAppl|OrderedAppls]) ->
check_modules(Appls, Path, TestP, Machine) ->
%% first check that all the module names are unique
- %% Make a list M1 = [{Mod,Vsn,App,AppVsn,Dir}]
- %% where Vsn = '$$ignore$$' | Specified
- M1 = [{Mod,Vsn,App,Appv,A#application.dir} ||
- {{App,Appv},A} <- Appls,
- {Mod,Vsn} <- get_mod_vsn(A#application.modules)],
+ %% Make a list M1 = [{Mod,App,Dir}]
+ M1 = [{Mod,App,A#application.dir} ||
+ {{App,_Appv},A} <- Appls,
+ Mod <- A#application.modules],
case duplicates(M1) of
[] ->
case check_mods(M1, Appls, Path, TestP, Machine) of
@@ -918,16 +917,8 @@ check_modules(Appls, Path, TestP, Machine) ->
throw({error, {duplicate_modules, Dups}})
end.
-get_mod_vsn([{Mod,Vsn}|Mods]) ->
- [{Mod,Vsn}|get_mod_vsn(Mods)];
-get_mod_vsn([Mod|Mods]) ->
- [{Mod,'$$ignore$$'}|get_mod_vsn(Mods)];
-get_mod_vsn([]) ->
- [].
-
%%______________________________________________________________________
-%% Check that all modules exists and that the specified version
-%% corresponds to the version in the module's source code.
+%% Check that all modules exists.
%% Use the module extension of the running machine as extension for
%% the checked modules.
@@ -952,7 +943,7 @@ check_src(Modules, Appls, Path, true, Machine) ->
Ext = objfile_extension(Machine),
IncPath = create_include_path(Appls, Path),
append(map(fun(ModT) ->
- {Mod,_Vsn,App,_,Dir} = ModT,
+ {Mod,App,Dir} = ModT,
case check_mod(Mod,App,Dir,Ext,IncPath) of
ok ->
[];
@@ -1421,10 +1412,7 @@ create_mandatory_path(Appls, PathFlag, Variables) ->
%% Load all modules, except those in Mandatory_modules.
load_appl_mods([{{Name,Vsn},A}|Appls], Mand, PathFlag, Variables) ->
- Mods = map(fun({Mod,_}) -> Mod;
- (Mod) -> Mod
- end,
- A#application.modules),
+ Mods = A#application.modules,
load_commands(filter(fun(Mod) -> not member(Mod, Mand) end, Mods),
cr_path(Name, Vsn, A, PathFlag, Variables)) ++
load_appl_mods(Appls, Mand, PathFlag, Variables);
@@ -1784,9 +1772,7 @@ add_appl(Name, Vsn, App, Tar, Variables, Flags, Var) ->
add_to_tar(Tar,
filename:join(AppDir, Name ++ ".app"),
filename:join(BinDir, Name ++ ".app")),
- add_modules(map(fun({Mod,_}) -> to_list(Mod);
- (Mod) -> to_list(Mod)
- end,
+ add_modules(map(fun(Mod) -> to_list(Mod) end,
App#application.modules),
Tar,
AppDir,
@@ -2026,14 +2012,6 @@ t_list_p([{A,_}|T]) when is_atom(A) -> t_list_p(T);
t_list_p([]) -> true;
t_list_p(_) -> false.
-% check if a term is a list of atoms or two-tuples with the first
-% element as an atom.
-
-mod_list_p([{A,_}|T]) when is_atom(A) -> mod_list_p(T);
-mod_list_p([A|T]) when is_atom(A) -> mod_list_p(T);
-mod_list_p([]) -> true;
-mod_list_p(_) -> false.
-
% check if a term is a list of atoms.
a_list_p([A|T]) when is_atom(A) -> a_list_p(T);
diff --git a/lib/sasl/src/systools_rc.erl b/lib/sasl/src/systools_rc.erl
index c16f6aa845..cf5cca7cb3 100644
--- a/lib/sasl/src/systools_rc.erl
+++ b/lib/sasl/src/systools_rc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -326,8 +326,7 @@ translate_application_instrs(Script, Appls, PreAppls) ->
fun({add_application, Appl, Type}) ->
case lists:keysearch(Appl, #application.name, Appls) of
{value, Application} ->
- Mods =
- remove_vsn(Application#application.modules),
+ Mods = Application#application.modules,
ApplyL = case Type of
none -> [];
load -> [{apply, {application, load, [Appl]}}];
@@ -349,9 +348,11 @@ translate_application_instrs(Script, Appls, PreAppls) ->
end,
case lists:keysearch(Appl, #application.name, PreAppls) of
{value, RemApplication} ->
- Mods = remove_vsn(RemApplication#application.modules),
+ Mods = RemApplication#application.modules,
+
[{apply, {application, stop, [Appl]}}] ++
- [{remove, {M, brutal_purge, brutal_purge}} || M <- Mods] ++
+ [{remove, {M, brutal_purge, brutal_purge}}
+ || M <- Mods] ++
[{purge, Mods},
{apply, {application, unload, [Appl]}}];
false ->
@@ -360,16 +361,14 @@ translate_application_instrs(Script, Appls, PreAppls) ->
({restart_application, Appl}) ->
case lists:keysearch(Appl, #application.name, PreAppls) of
{value, PreApplication} ->
- PreMods =
- remove_vsn(PreApplication#application.modules),
-
+ PreMods = PreApplication#application.modules,
case lists:keysearch(Appl, #application.name, Appls) of
{value, PostApplication} ->
- PostMods =
- remove_vsn(PostApplication#application.modules),
-
+ PostMods = PostApplication#application.modules,
+
[{apply, {application, stop, [Appl]}}] ++
- [{remove, {M, brutal_purge, brutal_purge}} || M <- PreMods] ++
+ [{remove, {M, brutal_purge, brutal_purge}}
+ || M <- PreMods] ++
[{purge, PreMods}] ++
[{add_module, M, []} || M <- PostMods] ++
[{apply, {application, start,
@@ -385,11 +384,6 @@ translate_application_instrs(Script, Appls, PreAppls) ->
end, Script),
lists:flatten(L).
-remove_vsn(Mods) ->
- lists:map(fun({Mod, _Vsn}) -> Mod;
- (Mod) -> Mod
- end, Mods).
-
%%-----------------------------------------------------------------
%% Translates add_module into load_module (high-level transformation)
%%-----------------------------------------------------------------
@@ -654,15 +648,9 @@ translate_dep_to_low(Mode, Instructions, Appls) ->
end.
get_lib(Mod, [#application{name = Name, vsn = Vsn, modules = Modules} | T]) ->
- %% Module = {Mod, Vsn} | Mod
- case lists:keysearch(Mod, 1, Modules) of
- {value, _} ->
- {Name, Vsn};
- false ->
- case lists:member(Mod, Modules) of
- true -> {Name, Vsn};
- false -> get_lib(Mod, T)
- end
+ case lists:member(Mod, Modules) of
+ true -> {Name, Vsn};
+ false -> get_lib(Mod, T)
end;
get_lib(Mod, []) ->
throw({error, {no_such_module, Mod}}).
diff --git a/lib/sasl/test/Makefile b/lib/sasl/test/Makefile
index bb3f82f411..837abf74d4 100644
--- a/lib/sasl/test/Makefile
+++ b/lib/sasl/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2011. All Rights Reserved.
+# Copyright Ericsson AB 2011-2012. 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/lib/sasl/test/installer.erl b/lib/sasl/test/installer.erl
index 6942ec21ea..709269a73c 100644
--- a/lib/sasl/test/installer.erl
+++ b/lib/sasl/test/installer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2012. 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
@@ -876,7 +876,9 @@ trace_disallowed_calls(Node) ->
MasterProc = self(),
rpc:call(Node,dbg,tracer,[process,{fun(T,_) -> MasterProc ! T end,[]}]),
rpc:call(Node,dbg,p,[all,call]),
- rpc:call(Node,dbg,tp,[file,[{'_',[],[{message,{caller}}]}]]).
+ rpc:call(Node,dbg,tp,[file,[{'_',[],[{message,{caller}}]}]]),
+ %% File:native_name_encoding/0 is a BIF and OK to use
+ rpc:call(Node,dbg,ctp,[file,native_name_encoding,0]).
check_disallowed_calls(TestNode,Line) ->
receive
diff --git a/lib/sasl/test/rb_SUITE.erl b/lib/sasl/test/rb_SUITE.erl
index 35a4eb7e7b..b0e43be3a2 100644
--- a/lib/sasl/test/rb_SUITE.erl
+++ b/lib/sasl/test/rb_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2012. 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
@@ -66,23 +66,31 @@ init_per_group(running_error_logger,Config) ->
restart_sasl(),
Config.
-end_per_group(running_error_logger,Config) ->
+end_per_group(running_error_logger,_Config) ->
%% Remove log_mf_h???
ok.
init_per_testcase(_Case,Config) ->
case whereis(?SUP) of
- undefined -> ok;
- Pid -> kill(Pid)
+ undefined ->
+ ok;
+ Sup ->
+ Server = whereis(?MODULE),
+ exit(Sup,kill),
+ wait_for_down([Server,Sup])
end,
empty_error_logs(Config),
Config.
-kill(Pid) ->
+wait_for_down([]) ->
+ ok;
+wait_for_down([undefined|Rest]) ->
+ wait_for_down(Rest);
+wait_for_down([Pid|Rest]) ->
Ref = erlang:monitor(process,Pid),
- exit(Pid,kill),
- receive {'DOWN', Ref, process, Pid, _Info} -> ok end.
+ receive {'DOWN', Ref, process, Pid, _Info} -> ok end,
+ wait_for_down(Rest).
end_per_testcase(Case,Config) ->
try apply(?MODULE,Case,[cleanup,Config])
@@ -96,8 +104,9 @@ end_per_testcase(Case,Config) ->
help(_Config) ->
Help = capture(fun() -> rb:h() end),
- "Report Browser Tool - usage" = hd(Help),
- "rb:stop - stop the rb_server" = lists:last(Help),
+ %% Check that first and last line is there
+ true = lists:member("Report Browser Tool - usage", Help),
+ true = lists:member("rb:stop - stop the rb_server", Help),
ok.
%% Test that all three sasl env vars must be set for a successful start of rb
diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl
index a8e3cc3d88..87a755031c 100644
--- a/lib/sasl/test/release_handler_SUITE.erl
+++ b/lib/sasl/test/release_handler_SUITE.erl
@@ -1206,12 +1206,25 @@ otp_9395_rm_many_mods(cleanup,_Conf) ->
stop_node(node_name(otp_9395_rm_many_mods)).
otp_9864(Conf) ->
+ case os:type() of
+ {win32,_} ->
+ {skip,"Testing handling of symlinks - skipped on windows"};
+ _ ->
+ do_otp_9864(Conf)
+ end.
+do_otp_9864(Conf) ->
%% Set some paths
PrivDir = priv_dir(Conf),
Dir = filename:join(PrivDir,"otp_9864"),
RelDir = filename:join(?config(data_dir, Conf), "app1_app2"),
- LibDir1 = filename:join(RelDir, "lib1"),
- LibDir2 = filename:join(RelDir, "lib2"),
+
+ %% Copy libs to priv_dir because remove_release will remove some
+ %% of these again, and we don't want to remove anything from
+ %% data_dir
+ copy_tree(Conf,filename:join(RelDir, "lib1"),Dir),
+ copy_tree(Conf,filename:join(RelDir, "lib2"),Dir),
+ LibDir1 = filename:join(Dir, "lib1"),
+ LibDir2 = filename:join(Dir, "lib2"),
%% Create the releases
Rel1 = create_and_install_fake_first_release(Dir,
@@ -1229,10 +1242,6 @@ otp_9864(Conf) ->
{ok, Node} = t_start_node(otp_9864, Rel1, filename:join(Rel1Dir,"sys.config")),
%% Unpack rel2 (make sure it does not work if an AppDir is bad)
- LibDir3 = filename:join(RelDir, "lib3"),
- {error, {no_such_directory, _}} =
- rpc:call(Node, release_handler, set_unpacked,
- [Rel2++".rel", [{app1,"2.0",LibDir2}, {app2,"1.0",LibDir3}]]),
{ok, RelVsn2} =
rpc:call(Node, release_handler, set_unpacked,
[Rel2++".rel", [{app1,"2.0",LibDir2}, {app2,"1.0",LibDir2}]]),
@@ -1243,22 +1252,27 @@ otp_9864(Conf) ->
ok = rpc:call(Node, release_handler, install_file,
[RelVsn2, filename:join(Rel2Dir, "sys.config")]),
- %% Install RelVsn2 without {update_paths, true} option
+ %% Install RelVsn2
{ok, RelVsn1, []} =
rpc:call(Node, release_handler, install_release, [RelVsn2]),
- %% Install RelVsn1 again
+ %% Create a symlink inside release 2
+ Releases2Dir = filename:join([Dir,"releases","2"]),
+ Link = filename:join(Releases2Dir,"foo_symlink_dir"),
+ file:make_symlink(Releases2Dir,Link),
+
+ %% Back down to RelVsn1
{ok, RelVsn1, []} =
rpc:call(Node, release_handler, install_release, [RelVsn1]),
- TempRel2Dir = filename:join(Dir,"releases/2"),
- file:make_symlink(TempRel2Dir, filename:join(TempRel2Dir, "foo_symlink_dir")),
-
%% This will fail if symlinks are not handled
ok = rpc:call(Node, release_handler, remove_release, [RelVsn2]),
ok.
+otp_9864(cleanup,_Conf) ->
+ stop_node(node_name(otp_9864)).
+
upgrade_supervisor(Conf) when is_list(Conf) ->
%% Set some paths
diff --git a/lib/sasl/test/release_handler_SUITE_data/Makefile.src b/lib/sasl/test/release_handler_SUITE_data/Makefile.src
index 55d20aa8b6..b794aa0e6f 100644
--- a/lib/sasl/test/release_handler_SUITE_data/Makefile.src
+++ b/lib/sasl/test/release_handler_SUITE_data/Makefile.src
@@ -1,9 +1,5 @@
EFLAGS=+debug_info
-P2B= \
- P2B/a-2.0/ebin/a.@EMULATOR@ \
- P2B/a-2.0/ebin/a_sup.@EMULATOR@
-
LIB= \
lib/a-9.1/ebin/a.@EMULATOR@ \
lib/a-9.1/ebin/a_sup.@EMULATOR@ \
@@ -80,13 +76,7 @@ SUP= \
release_handler_timeouts/dummy-0.1/ebin/dummy_sup.@EMULATOR@ \
release_handler_timeouts/dummy-0.1/ebin/dummy_sup_2.@EMULATOR@
-all: $(P2B) $(LIB) $(APP) $(OTP2740) $(C) $(SUP)
-
-P2B/a-2.0/ebin/a.@EMULATOR@: P2B/a-2.0/src/a.erl
- erlc $(EFLAGS) -oP2B/a-2.0/ebin P2B/a-2.0/src/a.erl
-P2B/a-2.0/ebin/a_sup.@EMULATOR@: P2B/a-2.0/src/a_sup.erl
- erlc $(EFLAGS) -oP2B/a-2.0/ebin P2B/a-2.0/src/a_sup.erl
-
+all: $(LIB) $(APP) $(OTP2740) $(C) $(SUP)
lib/a-1.0/ebin/a.@EMULATOR@: lib/a-1.0/src/a.erl
erlc $(EFLAGS) -olib/a-1.0/ebin lib/a-1.0/src/a.erl
diff --git a/lib/sasl/test/release_handler_SUITE_data/c/c.app b/lib/sasl/test/release_handler_SUITE_data/c/c.app
index 908a94cf2d..2f56196918 100644
--- a/lib/sasl/test/release_handler_SUITE_data/c/c.app
+++ b/lib/sasl/test/release_handler_SUITE_data/c/c.app
@@ -1,7 +1,7 @@
{application, c,
[{description, "C CXC 138 11"},
{vsn, "1.0"},
- {modules, [b, {aa, 1}, {c_sup,1}]},
+ {modules, [b, aa, c_sup]},
{registered, [cc,bb,c_sup]},
{applications, [kernel, stdlib]},
{env, [{key1, val1}]},
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app
index e938137f67..7ae189a36a 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app
@@ -1,7 +1,7 @@
{application, a,
[{description, "A CXC 138 11"},
{vsn, "1.0"},
- {modules, [{a, 1}, {a_sup,1}]},
+ {modules, [a, a_sup]},
{registered, [a_sup]},
{applications, [kernel, stdlib]},
{env, [{key1, val1}]},
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app
index 1c3053b2fa..f8ab31fab9 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app
@@ -1,7 +1,7 @@
{application, a,
[{description, "A CXC 138 11"},
{vsn, "1.1"},
- {modules, [{a, 2}, {a_sup,1}]},
+ {modules, [a, a_sup]},
{registered, [a_sup]},
{applications, [kernel, stdlib]},
{env, [{key1, val1}]},
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.app
index b38722f06d..2393af0493 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.app
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.app
@@ -1,7 +1,7 @@
{application, a,
[{description, "A CXC 138 11"},
{vsn, "1.2"},
- {modules, [{a, 2}, {a_sup,1}]},
+ {modules, [a, a_sup]},
{registered, [a_sup]},
{applications, [kernel, stdlib]},
{env, [{key1, val1}]},
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/ebin/b.app b/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/ebin/b.app
index 00347b2754..e2eada00a4 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/ebin/b.app
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/ebin/b.app
@@ -2,6 +2,6 @@
{application, b,
[{description, "B CXC 138 12"},
{vsn, "1.0"},
- {modules, [{b_server, 1},{b_lib, 1}]},
+ {modules, [b_server,b_lib]},
{registered, [b_server]},
{applications, [kernel, stdlib]}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.app b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.app
index 73c8e42b32..e4f8369ae5 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.app
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.app
@@ -2,6 +2,6 @@
{application, b,
[{description, "B CXC 138 12"},
{vsn, "2.0"},
- {modules, [{b_server, 1}]},
+ {modules, [b_server]},
{registered, [b_server]},
{applications, [kernel, stdlib]}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.0/ebin/many_mods.app b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.0/ebin/many_mods.app
index aa39adfffa..5c56c4cd74 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.0/ebin/many_mods.app
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.0/ebin/many_mods.app
@@ -2,16 +2,6 @@
{application, many_mods,
[{description, "Application with many modules CXC 138 11"},
{vsn, "1.0"},
- {modules, [{m, 1},
- {m1,1},
- {m2,1},
- {m3,1},
- {m4,1},
- {m5,1},
- {m6,1},
- {m7,1},
- {m8,1},
- {m9,1},
- {m10,1}]},
+ {modules, [m,m1,m2,m3,m4,m5,m6,m7,m8,m9,m10]},
{registered, []},
{applications, [kernel, stdlib]}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.1/ebin/many_mods.app b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.1/ebin/many_mods.app
index 36c50caf2f..87fdfe8442 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.1/ebin/many_mods.app
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-1.1/ebin/many_mods.app
@@ -2,16 +2,6 @@
{application, many_mods,
[{description, "Application with many modules CXC 138 11"},
{vsn, "1.1"},
- {modules, [{m, 1},
- {m1,1},
- {m2,1},
- {m3,1},
- {m4,1},
- {m5,1},
- {m6,1},
- {m7,1},
- {m8,1},
- {m9,1},
- {m10,1}]},
+ {modules, [m,m1,m2,m3,m4,m5,m6,m7,m8,m9,m10]},
{registered, []},
{applications, [kernel, stdlib]}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-2.0/ebin/many_mods.app b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-2.0/ebin/many_mods.app
index 98f6527750..aba906d667 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-2.0/ebin/many_mods.app
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/many_mods-2.0/ebin/many_mods.app
@@ -2,6 +2,6 @@
{application, many_mods,
[{description, "Application with many modules CXC 138 11"},
{vsn, "2.0"},
- {modules, [{m, 1}]},
+ {modules, [m]},
{registered, []},
{applications, [kernel, stdlib]}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app
index d375768b99..0878b80cb5 100644
--- a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app
+++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app
@@ -1,7 +1,7 @@
{application, db,
[{description, "ERICSSON NR FOR DB"},
{vsn, "2.0"},
- {modules, [{db1, "1.0"}, {db2, "1.0"}]},
+ {modules, [db1, db2]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app
index d3bd85cda6..33d44805bd 100644
--- a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "3.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-2.1/ebin/fe.app
index 3cb0b0c2cf..d7fae9e092 100644
--- a/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-2.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-2.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "2.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{mod, {fe1, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app
index 0696e2494c..682d40eb12 100644
--- a/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "3.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{mod, {fe1, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app
index 191919f8d3..a1025c306a 100644
--- a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app
+++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app
@@ -1,7 +1,7 @@
{application, db,
[{description, "ERICSSON NR FOR DB"},
{vsn, "2.1"},
- {modules, [{db1, "1.0"}, {db2, "1.0"}]},
+ {modules, [db1, db2]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app
index d3bd85cda6..33d44805bd 100644
--- a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "3.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app
index 3e0ac3c3c9..28d0f80d34 100644
--- a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app
+++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app
@@ -1,7 +1,7 @@
{application, db,
[{description, "ERICSSON NR FOR DB"},
{vsn, "2.1"},
- {modules, [{db1, "1.0"}, {db2, "1.0"}, {db3, "2.0"}]},
+ {modules, [db1, db2, db3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app
index 191919f8d3..a1025c306a 100644
--- a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app
+++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app
@@ -1,7 +1,7 @@
{application, db,
[{description, "ERICSSON NR FOR DB"},
{vsn, "2.1"},
- {modules, [{db1, "1.0"}, {db2, "1.0"}]},
+ {modules, [db1, db2]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app
index d3bd85cda6..33d44805bd 100644
--- a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "3.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app
index 47ea248720..717d30cf45 100644
--- a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "2.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app
index 0696e2494c..682d40eb12 100644
--- a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "3.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{mod, {fe1, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-500.18.7/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-500.18.7/ebin/fe.app
index 3a5c0ddd9b..77b97979cb 100644
--- a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-500.18.7/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-500.18.7/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "500.18.7"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{mod, {fe1, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app
index 22530ee335..3968ce3c32 100644
--- a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app
+++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app
@@ -1,7 +1,7 @@
{application, db,
[{description, "ERICSSON NR FOR DB"},
{vsn, "1.0"},
- {modules, [{db1, "1.0"}, {db2, "1.0"}]},
+ {modules, [db1, db2]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app
index 7243a0a96a..0f0c926fbd 100644
--- a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app
+++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app
@@ -1,7 +1,7 @@
{application, db,
[{description, "ERICSSON NR FOR DB"},
{vsn, "1.1"},
- {modules, [{db1, "1.0"}, {db2, "1.0"}]},
+ {modules, [db1, db2]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app
index 202d7f1234..6901d225e2 100644
--- a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app
+++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app
@@ -1,7 +1,7 @@
{application, db,
[{description, "ERICSSON NR FOR DB"},
{vsn, "2.1"},
- {modules, [{db1, "1.0"}, {db2, "1.0"}]},
+ {modules, [db1, db2]},
{registered, []},
{applications, []},
{mod, {db1, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app
index c7ba1dfe91..d963a7e7fa 100644
--- a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "2.1.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app
index 47ea248720..717d30cf45 100644
--- a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "2.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app
index 0696e2494c..682d40eb12 100644
--- a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "3.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{mod, {fe1, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1.1/ebin/fe.app
index c7ba1dfe91..d963a7e7fa 100644
--- a/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "2.1.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1/ebin/fe.app
index 47ea248720..717d30cf45 100644
--- a/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-2.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "2.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{env, []},
diff --git a/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.app
index 0696e2494c..682d40eb12 100644
--- a/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.app
+++ b/lib/sasl/test/systools_SUITE_data/d_regexp_appup/lib/fe-3.1/ebin/fe.app
@@ -1,7 +1,7 @@
{application, fe,
[{description, "ERICSSON NR FOR FE"},
{vsn, "3.1"},
- {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]},
+ {modules, [fe1, fe2, fe3]},
{registered, []},
{applications, []},
{mod, {fe1, []}}]}.
diff --git a/lib/sasl/test/systools_rc_SUITE.erl b/lib/sasl/test/systools_rc_SUITE.erl
index bd4aa9e7a7..0cb6e63cf3 100644
--- a/lib/sasl/test/systools_rc_SUITE.erl
+++ b/lib/sasl/test/systools_rc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -46,7 +46,7 @@ syntax_check(Config) when is_list(Config) ->
[#application{name = test,
description = "TEST",
vsn = "0.1",
- modules = [{foo,1},{bar,1},{baz,1},{old_mod,1}],
+ modules = [foo,bar,baz,old_mod],
regs = [],
mod = {sasl, []}},
#application{name = snmp,
@@ -59,7 +59,7 @@ syntax_check(Config) when is_list(Config) ->
[#application{name = test,
description = "TEST",
vsn = "1.0",
- modules = [{foo,1},{bar,1},{baz,1},{new_mod,1}],
+ modules = [foo,bar,baz,new_mod],
regs = [],
mod = {sasl, []}}],
S1 = [
@@ -128,8 +128,8 @@ translate(Config) when is_list(Config) ->
[#application{name = test,
description = "TEST",
vsn = "1.0",
- modules = [{foo,1},{bar,1},{baz,1},
- {x,1},{y,1},{z,1}],
+ modules = [foo,bar,baz,
+ x,y,z],
regs = [],
mod = {sasl, []}}],
%% Simple translation (1)
@@ -439,7 +439,7 @@ translate_app(Config) when is_list(Config) ->
[#application{name = test,
description = "TEST",
vsn = "1.0",
- modules = [{foo,1},{bar,1},{baz,1}],
+ modules = [foo,bar,baz],
regs = [],
mod = {sasl, []}},
#application{name = pelle,
@@ -452,7 +452,7 @@ translate_app(Config) when is_list(Config) ->
[#application{name = test,
description = "TEST",
vsn = "1.0",
- modules = [{foo,1},{bar,1},{baz,1}],
+ modules = [foo,bar,baz],
regs = [],
mod = {sasl, []}}],
%% Simple translation (1)
@@ -492,13 +492,13 @@ translate_emulator_restarts(_Config) ->
[#application{name = test,
description = "TEST",
vsn = "1.0",
- modules = [{foo,1},{bar,1},{baz,1}],
+ modules = [foo,bar,baz],
regs = [],
mod = {sasl, []}},
#application{name = test,
description = "TEST2",
vsn = "1.0",
- modules = [{x,1},{y,1},{z,1}],
+ modules = [x,y,z],
regs = [],
mod = {sasl, []}}],
%% restart_new_emulator
diff --git a/lib/snmp/doc/src/Makefile b/lib/snmp/doc/src/Makefile
index 6c58ae994a..8820565124 100644
--- a/lib/snmp/doc/src/Makefile
+++ b/lib/snmp/doc/src/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml
index 2d045faa0f..442837d57d 100644
--- a/lib/snmp/doc/src/notes.xml
+++ b/lib/snmp/doc/src/notes.xml
@@ -34,6 +34,98 @@
<section>
+ <title>SNMP Development Toolkit 4.22.1</title>
+ <p>Version 4.22.1 supports code replacement in runtime from/to
+ version 4.22, 4.21.7 4.21.6 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1 and
+ 4.21. </p>
+
+ <section>
+ <title>Improvements and new features</title>
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>[agent] Sematic fixes to SNMP-USER-BASED-SM-MIB.
+ The semantics allow the <c>usmUserAuthKeyChange</c> and
+ <c>usmUserPrivKeyChange</c> objects to be written to in the
+ same set requests that also creates and clones the user.
+ This was not possible beforehand, causing test tools checking
+ semantic SNMPv3 behaviour to fail on a lot of test cases. </p>
+ <p>Furthermore, once the user has been cloned by writing to an
+ instance of <c>usmUserCloneFrom</c>, further set-operations to
+ the same object will not return an error, but be no-ops.
+ Especially, it must be avoided to copy security parameters
+ again (possibly even from a different user). </p>
+ <p>Stefan Zegenhagen</p>
+ <p>Own Id: OTP-10166</p>
+ </item>
+
+ <item>
+ <p>[agent] Errors in <c>vacmAccessTable</c> RowStatus handling.
+ There are problems with the handling of vacmAccessTableStatus
+ that cause some SNMP test suites to report errors.
+ Most notably, erroneous set operations frequently cause "genErr"
+ errors to be returned. These "genErr" errors are usually caused
+ by badmatch exceptions coming from
+ <c>{ok, Row} = snmpa_vacm:get_row(RowIndex)</c>
+ if the row does not exist. </p>
+ <p>The semantics of the RowStatus handling in that table has
+ been adjusted to be compliant with the RowStatus
+ textual description of SNPMv2-TC MIB. </p>
+ <p>Stefan Zegenhagen</p>
+ <p>Own Id: OTP-10164</p>
+ </item>
+ </list>
+
+ </section>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>[agent] Fix walk over vacmAccessTable.
+ Fix the get_next implementation of vacmAccessTable to
+ return all table entries. </p>
+ <p>The get_next implementation of vacmAccessTable did not return
+ all available table data. Instead, it only returned the first
+ column for each row, and all columns for the last row available. </p>
+ <p>Stefan Zegenhagen</p>
+ <p>Own Id: OTP-10165</p>
+ </item>
+
+ <item>
+ <p>[manager]
+ <seealso marker="snmpm#log_to_io">snmpm:log_to_io/6</seealso>
+ did not use the LogName argument. </p>
+ <p>Own Id: OTP-10066</p>
+ </item>
+
+ <item>
+ <p>Incorrect TimeTicks decode. Also bad handling of
+ invalid encode (value outside of value range) for both
+ <c>TimeTicks</c> and <c>Unsigned32</c>. </p>
+ <p>Own Id: OTP-10132</p>
+ </item>
+
+ </list>
+
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+ </section>
+
+ </section> <!-- 4.22.1 -->
+
+
+ <section>
<title>SNMP Development Toolkit 4.22</title>
<p>Version 4.22 supports code replacement in runtime from/to
version 4.21.7 4.21.6 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1 and 4.21. </p>
diff --git a/lib/snmp/doc/src/snmp_config.xml b/lib/snmp/doc/src/snmp_config.xml
index 340f2f1fa7..eec53162a1 100644
--- a/lib/snmp/doc/src/snmp_config.xml
+++ b/lib/snmp/doc/src/snmp_config.xml
@@ -963,7 +963,8 @@ Manager snmp config:
7b. User name? hobbes
7c. Security name? [hobbes]
7d. Authentication protocol (no/sha/md5)? [no] sha
-7e Authentication [sha] key (length 0 or 20)? [""] [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
+7e Authentication [sha] key (length 0 or 20)? [""] [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, \
+ 17,18,19,20]
7d. Priv protocol (no/des/aes)? [no] des
7f Priv [des] key (length 0 or 16)? [""] 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25
7. Configure an usm user handled by this manager (y/n)? [y] n
diff --git a/lib/snmp/doc/src/snmpa_network_interface_filter.xml b/lib/snmp/doc/src/snmpa_network_interface_filter.xml
index 10419517dd..bff0a24879 100644
--- a/lib/snmp/doc/src/snmpa_network_interface_filter.xml
+++ b/lib/snmp/doc/src/snmpa_network_interface_filter.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>2007</year><year>2010</year>
+ <year>2007</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -84,7 +84,9 @@
<title>DATA TYPES</title>
<code type="none">
port() = integer() > 0
-pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | 'set-request' | trap | 'get-bulk-request' | 'inform-request' | report
+pdu_type() = 'get-request' | 'get-next-request' | 'get-response' |
+ 'set-request' | trap | 'get-bulk-request' | 'inform-request' |
+ report
</code>
<marker id="accept_recv"></marker>
</section>
diff --git a/lib/snmp/doc/src/snmpm.xml b/lib/snmp/doc/src/snmpm.xml
index 9bbb6cdbdb..8ab3be8e18 100644
--- a/lib/snmp/doc/src/snmpm.xml
+++ b/lib/snmp/doc/src/snmpm.xml
@@ -173,16 +173,16 @@ sec_level() = noAuthNoPriv | authNoPriv | authPriv
</type>
<desc>
<p>Register the manager entity (=user) responsible for specific
- agent(s). </p>
+ agent(s). </p>
<p><c>Module</c> is the callback module (snmpm_user behaviour) which
- will be called whenever something happens (detected
- agent, incoming reply or incoming trap/notification).
- Note that this could have already been done as a
- consequence of the node config. (see users.conf).</p>
+ will be called whenever something happens (detected
+ agent, incoming reply or incoming trap/notification).
+ Note that this could have already been done as a
+ consequence of the node config. (see users.conf).</p>
- <p>The argument <c>DefaultAgentConfig</c> is used as default values when
- this user register agents.</p>
+ <p>The argument <c>DefaultAgentConfig</c> is used as default
+ values when this user register agents.</p>
<p>The type of <c>Val</c> depends on <c>Item</c>: </p>
<code type="none"><![CDATA[
diff --git a/lib/snmp/doc/src/snmpm_network_interface_filter.xml b/lib/snmp/doc/src/snmpm_network_interface_filter.xml
index 5f80cec94e..4dc133dd71 100644
--- a/lib/snmp/doc/src/snmpm_network_interface_filter.xml
+++ b/lib/snmp/doc/src/snmpm_network_interface_filter.xml
@@ -82,7 +82,9 @@
<title>DATA TYPES</title>
<code type="none">
port() = integer() > 0
-pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | 'set-request' | trap | 'get-bulk-request' | 'inform-request' | report | trappdu
+pdu_type() = 'get-request' | 'get-next-request' | 'get-response' |
+ 'set-request' | trap | 'get-bulk-request' | 'inform-request' |
+ report | trappdu
</code>
<marker id="accept_recv"></marker>
</section>
diff --git a/lib/snmp/examples/ex1/Makefile b/lib/snmp/examples/ex1/Makefile
index 996b686e0e..8d10476b30 100644
--- a/lib/snmp/examples/ex1/Makefile
+++ b/lib/snmp/examples/ex1/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/snmp/examples/ex2/Makefile b/lib/snmp/examples/ex2/Makefile
index 090b9274d4..a85ff8e4e2 100644
--- a/lib/snmp/examples/ex2/Makefile
+++ b/lib/snmp/examples/ex2/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+# Copyright Ericsson AB 2006-2012. 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/lib/snmp/mibs/Makefile.in b/lib/snmp/mibs/Makefile.in
index c74c0b2ac4..5c1f855fad 100644
--- a/lib/snmp/mibs/Makefile.in
+++ b/lib/snmp/mibs/Makefile.in
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/snmp/priv/conf/agent/Makefile b/lib/snmp/priv/conf/agent/Makefile
index c7921bacc2..3082889dc8 100644
--- a/lib/snmp/priv/conf/agent/Makefile
+++ b/lib/snmp/priv/conf/agent/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/snmp/priv/conf/manager/Makefile b/lib/snmp/priv/conf/manager/Makefile
index ba5a74c3fe..a04c227754 100644
--- a/lib/snmp/priv/conf/manager/Makefile
+++ b/lib/snmp/priv/conf/manager/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2009. All Rights Reserved.
+# Copyright Ericsson AB 2004-2012. 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/lib/snmp/src/agent/Makefile b/lib/snmp/src/agent/Makefile
index 9a982edf91..beed696648 100644
--- a/lib/snmp/src/agent/Makefile
+++ b/lib/snmp/src/agent/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/snmp/src/agent/snmp_user_based_sm_mib.erl b/lib/snmp/src/agent/snmp_user_based_sm_mib.erl
index 2e801622e7..3c4ba1af66 100644
--- a/lib/snmp/src/agent/snmp_user_based_sm_mib.erl
+++ b/lib/snmp/src/agent/snmp_user_based_sm_mib.erl
@@ -54,6 +54,7 @@
%% Columns not accessible via SNMP
-define(usmUserAuthKey, 14).
-define(usmUserPrivKey, 15).
+-define(is_cloning, 16).
%%%-----------------------------------------------------------------
@@ -564,7 +565,9 @@ usmUserTable(set, RowIndex, Cols0) ->
{ok, Cols} ->
?vtrace("usmUserTable(set) -> verified"
"~n Cols: ~p", [Cols]),
- NCols = pre_set(RowIndex, Cols),
+ % check whether we're cloning. if so, get cloned params and add a few
+ % defaults that might be needed.
+ NCols = pre_set(RowIndex, validate_clone_from(RowIndex, Cols)),
?vtrace("usmUserTable(set) -> pre-set: "
"~n NCols: ~p", [NCols]),
%% NOTE: The NCols parameter is sent to snmp_generic, but not to
@@ -730,30 +733,40 @@ validate_is_set_ok(Error, _RowIndex, _Cols) ->
Error.
do_validate_is_set_ok(RowIndex, Cols) ->
- validate_clone_from(RowIndex, Cols),
- validate_auth_protocol(RowIndex, Cols),
- validate_auth_key_change(RowIndex, Cols),
- validate_own_auth_key_change(RowIndex, Cols),
- validate_priv_protocol(RowIndex, Cols),
- validate_priv_key_change(RowIndex, Cols),
- validate_own_priv_key_change(RowIndex, Cols),
+ NCols = validate_clone_from(RowIndex, Cols),
+ validate_auth_protocol(RowIndex, NCols),
+ validate_auth_key_change(RowIndex, NCols),
+ validate_own_auth_key_change(RowIndex, NCols),
+ validate_priv_protocol(RowIndex, NCols),
+ validate_priv_key_change(RowIndex, NCols),
+ validate_own_priv_key_change(RowIndex, NCols),
ok.
pre_set(RowIndex, Cols) ->
+ %% Remove the ?is_cloning member again; it must no longer be
+ %% present.
+ Cols0 = key1delete(?is_cloning, Cols),
%% Possibly initialize the usmUserSecurityName and privacy keys
case snmp_generic:table_row_exists(db(usmUserTable), RowIndex) of
- true -> Cols;
+ true -> Cols0;
false ->
SecName = get_user_name(RowIndex),
- [{?usmUserSecurityName, SecName} | Cols] ++
- [{?usmUserAuthKey, ""},
- {?usmUserPrivKey, ""}]
+ Cols1 = [{?usmUserSecurityName, SecName} | Cols0],
+ case proplists:get_value(?is_cloning, Cols) of
+ true ->
+ % the row is just being cloned. the cloned user's
+ % passwords are already present in Cols and must
+ % not be overwritten.
+ Cols1;
+ _ ->
+ Cols1 ++ [{?usmUserAuthKey, ""},
+ {?usmUserPrivKey, ""}]
+ end
end.
validate_set({noError, 0}, RowIndex, Cols) ->
%% Now, all is_set_ok validation steps have been executed. So
%% everything is ready for the set.
- set_clone_from(RowIndex, Cols),
set_auth_key_change(RowIndex, Cols),
set_own_auth_key_change(RowIndex, Cols),
set_priv_key_change(RowIndex, Cols),
@@ -769,7 +782,7 @@ validate_set(Error, _RowIndex, _Cols) ->
%% no further checks.
%%-----------------------------------------------------------------
validate_clone_from(RowIndex, Cols) ->
- case lists:keysearch(?usmUserCloneFrom, 1, Cols) of
+ case key1search(?usmUserCloneFrom, Cols) of
{value, {_Col, RowPointer}} ->
RowIndex2 = extract_row(RowPointer),
OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable),
@@ -778,35 +791,63 @@ validate_clone_from(RowIndex, Cols) ->
case OldCloneFrom of
{value, Val} when Val /= noinit ->
%% This means that the cloning is already done...
- ok;
+ no_cloning(Cols);
_ ->
- %% Otherwise, we must check the CloneFrom value
- case snmp_generic:table_get_element(db(usmUserTable),
- RowIndex2,
- ?usmUserStatus) of
- {value, ?'RowStatus_active'} -> ok;
- _ -> inconsistentName(?usmUserCloneFrom)
- end
+ %% Otherwise, we must check the CloneFrom value. It
+ %% must relate to a usmUserEntry that exists and is active.
+ case snmp_generic:table_get_row(db(usmUserTable), RowIndex2) of
+ CloneFromRow when is_tuple(CloneFromRow) ->
+ case element(?usmUserStatus, CloneFromRow) of
+ ?'RowStatus_active' ->
+ get_cloned_cols(CloneFromRow, Cols);
+ _ ->
+ inconsistentName(?usmUserCloneFrom)
+ end;
+ undefined ->
+ inconsistentName(?usmUserCloneFrom)
+ end
end;
false ->
- ok
+ % no ?usmUserCloneFrom specified, don't modify columns
+ no_cloning(Cols)
end.
+get_cloned_cols(CloneFromRow, Cols) ->
+ % initialize cloned columns with data from CloneFromRow
+ % and overwrite that again with data found in Cols
+ AuthP = element(?usmUserAuthProtocol, CloneFromRow),
+ PrivP = element(?usmUserPrivProtocol, CloneFromRow),
+ AuthK = element(?usmUserAuthKey, CloneFromRow),
+ PrivK = element(?usmUserPrivKey, CloneFromRow),
+ ClonedCols = [{?usmUserAuthProtocol, AuthP},
+ {?usmUserPrivProtocol, PrivP},
+ {?usmUserAuthKey, AuthK},
+ {?usmUserPrivKey, PrivK},
+ {?is_cloning, true}
+ ],
+ Func = fun({Col, _} = Item, NCols) ->
+ key1store(Col, NCols, Item)
+ end,
+ Cols1 = lists:foldl(Func, ClonedCols, Cols),
+ key1sort(Cols1).
+
+no_cloning(Cols0) ->
+ Cols1 = key1delete(?usmUserCloneFrom, Cols0),
+ key1delete(?is_cloning, Cols1).
+
validate_auth_protocol(RowIndex, Cols) ->
- case lists:keysearch(?usmUserAuthProtocol, 1, Cols) of
+ case key1search(?usmUserAuthProtocol, Cols) of
{value, {_Col, AuthProtocol}} ->
- %% Check if the row has been cloned; we can't check the
+ %% Check if the row is being cloned; we can't check the
%% old value of authProtocol, because if the row was
%% createAndWaited, the default value would have been
%% written (usmNoAuthProtocol).
- OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable),
- RowIndex,
- ?usmUserCloneFrom),
- case OldCloneFrom of
- {value, Val} when Val /= noinit ->
- %% This means that the cloning is already done; set is ok
- %% if new protocol is usmNoAuthProtocol
+ IsCloning = proplists:get_value(?is_cloning, Cols, false),
+ if
+ not IsCloning ->
+ %% This means that the row is not being cloned right
+ %% now; set is ok if new protocol is usmNoAuthProtocol
case AuthProtocol of
?usmNoAuthProtocol ->
%% Check that the Priv protocl is noPriv
@@ -821,7 +862,7 @@ validate_auth_protocol(RowIndex, Cols) ->
_ ->
wrongValue(?usmUserAuthProtocol)
end;
- _ ->
+ true ->
%% Otherwise, check that the new protocol is known,
%% and that the system we're running supports the
%% hash function.
@@ -867,7 +908,7 @@ validate_own_priv_key_change(RowIndex, Cols) ->
%% Check that the requesting user is the same as the modified user
validate_requester(RowIndex, Cols, KeyChangeCol) ->
- case lists:keysearch(KeyChangeCol, 1, Cols) of
+ case key1search(KeyChangeCol, Cols) of
{value, _} ->
case get(sec_model) of % Check the securityModel in the request
?SEC_USM -> ok;
@@ -890,17 +931,14 @@ validate_requester(RowIndex, Cols, KeyChangeCol) ->
end.
validate_key_change(RowIndex, Cols, KeyChangeCol, Type) ->
- case lists:keysearch(KeyChangeCol, 1, Cols) of
+ case key1search(KeyChangeCol, Cols) of
{value, {_Col, KeyC}} ->
%% Check if the row has been cloned; or if it is cloned in
%% this set-operation.
OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable),
RowIndex,
?usmUserCloneFrom),
- IsClonePresent = case lists:keysearch(?usmUserCloneFrom, 1, Cols) of
- {value, _} -> true;
- false -> false
- end,
+ IsClonePresent = proplists:get_value(?is_cloning, Cols, false),
%% Set is ok if 1) the user already is created, 2) this is
%% a new user, which has been cloned, or is about to be
%% cloned.
@@ -912,7 +950,7 @@ validate_key_change(RowIndex, Cols, KeyChangeCol, Type) ->
%% The user is cloned in this operation
ok;
_ ->
- %% The user doen't exist, or hasn't been cloned,
+ %% The user doesn't exist, or hasn't been cloned,
%% and is not cloned in this operation.
inconsistentName(KeyChangeCol)
end,
@@ -939,17 +977,15 @@ validate_key_change(RowIndex, Cols, KeyChangeCol, Type) ->
end.
validate_priv_protocol(RowIndex, Cols) ->
- case lists:keysearch(?usmUserPrivProtocol, 1, Cols) of
+ case key1search(?usmUserPrivProtocol, Cols) of
{value, {_Col, PrivProtocol}} ->
%% Check if the row has been cloned; we can't check the
%% old value of privhProtocol, because if the row was
%% createAndWaited, the default value would have been
%% written (usmNoPrivProtocol).
- OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable),
- RowIndex,
- ?usmUserCloneFrom),
- case OldCloneFrom of
- {value, Val} when Val /= noinit ->
+ IsCloning = proplists:get_value(?is_cloning, Cols, false),
+ if
+ not IsCloning ->
%% This means that the cloning is already done; set is ok
%% if new protocol is usmNoPrivProtocol
case PrivProtocol of
@@ -962,7 +998,7 @@ validate_priv_protocol(RowIndex, Cols) ->
_ ->
wrongValue(?usmUserPrivProtocol)
end;
- _ ->
+ true ->
%% Otherwise, check that the new protocol is known,
%% and that the system we're running supports the
%% crypto function.
@@ -1005,31 +1041,6 @@ validate_priv_protocol(RowIndex, Cols) ->
end.
-set_clone_from(RowIndex, Cols) ->
- %% If CloneFrom is modified, do the cloning.
- case lists:keysearch(?usmUserCloneFrom, 1, Cols) of
- {value, {_Col, RowPointer}} ->
- RowIndex2 = extract_row(RowPointer), % won't fail
- CloneRow = snmp_generic:table_get_row(db(usmUserTable), RowIndex2,
- foi(usmUserTable)),
- AuthP = element(?usmUserAuthProtocol, CloneRow),
- PrivP = element(?usmUserPrivProtocol, CloneRow),
- AuthK = element(?usmUserAuthKey, CloneRow),
- PrivK = element(?usmUserPrivKey, CloneRow),
- SCols = [{?usmUserAuthProtocol, AuthP},
- {?usmUserPrivProtocol, PrivP},
- {?usmUserAuthKey, AuthK},
- {?usmUserPrivKey, PrivK}],
- case snmp_generic:table_set_elements(db(usmUserTable),
- RowIndex,
- SCols) of
- true -> ok;
- false -> {commitFailed, ?usmUserCloneFrom}
- end;
- false ->
- ok
- end.
-
set_auth_key_change(RowIndex, Cols) ->
set_key_change(RowIndex, Cols, ?usmUserAuthKeyChange, auth).
@@ -1043,7 +1054,7 @@ set_own_priv_key_change(RowIndex, Cols) ->
set_key_change(RowIndex, Cols, ?usmUserOwnPrivKeyChange, priv).
set_key_change(RowIndex, Cols, KeyChangeCol, Type) ->
- case lists:keysearch(KeyChangeCol, 1, Cols) of
+ case key1search(KeyChangeCol, Cols) of
{value, {_Col, KeyChange}} ->
KeyCol = case Type of
auth -> ?usmUserAuthKey;
@@ -1071,11 +1082,11 @@ extract_row([H | T], [H | T2]) -> extract_row(T, T2);
extract_row([], [?usmUserSecurityName | T]) -> T;
extract_row(_, _) -> wrongValue(?usmUserCloneFrom).
-%% Pre: the user exixt
+%% Pre: the user exists or is being cloned in this operation
get_auth_proto(RowIndex, Cols) ->
- %% The protocol can be chanegd by the request too, otherwise,
+ %% The protocol can be changed by the request too, otherwise,
%% check the stored protocol.
- case lists:keysearch(?usmUserAuthProtocol, 1, Cols) of
+ case key1search(?usmUserAuthProtocol, Cols) of
{value, {_, Protocol}} ->
Protocol;
false ->
@@ -1090,11 +1101,11 @@ get_auth_proto(RowIndex, Cols) ->
end
end.
-%% Pre: the user exixt
+%% Pre: the user exists or is being cloned in this operation
get_priv_proto(RowIndex, Cols) ->
- %% The protocol can be chanegd by the request too, otherwise,
+ %% The protocol can be changed by the request too, otherwise,
%% check the stored protocol.
- case lists:keysearch(?usmUserPrivProtocol, 1, Cols) of
+ case key1search(?usmUserPrivProtocol, Cols) of
{value, {_, Protocol}} ->
Protocol;
false ->
@@ -1232,6 +1243,27 @@ set_sname(_) -> %% Keep it, if already set.
error(Reason) ->
throw({error, Reason}).
+
+%%-----------------------------------------------------------------
+%% lists key-function(s) wrappers
+
+-compile({inline,key1delete/2}).
+key1delete(Key, List) ->
+ lists:keydelete(Key, 1, List).
+
+-compile({inline,key1search/2}).
+key1search(Key, List) ->
+ lists:keysearch(Key, 1, List).
+
+-compile({inline,key1store/3}).
+key1store(Key, List, Elem) ->
+ lists:keystore(Key, 1, List, Elem).
+
+-compile({inline,key1sort/1}).
+key1sort(List) ->
+ lists:keysort(1, List).
+
+
%%-----------------------------------------------------------------
info_msg(F, A) ->
diff --git a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl
index 479a44795f..436f15eb9c 100644
--- a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl
+++ b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl
@@ -565,45 +565,85 @@ vacmAccessTable(is_set_ok, RowIndex, Cols0) ->
case (catch verify_vacmAccessTable_cols(Cols0, [])) of
{ok, Cols} ->
IsValidKey = is_valid_key(RowIndex),
- case lists:keysearch(?vacmAccessStatus, 1, Cols) of
- %% Ok, if contextMatch is init
- {value, {Col, ?'RowStatus_active'}} ->
- {ok, Row} = snmpa_vacm:get_row(RowIndex),
- case element(?vacmAContextMatch, Row) of
- noinit -> {inconsistentValue, Col};
- _ -> {noError, 0}
+ StatusCol = lists:keyfind(?vacmAccessStatus, 1, Cols),
+ MaybeRow = snmpa_vacm:get_row(RowIndex),
+ case {StatusCol, MaybeRow} of
+ {{Col, ?'RowStatus_active'}, false} ->
+ %% row does not yet exist => inconsistentValue
+ %% (see SNMPv2-TC.mib, RowStatus textual convention)
+ {inconsistentValue, Col};
+ {{Col, ?'RowStatus_active'}, {ok, Row}} ->
+ %% Ok, if contextMatch is init
+ case element(?vacmAContextMatch, Row) of
+ noinit ->
+ %% check whether ContextMatch is being set in
+ %% the same operation
+ case proplists:get_value(?vacmAccessContextMatch,
+ Cols) of
+ undefined ->
+ %% no, we can't make this row active yet
+ {inconsistentValue, Col};
+ _ ->
+ %% ok, activate the row
+ {noError, 0}
+ end;
+ _ ->
+ {noError, 0}
end;
- {value, {Col, ?'RowStatus_notInService'}} -> % Ok, if not notReady
- {ok, Row} = snmpa_vacm:get_row(RowIndex),
+ {{Col, ?'RowStatus_notInService'}, false} ->
+ %% row does not yet exist => inconsistentValue
+ %% (see SNMPv2-TC.mib, RowStatus textual convention)
+ {inconsistentValue, Col};
+ {{Col, ?'RowStatus_notInService'}, {ok, Row}} ->
+ %% Ok, if not notReady
case element(?vacmAStatus, Row) of
- ?'RowStatus_notReady' -> {inconsistentValue, Col};
- _ -> {noError, 0}
+ ?'RowStatus_notReady' ->
+ {inconsistentValue, Col};
+ _ ->
+ {noError, 0}
end;
- {value, {Col, ?'RowStatus_notReady'}} -> % never ok!
+ {{Col, ?'RowStatus_notReady'}, _} ->
+ %% never ok!
{inconsistentValue, Col};
- {value, {Col, ?'RowStatus_createAndGo'}} -> % ok, if it doesn't exist
+ {{Col, ?'RowStatus_createAndGo'}, false} ->
+ %% ok, if it doesn't exist
Res = lists:keysearch(?vacmAccessContextMatch, 1, Cols),
- case snmpa_vacm:get_row(RowIndex) of
- false when (IsValidKey =:= true) andalso
- is_tuple(Res) -> {noError, 0};
- false -> {noCreation, Col}; % Bad RowIndex
- _ -> {inconsistentValue, Col}
+ if
+ IsValidKey =/= true ->
+ %% bad RowIndex => noCreation
+ {noCreation, Col};
+ is_tuple(Res) ->
+ %% required field is present => noError
+ {noError, 0};
+ true ->
+ %% required field is missing => inconsistentValue
+ {inconsistentValue, Col}
end;
- {value, {Col, ?'RowStatus_createAndWait'}} -> % ok, if it doesn't exist
- case snmpa_vacm:get_row(RowIndex) of
- false when (IsValidKey =:= true) -> {noError, 0};
- false -> {noCreation, Col}; % Bad RowIndex
- _ -> {inconsistentValue, Col}
+ {{Col, ?'RowStatus_createAndGo'}, _} ->
+ %% row already exists => inconsistentValue
+ {inconsistentValue, Col};
+ {{Col, ?'RowStatus_createAndWait'}, false} ->
+ %% ok, if it doesn't exist
+ if
+ IsValidKey =:= true ->
+ %% RowIndex is valid => noError
+ {noError, 0};
+ true ->
+ {noCreation, Col}
end;
- {value, {_Col, ?'RowStatus_destroy'}} -> % always ok!
+ {{Col, ?'RowStatus_createAndWait'}, _} ->
+ %% Row already exists => inconsistentValue
+ {inconsistentValue, Col};
+ {value, {_Col, ?'RowStatus_destroy'}} ->
+ %% always ok!
{noError, 0};
- _ -> % otherwise, it's a change; it must exist
- case snmpa_vacm:get_row(RowIndex) of
- {ok, _} ->
- {noError, 0};
- false ->
- {inconsistentName, element(1, hd(Cols))}
- end
+ {_, false} ->
+ %% otherwise, it's a row change;
+ %% row does not exist => inconsistentName
+ {inconsistentName, element(1, hd(Cols))};
+ _ ->
+ %% row change and row exists => noError
+ {noError, 0}
end;
Error ->
Error
@@ -734,10 +774,15 @@ do_vacmAccessTable_set(RowIndex, Cols) ->
%% Cols are sorted, and all columns are > 3.
+do_get_next(_RowIndex, []) ->
+ % Cols can be empty because we're called for each
+ % output of split_cols(); and one of that may well
+ % be empty.
+ [];
do_get_next(RowIndex, Cols) ->
case snmpa_vacm:get_next_row(RowIndex) of
{NextIndex, Row} ->
- F1 = fun(Col) when Col < ?vacmAccessStatus ->
+ F1 = fun(Col) when Col =< ?vacmAccessStatus ->
{[Col | NextIndex], element(Col-3, Row)};
(_) ->
endOfTable
@@ -745,9 +790,9 @@ do_get_next(RowIndex, Cols) ->
lists:map(F1, Cols);
false ->
case snmpa_vacm:get_next_row([]) of
- {_NextIndex, Row} ->
+ {NextIndex2, Row} ->
F2 = fun(Col) when Col < ?vacmAccessStatus ->
- {[Col+1 | RowIndex], element(Col-2, Row)};
+ {[Col+1 | NextIndex2], element(Col-2, Row)};
(_) ->
endOfTable
end,
diff --git a/lib/snmp/src/app/Makefile b/lib/snmp/src/app/Makefile
index 7a16d42c30..f7c311b663 100644
--- a/lib/snmp/src/app/Makefile
+++ b/lib/snmp/src/app/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. 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/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src
index 8360d88c94..593ddd82bd 100644
--- a/lib/snmp/src/app/snmp.appup.src
+++ b/lib/snmp/src/app/snmp.appup.src
@@ -22,8 +22,18 @@
%% ----- U p g r a d e -------------------------------------------------------
[
+ {"4.22",
+ [
+ {load_module, snmpm, soft_purge, soft_purge, []},
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+ {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []},
+ {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, []}
+ ]
+ },
{"4.21.7",
[
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+
{load_module, snmp_config, soft_purge, soft_purge, []},
{load_module, snmp_conf, soft_purge, soft_purge, []},
{load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
@@ -50,6 +60,8 @@
},
{"4.21.6",
[
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+
{load_module, snmp_config, soft_purge, soft_purge, []},
{load_module, snmp_conf, soft_purge, soft_purge, []},
{load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
@@ -76,6 +88,8 @@
},
{"4.21.5",
[
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+
{load_module, snmp_config, soft_purge, soft_purge, []},
{load_module, snmp_conf, soft_purge, soft_purge, []},
{load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
@@ -107,6 +121,8 @@
},
{"4.21.4",
[
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+
{load_module, snmp_config, soft_purge, soft_purge, []},
{load_module, snmp_conf, soft_purge, soft_purge, []},
{load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
@@ -141,6 +157,8 @@
},
{"4.21.3",
[
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+
{load_module, snmp_config, soft_purge, soft_purge, []},
{load_module, snmp_conf, soft_purge, soft_purge, []},
{load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
@@ -175,6 +193,8 @@
},
{"4.21.2",
[
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+
{load_module, snmp_config, soft_purge, soft_purge, []},
{load_module, snmp_conf, soft_purge, soft_purge, []},
{load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
@@ -211,6 +231,8 @@
},
{"4.21.1",
[
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+
{load_module, snmp_config, soft_purge, soft_purge, []},
{load_module, snmp_conf, soft_purge, soft_purge, []},
{load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
@@ -248,6 +270,8 @@
},
{"4.21",
[
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+
{load_module, snmp_config, soft_purge, soft_purge, []},
{load_module, snmp_conf, soft_purge, soft_purge, []},
{load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
@@ -288,8 +312,19 @@
%% ------D o w n g r a d e ---------------------------------------------------
[
+ {"4.22",
+ [
+ {load_module, snmpm, soft_purge, soft_purge, []},
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+ {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []},
+ {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, []}
+ ]
+ },
{"4.21.7",
[
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
{load_module, snmp_config, soft_purge, soft_purge, []},
{load_module, snmp_conf, soft_purge, soft_purge, []},
{load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
@@ -316,6 +351,8 @@
},
{"4.21.6",
[
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+
{load_module, snmp_config, soft_purge, soft_purge, []},
{load_module, snmp_conf, soft_purge, soft_purge, []},
{load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
@@ -342,6 +379,8 @@
},
{"4.21.5",
[
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+
{load_module, snmp_config, soft_purge, soft_purge, []},
{load_module, snmp_conf, soft_purge, soft_purge, []},
{load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
@@ -373,6 +412,8 @@
},
{"4.21.4",
[
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+
{load_module, snmp_config, soft_purge, soft_purge, []},
{load_module, snmp_conf, soft_purge, soft_purge, []},
{load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
@@ -407,6 +448,8 @@
},
{"4.21.3",
[
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+
{load_module, snmp_config, soft_purge, soft_purge, []},
{load_module, snmp_conf, soft_purge, soft_purge, []},
{load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
@@ -441,6 +484,8 @@
},
{"4.21.2",
[
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+
{load_module, snmp_config, soft_purge, soft_purge, []},
{load_module, snmp_conf, soft_purge, soft_purge, []},
{load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
@@ -477,6 +522,8 @@
},
{"4.21.1",
[
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+
{load_module, snmp_config, soft_purge, soft_purge, []},
{load_module, snmp_conf, soft_purge, soft_purge, []},
{load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
@@ -514,6 +561,8 @@
},
{"4.21",
[
+ {load_module, snmp_pdus, soft_purge, soft_purge, []},
+
{load_module, snmp_config, soft_purge, soft_purge, []},
{load_module, snmp_conf, soft_purge, soft_purge, []},
{load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]},
diff --git a/lib/snmp/src/compile/Makefile b/lib/snmp/src/compile/Makefile
index 9658ee0005..f63fc18997 100644
--- a/lib/snmp/src/compile/Makefile
+++ b/lib/snmp/src/compile/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/snmp/src/manager/Makefile b/lib/snmp/src/manager/Makefile
index 1f30669f02..c76dbacf90 100644
--- a/lib/snmp/src/manager/Makefile
+++ b/lib/snmp/src/manager/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2009. All Rights Reserved.
+# Copyright Ericsson AB 2004-2012. 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/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl
index 89eaee9f80..f590892c66 100644
--- a/lib/snmp/src/manager/snmpm.erl
+++ b/lib/snmp/src/manager/snmpm.erl
@@ -1411,7 +1411,7 @@ log_to_io(LogDir, Mibs, LogName, LogFile) ->
log_to_io(LogDir, Mibs, LogName, LogFile, Start) ->
snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Start).
log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) ->
- snmp:log_to_io(LogDir, Mibs, LogFile, Start, Stop).
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop).
change_log_size(NewSize) ->
diff --git a/lib/snmp/src/misc/Makefile b/lib/snmp/src/misc/Makefile
index c6a5064d89..698c341268 100644
--- a/lib/snmp/src/misc/Makefile
+++ b/lib/snmp/src/misc/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. 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/lib/snmp/src/misc/snmp_pdus.erl b/lib/snmp/src/misc/snmp_pdus.erl
index 0788d86b2d..15156f7467 100644
--- a/lib/snmp/src/misc/snmp_pdus.erl
+++ b/lib/snmp/src/misc/snmp_pdus.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -254,68 +254,95 @@ strip(0, _Tail) ->
%%----------------------------------------------------------------------
%% Returns:{Type, Value}
%%----------------------------------------------------------------------
+
+%% OBJECT IDENTIFIER
dec_value([6 | Bytes]) ->
{Value, Rest} = dec_oid_notag(Bytes),
{{'OBJECT IDENTIFIER', Value}, Rest};
dec_value([5,0 | T]) ->
{{'NULL', 'NULL'}, T};
+
+%% INTEGER
dec_value([2 | Bytes]) ->
{Value, Rest} = dec_integer_notag(Bytes),
{{'INTEGER', Value}, Rest};
+
+%% OCTET STRING
dec_value([4 | Bytes]) ->
{Value, Rest} = dec_oct_str_notag(Bytes),
{{'OCTET STRING', Value}, Rest};
+
+%% IpAddress
dec_value([64 | Bytes]) ->
{Value, Rest} = dec_oct_str_notag(Bytes),
{{'IpAddress', Value}, Rest};
+
+%% Counter32
dec_value([65 | Bytes]) ->
%% Counter32 is an unsigned 32 but is actually encoded as
%% a signed integer 32 (INTEGER).
{Value, Rest} = dec_integer_notag(Bytes),
Value2 =
- if
+ if
+ (Value >= 0) andalso (Value =< 16#ffffffff) ->
+ %% We accept value above 16#7fffffff
+ %% in order to be backward bug-compatible
+ Value;
+ (Value < 0) ->
+ 16#ffffffff + Value + 1;
+ true ->
+ exit({error, {bad_counter32, Value}})
+ end,
+ {{'Counter32', Value2}, Rest};
+
+%% Unsigned32
+dec_value([66 | Bytes]) ->
+ {Value, Rest} = dec_integer_notag(Bytes),
+ Value2 =
+ if
(Value >= 0) andalso (Value =< 16#ffffffff) ->
- %% We accept value above 16#7fffffff
- %% in order to be backward bug-compatible
Value;
(Value < 0) ->
16#ffffffff + Value + 1;
true ->
- exit({error, {bad_counter32, Value}})
+ exit({error, {bad_unsigned32, Value}})
end,
- {{'Counter32', Value2}, Rest};
-dec_value([66 | Bytes]) ->
- {Value, Rest} = dec_integer_notag(Bytes),
- if
- (Value >= 0) andalso (Value =< 4294967295) ->
- {{'Unsigned32', Value}, Rest};
- true ->
- exit({error, {bad_unsigned32, Value}})
- end;
+ {{'Unsigned32', Value2}, Rest};
+
+%% TimeTicks
dec_value([67 | Bytes]) ->
{Value, Rest} = dec_integer_notag(Bytes),
- if
- (Value >= 0) andalso (Value =< 4294967295) ->
- {{'TimeTicks', Value}, Rest};
+ Value2 =
+ if
+ (Value >= 0) andalso (Value =< 16#ffffffff) ->
+ Value;
+ (Value < 0) ->
+ 16#ffffffff + Value + 1;
true ->
exit({error, {bad_timeticks, Value}})
- end;
+ end,
+ {{'TimeTicks', Value2}, Rest};
+
+%% Opaque
dec_value([68 | Bytes]) ->
{Value, Rest} = dec_oct_str_notag(Bytes),
{{'Opaque', Value}, Rest};
+
+%% Counter64
dec_value([70 | Bytes]) ->
%% Counter64 is an unsigned 64 but is actually encoded as
%% a signed integer 64.
{Value, Rest} = dec_integer_notag(Bytes),
Value2 =
- if
- (Value >= 0) andalso (Value < 16#8000000000000000) ->
- Value;
- (Value < 0) ->
- 18446744073709551615 + Value + 1;
- true ->
- exit({error, {bad_counter64, Value}}) end,
+ if
+ (Value >= 0) andalso (Value < 16#8000000000000000) ->
+ Value;
+ (Value < 0) ->
+ 16#ffffffffffffffff + Value + 1;
+ true ->
+ exit({error, {bad_counter64, Value}}) end,
{{'Counter64', Value2}, Rest};
+
dec_value([128,0|T]) ->
{{'NULL', noSuchObject}, T};
dec_value([129,0|T]) ->
@@ -629,7 +656,7 @@ enc_VarBind_attributes(#varbind{oid = Oid, variabletype = Type,value = Val}) ->
ValueBytes = enc_value(Type, Val),
lists:append(OidBytes, ValueBytes).
-enc_value('INTEGER',Val) ->
+enc_value('INTEGER', Val) ->
enc_integer_tag(Val);
enc_value('OCTET STRING', Val) ->
enc_oct_str_tag(Val);
@@ -637,7 +664,7 @@ enc_value('BITS', Val) ->
enc_oct_str_tag(bits_to_str(Val));
enc_value('OBJECT IDENTIFIER', Val) ->
enc_oid_tag(Val);
-enc_value('IpAddress',Val) ->
+enc_value('IpAddress', Val) ->
Bytes2 = enc_oct_str_notag(Val),
Len2 = elength(length(Bytes2)),
lists:append([64 | Len2],Bytes2);
@@ -668,6 +695,24 @@ enc_value('Counter32', Val) ->
Bytes2 = enc_integer_notag(Val2),
Len2 = elength(length(Bytes2)),
lists:append([65 | Len2],Bytes2);
+enc_value('Unsigned32', Val) ->
+ if
+ (Val >= 0) andalso (Val =< 4294967295) ->
+ Bytes2 = enc_integer_notag(Val),
+ Len2 = elength(length(Bytes2)),
+ lists:append([66 | Len2], Bytes2);
+ true ->
+ exit({error, {bad_counter32, Val}})
+ end;
+enc_value('TimeTicks', Val) ->
+ if
+ (Val >= 0) andalso (Val =< 4294967295) ->
+ Bytes2 = enc_integer_notag(Val),
+ Len2 = elength(length(Bytes2)),
+ lists:append([67 | Len2], Bytes2);
+ true ->
+ exit({error, {bad_timeticks, Val}})
+ end;
enc_value('Counter64', Val) ->
Val2 =
if
@@ -682,18 +727,7 @@ enc_value('Counter64', Val) ->
end,
Bytes2 = enc_integer_notag(Val2),
Len2 = elength(length(Bytes2)),
- lists:append([70 | Len2],Bytes2);
-enc_value(Type, Val) ->
- Bytes2 = enc_integer_notag(Val),
- Len2 = elength(length(Bytes2)),
- lists:append([enc_val_tag(Type,Val) | Len2],Bytes2).
-
-enc_val_tag('Counter32',Val) when (Val >= 0) andalso (Val =< 4294967295) ->
- 65;
-enc_val_tag('Unsigned32', Val) when (Val >= 0) andalso (Val =< 4294967295) ->
- 66;
-enc_val_tag('TimeTicks', Val) when (Val >= 0) andalso (Val =< 4294967295) ->
- 67.
+ lists:append([70 | Len2],Bytes2).
%%----------------------------------------------------------------------
diff --git a/lib/snmp/test/exp/snmp_agent_bl_test.erl b/lib/snmp/test/exp/snmp_agent_bl_test.erl
index a5a6e8260b..263319aa5d 100644
--- a/lib/snmp/test/exp/snmp_agent_bl_test.erl
+++ b/lib/snmp/test/exp/snmp_agent_bl_test.erl
@@ -95,24 +95,13 @@ end_per_testcase(_Case, Config) when list(Config) ->
Config.
cases() ->
- case ?OSTYPE() of
- vxworks ->
- %% No crypto app, so skip v3 testcases
- [
- app_info,
- test_v1, test_v2, test_v1_v2,
- test_multi_threaded,
- mib_storage,
- tickets];
- _Else ->
- [
- app_info,
- test_v1, test_v2, test_v1_v2, test_v3,
- test_multi_threaded,
- mib_storage,
- tickets
- ]
- end.
+ [
+ app_info,
+ test_v1, test_v2, test_v1_v2, test_v3,
+ test_multi_threaded,
+ mib_storage,
+ tickets
+ ].
%%%-----------------------------------------------------------------
@@ -1187,21 +1176,16 @@ init_v3(Config) when list(Config) ->
%% and we will be stuck with a bunch of mnesia tables for
%% the rest of this suite...
?DBG("start_agent -> start crypto app",[]),
- case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- case ?CRYPTO_START() of
- ok ->
- case ?CRYPTO_SUPPORT() of
- {no, Reason} ->
- ?SKIP({unsupported_encryption, Reason});
- yes ->
- ok
- end;
- {error, Reason} ->
- ?SKIP({failed_starting_crypto, Reason})
- end
+ case ?CRYPTO_START() of
+ ok ->
+ case ?CRYPTO_SUPPORT() of
+ {no, Reason} ->
+ ?SKIP({unsupported_encryption, Reason});
+ yes ->
+ ok
+ end;
+ {error, Reason} ->
+ ?SKIP({failed_starting_crypto, Reason})
end,
SaNode = ?config(snmp_sa, Config),
create_tables(SaNode),
@@ -5071,12 +5055,7 @@ run(F, A, Opts) ->
CtxEngineID = snmp_misc:get_option(context_engine_id, Opts, EngineID),
Community = snmp_misc:get_option(community, Opts, "all-rights"),
?DBG("run -> start crypto app",[]),
- Crypto = case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- ?CRYPTO_START()
- end,
+ Crypto = ?CRYPTO_START(),
?DBG("run -> Crypto: ~p",[Crypto]),
catch snmp_test_mgr:stop(), % If we had a running mgr from a failed case
StdM = filename:join(code:priv_dir(snmp), "mibs") ++ "/",
diff --git a/lib/snmp/test/exp/snmp_agent_ms_test.erl b/lib/snmp/test/exp/snmp_agent_ms_test.erl
index d5eaea55fa..340b95f512 100644
--- a/lib/snmp/test/exp/snmp_agent_ms_test.erl
+++ b/lib/snmp/test/exp/snmp_agent_ms_test.erl
@@ -231,17 +231,13 @@ end_per_testcase(_Case, Config) when list(Config) ->
Config.
cases() ->
-case ?OSTYPE() of
- vxworks ->
- [app_info, {group, test_v1}, {group, test_v2},
- {group, test_v1_v2}, {group, test_multi_threaded},
- {group, mib_storage}, {group, tickets}];
- _Else ->
- [app_info, {group, test_v1}, {group, test_v2},
- {group, test_v1_v2}, {group, test_v3},
- {group, test_multi_threaded}, {group, mib_storage},
- {group, tickets}]
-end.
+ [
+ app_info,
+ {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_v3},
+ {group, test_multi_threaded}, {group, mib_storage},
+ {group, tickets}
+ ].
%%%-----------------------------------------------------------------
@@ -1221,21 +1217,16 @@ init_v3(Config) when list(Config) ->
%% and we will be stuck with a bunch of mnesia tables for
%% the rest of this suite...
?DBG("start_agent -> start crypto app",[]),
- case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- case ?CRYPTO_START() of
- ok ->
- case ?CRYPTO_SUPPORT() of
- {no, Reason} ->
- ?SKIP({unsupported_encryption, Reason});
- yes ->
- ok
- end;
- {error, Reason} ->
- ?SKIP({failed_starting_crypto, Reason})
- end
+ case ?CRYPTO_START() of
+ ok ->
+ case ?CRYPTO_SUPPORT() of
+ {no, Reason} ->
+ ?SKIP({unsupported_encryption, Reason});
+ yes ->
+ ok
+ end;
+ {error, Reason} ->
+ ?SKIP({failed_starting_crypto, Reason})
end,
SaNode = ?config(snmp_sa, Config),
create_tables(SaNode),
@@ -5066,12 +5057,7 @@ run(F, A, Opts) ->
CtxEngineID = snmp_misc:get_option(context_engine_id, Opts, EngineID),
Community = snmp_misc:get_option(community, Opts, "all-rights"),
?DBG("run -> start crypto app",[]),
- Crypto = case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- ?CRYPTO_START()
- end,
+ Crypto = ?CRYPTO_START(),
?DBG("run -> Crypto: ~p",[Crypto]),
catch snmp_test_mgr:stop(), % If we had a running mgr from a failed case
StdM = filename:join(code:priv_dir(snmp), "mibs") ++ "/",
diff --git a/lib/snmp/test/exp/snmp_agent_mt_test.erl b/lib/snmp/test/exp/snmp_agent_mt_test.erl
index d62bc6c2e7..33d104305a 100644
--- a/lib/snmp/test/exp/snmp_agent_mt_test.erl
+++ b/lib/snmp/test/exp/snmp_agent_mt_test.erl
@@ -231,17 +231,13 @@ end_per_testcase(_Case, Config) when list(Config) ->
Config.
cases() ->
-case ?OSTYPE() of
- vxworks ->
- [app_info, {group, test_v1}, {group, test_v2},
- {group, test_v1_v2}, {group, test_multi_threaded},
- {group, mib_storage}, {group, tickets}];
- _Else ->
- [app_info, {group, test_v1}, {group, test_v2},
- {group, test_v1_v2}, {group, test_v3},
- {group, test_multi_threaded}, {group, mib_storage},
- {group, tickets}]
-end.
+ [
+ app_info,
+ {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_v3},
+ {group, test_multi_threaded}, {group, mib_storage},
+ {group, tickets}
+ ].
%%%-----------------------------------------------------------------
@@ -1221,21 +1217,16 @@ init_v3(Config) when list(Config) ->
%% and we will be stuck with a bunch of mnesia tables for
%% the rest of this suite...
?DBG("start_agent -> start crypto app",[]),
- case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- case ?CRYPTO_START() of
- ok ->
- case ?CRYPTO_SUPPORT() of
- {no, Reason} ->
- ?SKIP({unsupported_encryption, Reason});
- yes ->
- ok
- end;
- {error, Reason} ->
- ?SKIP({failed_starting_crypto, Reason})
- end
+ case ?CRYPTO_START() of
+ ok ->
+ case ?CRYPTO_SUPPORT() of
+ {no, Reason} ->
+ ?SKIP({unsupported_encryption, Reason});
+ yes ->
+ ok
+ end;
+ {error, Reason} ->
+ ?SKIP({failed_starting_crypto, Reason})
end,
SaNode = ?config(snmp_sa, Config),
create_tables(SaNode),
@@ -5066,12 +5057,7 @@ run(F, A, Opts) ->
CtxEngineID = snmp_misc:get_option(context_engine_id, Opts, EngineID),
Community = snmp_misc:get_option(community, Opts, "all-rights"),
?DBG("run -> start crypto app",[]),
- Crypto = case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- ?CRYPTO_START()
- end,
+ Crypto = ?CRYPTO_START(),
?DBG("run -> Crypto: ~p",[Crypto]),
catch snmp_test_mgr:stop(), % If we had a running mgr from a failed case
StdM = filename:join(code:priv_dir(snmp), "mibs") ++ "/",
diff --git a/lib/snmp/test/exp/snmp_agent_v2_test.erl b/lib/snmp/test/exp/snmp_agent_v2_test.erl
index a86449ca72..dc3d2efbb3 100644
--- a/lib/snmp/test/exp/snmp_agent_v2_test.erl
+++ b/lib/snmp/test/exp/snmp_agent_v2_test.erl
@@ -231,17 +231,13 @@ end_per_testcase(_Case, Config) when list(Config) ->
Config.
cases() ->
-case ?OSTYPE() of
- vxworks ->
- [app_info, {group, test_v1}, {group, test_v2},
- {group, test_v1_v2}, {group, test_multi_threaded},
- {group, mib_storage}, {group, tickets}];
- _Else ->
- [app_info, {group, test_v1}, {group, test_v2},
- {group, test_v1_v2}, {group, test_v3},
- {group, test_multi_threaded}, {group, mib_storage},
- {group, tickets}]
-end.
+ [
+ app_info,
+ {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_v3},
+ {group, test_multi_threaded}, {group, mib_storage},
+ {group, tickets}
+ ].
%%%-----------------------------------------------------------------
@@ -1221,21 +1217,16 @@ init_v3(Config) when list(Config) ->
%% and we will be stuck with a bunch of mnesia tables for
%% the rest of this suite...
?DBG("start_agent -> start crypto app",[]),
- case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- case ?CRYPTO_START() of
- ok ->
- case ?CRYPTO_SUPPORT() of
- {no, Reason} ->
- ?SKIP({unsupported_encryption, Reason});
- yes ->
- ok
- end;
- {error, Reason} ->
- ?SKIP({failed_starting_crypto, Reason})
- end
+ case ?CRYPTO_START() of
+ ok ->
+ case ?CRYPTO_SUPPORT() of
+ {no, Reason} ->
+ ?SKIP({unsupported_encryption, Reason});
+ yes ->
+ ok
+ end;
+ {error, Reason} ->
+ ?SKIP({failed_starting_crypto, Reason})
end,
SaNode = ?config(snmp_sa, Config),
create_tables(SaNode),
@@ -5066,12 +5057,7 @@ run(F, A, Opts) ->
CtxEngineID = snmp_misc:get_option(context_engine_id, Opts, EngineID),
Community = snmp_misc:get_option(community, Opts, "all-rights"),
?DBG("run -> start crypto app",[]),
- Crypto = case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- ?CRYPTO_START()
- end,
+ Crypto = ?CRYPTO_START(),
?DBG("run -> Crypto: ~p",[Crypto]),
catch snmp_test_mgr:stop(), % If we had a running mgr from a failed case
StdM = filename:join(code:priv_dir(snmp), "mibs") ++ "/",
diff --git a/lib/snmp/test/exp/snmp_agent_v3_test.erl b/lib/snmp/test/exp/snmp_agent_v3_test.erl
index c72d845bf2..b0bc6384e8 100644
--- a/lib/snmp/test/exp/snmp_agent_v3_test.erl
+++ b/lib/snmp/test/exp/snmp_agent_v3_test.erl
@@ -231,17 +231,12 @@ end_per_testcase(_Case, Config) when list(Config) ->
Config.
cases() ->
-case ?OSTYPE() of
- vxworks ->
- [app_info, {group, test_v1}, {group, test_v2},
- {group, test_v1_v2}, {group, test_multi_threaded},
- {group, mib_storage}, {group, tickets}];
- _Else ->
- [app_info, {group, test_v1}, {group, test_v2},
- {group, test_v1_v2}, {group, test_v3},
- {group, test_multi_threaded}, {group, mib_storage},
- {group, tickets}]
-end.
+ [
+ app_info, {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_v3},
+ {group, test_multi_threaded}, {group, mib_storage},
+ {group, tickets}
+ ].
%%%-----------------------------------------------------------------
@@ -1221,21 +1216,16 @@ init_v3(Config) when list(Config) ->
%% and we will be stuck with a bunch of mnesia tables for
%% the rest of this suite...
?DBG("start_agent -> start crypto app",[]),
- case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- case ?CRYPTO_START() of
- ok ->
- case ?CRYPTO_SUPPORT() of
- {no, Reason} ->
- ?SKIP({unsupported_encryption, Reason});
- yes ->
- ok
- end;
- {error, Reason} ->
- ?SKIP({failed_starting_crypto, Reason})
- end
+ case ?CRYPTO_START() of
+ ok ->
+ case ?CRYPTO_SUPPORT() of
+ {no, Reason} ->
+ ?SKIP({unsupported_encryption, Reason});
+ yes ->
+ ok
+ end;
+ {error, Reason} ->
+ ?SKIP({failed_starting_crypto, Reason})
end,
SaNode = ?config(snmp_sa, Config),
create_tables(SaNode),
@@ -5066,12 +5056,7 @@ run(F, A, Opts) ->
CtxEngineID = snmp_misc:get_option(context_engine_id, Opts, EngineID),
Community = snmp_misc:get_option(community, Opts, "all-rights"),
?DBG("run -> start crypto app",[]),
- Crypto = case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- ?CRYPTO_START()
- end,
+ Crypto = ?CRYPTO_START(),
?DBG("run -> Crypto: ~p",[Crypto]),
catch snmp_test_mgr:stop(), % If we had a running mgr from a failed case
StdM = filename:join(code:priv_dir(snmp), "mibs") ++ "/",
diff --git a/lib/snmp/test/modules.mk b/lib/snmp/test/modules.mk
index a444cab6d6..3d658bf8e8 100644
--- a/lib/snmp/test/modules.mk
+++ b/lib/snmp/test/modules.mk
@@ -79,5 +79,5 @@ MIB_FILES = \
Test2.mib \
Test3.mib
-SPECS = snmp.spec snmp.spec.vxworks
+SPECS = snmp.spec
diff --git a/lib/snmp/test/snmp.spec.vxworks b/lib/snmp/test/snmp.spec.vxworks
deleted file mode 100644
index 654aa96d8c..0000000000
--- a/lib/snmp/test/snmp.spec.vxworks
+++ /dev/null
@@ -1,4 +0,0 @@
-{topcase, {dir, "../snmp_test"}}.
-{skip, {snmp_SUITE, test_v3, "Requires crypto"}}.
-
-
diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl
index 8ee5dd534d..bfe14bc080 100644
--- a/lib/snmp/test/snmp_agent_test.erl
+++ b/lib/snmp/test/snmp_agent_test.erl
@@ -346,47 +346,72 @@ end_per_group(_GroupName, Config) ->
-init_per_testcase(otp8395 = Case, Config) when is_list(Config) ->
+%% ---- Init Per TestCase ----
+
+init_per_testcase(Case, Config) when is_list(Config) ->
?DBG("init_per_testcase -> entry with"
+ "~n Config: ~p", [Config]),
+
+ p("Agent Info: "
+ "~n ~p", [snmpa:info()]),
+
+ init_per_testcase1(Case, Config).
+
+init_per_testcase1(otp8395 = Case, Config) when is_list(Config) ->
+ ?DBG("init_per_testcase1 -> entry with"
"~n Case: ~p"
"~n Config: ~p", [Case, Config]),
otp8395({init, init_per_testcase2(Case, Config)});
-init_per_testcase(otp9884 = Case, Config) when is_list(Config) ->
- ?DBG("init_per_testcase -> entry with"
+init_per_testcase1(otp9884 = Case, Config) when is_list(Config) ->
+ ?DBG("init_per_testcase1 -> entry with"
"~n Case: ~p"
"~n Config: ~p", [Case, Config]),
otp9884({init, init_per_testcase2(Case, Config)});
-init_per_testcase(otp_7157_test = _Case, Config) when is_list(Config) ->
- ?DBG("init_per_testcase -> entry with"
+init_per_testcase1(otp_7157_test = _Case, Config) when is_list(Config) ->
+ ?DBG("init_per_testcase1 -> entry with"
"~n Case: ~p"
"~n Config: ~p", [_Case, Config]),
Dog = ?WD_START(?MINS(1)),
[{watchdog, Dog} | Config ];
-init_per_testcase(v2_inform_i = _Case, Config) when is_list(Config) ->
- ?DBG("init_per_testcase -> entry with"
+init_per_testcase1(v2_inform_i = _Case, Config) when is_list(Config) ->
+ ?DBG("init_per_testcase1 -> entry with"
"~n Case: ~p"
"~n Config: ~p", [_Case, Config]),
Dog = ?WD_START(?MINS(10)),
[{watchdog, Dog} | Config ];
-init_per_testcase(v3_inform_i = _Case, Config) when is_list(Config) ->
- ?DBG("init_per_testcase -> entry with"
+init_per_testcase1(v3_inform_i = _Case, Config) when is_list(Config) ->
+ ?DBG("init_per_testcase1 -> entry with"
"~n Case: ~p"
"~n Config: ~p", [_Case, Config]),
Dog = ?WD_START(?MINS(10)),
[{watchdog, Dog} | Config ];
-init_per_testcase(_Case, Config) when is_list(Config) ->
+init_per_testcase1(_Case, Config) when is_list(Config) ->
?DBG("init_per_testcase -> entry with"
"~n Case: ~p"
"~n Config: ~p", [_Case, Config]),
Dog = ?WD_START(?MINS(6)),
[{watchdog, Dog}| Config ].
-end_per_testcase(otp8395, Config) when is_list(Config) ->
+
+%% ---- End Per TestCase ----
+
+end_per_testcase(Case, Config) when is_list(Config) ->
+ ?DBG("end_per_testcase -> entry with"
+ "~n Config: ~p", [Config]),
+
+ p("Agent Info: "
+ "~n ~p", [snmpa:info()]),
+
+ display_log(Config),
+
+ end_per_testcase1(Case, Config).
+
+end_per_testcase1(otp8395, Config) when is_list(Config) ->
otp8395({fin, Config});
-end_per_testcase(otp9884, Config) when is_list(Config) ->
+end_per_testcase1(otp9884, Config) when is_list(Config) ->
otp9884({fin, Config});
-end_per_testcase(_Case, Config) when is_list(Config) ->
- ?DBG("end_per_testcase -> entry with"
+end_per_testcase1(_Case, Config) when is_list(Config) ->
+ ?DBG("end_per_testcase1 -> entry with"
"~n Case: ~p"
"~n Config: ~p", [_Case, Config]),
Dog = ?config(watchdog, Config),
@@ -433,29 +458,16 @@ end_per_testcase2(_Case, Config) ->
cases() ->
- case ?OSTYPE() of
- vxworks ->
- [
- {group, misc},
- {group, test_v1},
- {group, test_v2},
- {group, test_v1_v2},
- {group, test_multi_threaded},
- {group, mib_storage},
- {group, tickets1}
- ];
- _Else ->
- [
- {group, misc},
- {group, test_v1},
- {group, test_v2},
- {group, test_v1_v2},
- {group, test_v3},
- {group, test_multi_threaded},
- {group, mib_storage},
- {group, tickets1}
- ]
- end.
+ [
+ {group, misc},
+ {group, test_v1},
+ {group, test_v2},
+ {group, test_v1_v2},
+ {group, test_v3},
+ {group, test_multi_threaded},
+ {group, mib_storage},
+ {group, tickets1}
+ ].
%%%-----------------------------------------------------------------
@@ -1280,21 +1292,16 @@ init_v3(Config) when is_list(Config) ->
%% and we will be stuck with a bunch of mnesia tables for
%% the rest of this suite...
?DBG("start_agent -> start crypto app",[]),
- case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- case ?CRYPTO_START() of
- ok ->
- case ?CRYPTO_SUPPORT() of
- {no, Reason} ->
- ?SKIP({unsupported_encryption, Reason});
- yes ->
- ok
- end;
- {error, Reason} ->
- ?SKIP({failed_starting_crypto, Reason})
- end
+ case ?CRYPTO_START() of
+ ok ->
+ case ?CRYPTO_SUPPORT() of
+ {no, Reason} ->
+ ?SKIP({unsupported_encryption, Reason});
+ yes ->
+ ok
+ end;
+ {error, Reason} ->
+ ?SKIP({failed_starting_crypto, Reason})
end,
SaNode = ?config(snmp_sa, Config),
create_tables(SaNode),
@@ -1406,9 +1413,6 @@ simple(Config) when is_list(Config) ->
try_test(simple_standard_test),
- p("Display log"),
- display_log(Config),
-
p("done"),
ok.
diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl
index 238832b7c1..757aebfa9b 100644
--- a/lib/snmp/test/snmp_agent_test_lib.erl
+++ b/lib/snmp/test/snmp_agent_test_lib.erl
@@ -338,12 +338,7 @@ run(Mod, Func, Args, Opts) ->
CtxEngineID = snmp_misc:get_option(context_engine_id, Opts, EngineID),
Community = snmp_misc:get_option(community, Opts, "all-rights"),
?DBG("run -> start crypto app",[]),
- Crypto = case os:type() of
- vxworks ->
- no_crypto;
- _ ->
- ?CRYPTO_START()
- end,
+ Crypto = ?CRYPTO_START(),
?DBG("run -> Crypto: ~p", [Crypto]),
catch snmp_test_mgr:stop(), % If we had a running mgr from a failed case
StdM = filename:join(code:priv_dir(snmp), "mibs") ++ "/",
@@ -729,7 +724,6 @@ expect2(Id, F) ->
get_timeout() ->
get_timeout(os:type()).
-get_timeout(vxworks) -> 7000;
get_timeout(_) -> 3500.
receive_pdu(To) ->
@@ -1540,7 +1534,6 @@ rpc(Node, F, A) ->
%% timeout() ->
%% timeout(os:type()).
%%
-%% timeout(vxworks) -> 7000;
%% timeout(_) -> 3500.
diff --git a/lib/snmp/test/snmp_pdus_test.erl b/lib/snmp/test/snmp_pdus_test.erl
index 07b6d6b657..7d649b1dad 100644
--- a/lib/snmp/test/snmp_pdus_test.erl
+++ b/lib/snmp/test/snmp_pdus_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. 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
@@ -39,6 +39,7 @@
otp7575/1,
otp8563/1,
otp9022/1,
+ otp10132/1,
init_per_testcase/2, end_per_testcase/2
]).
@@ -74,16 +75,16 @@ end_per_testcase(_Case, Config) when is_list(Config) ->
%% Test case definitions
%%======================================================================
all() ->
-[{group, tickets}].
+ [{group, tickets}].
groups() ->
- [{tickets, [], [otp7575, otp8563, otp9022]}].
+ [{tickets, [], [otp7575, otp8563, otp9022, otp10132]}].
init_per_group(_GroupName, Config) ->
- Config.
+ Config.
end_per_group(_GroupName, Config) ->
- Config.
+ Config.
@@ -94,7 +95,7 @@ end_per_group(_GroupName, Config) ->
%%======================================================================
otp7575(suite) -> [];
-otp7575(doc) -> ["OTP-7575"];
+otp7575(doc) -> ["OTP-7575 - Message version"];
otp7575(Config) when is_list(Config) ->
io:format("attempt to decode message with valid version~n", []),
MsgWithOkVersion = <<48,39,2,1,0,4,6,112,117,98,108,105,99,160,26,2,2,1,49,2,1,0,2,1,0,48,14,48,12,6,8,43,6,1,2,1,1,5,0,5,0>>,
@@ -127,48 +128,55 @@ otp7575(Config) when is_list(Config) ->
otp8563(suite) -> [];
-otp8563(doc) -> ["OTP-8563"];
+otp8563(doc) -> ["OTP-8563 - Counter64"];
otp8563(Config) when is_list(Config) ->
Val1 = 16#7fffffffffffffff,
- io:format("try encode and decode ~w~n", [Val1]),
+ io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]),
Enc1 = snmp_pdus:enc_value('Counter64', Val1),
+ io:format(" => ~w~n", [Enc1]),
{{'Counter64', Val1}, []} = snmp_pdus:dec_value(Enc1),
Val2 = Val1 + 1,
- io:format("try encode and decode ~w~n", [Val2]),
+ io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]),
Enc2 = snmp_pdus:enc_value('Counter64', Val2),
+ io:format(" => ~w~n", [Enc2]),
{{'Counter64', Val2}, []} = snmp_pdus:dec_value(Enc2),
Val3 = Val2 + 1,
- io:format("try encode and decode ~w~n", [Val3]),
+ io:format("try encode and decode valule 3: ~w (0x~.16b)~n", [Val3, Val3]),
Enc3 = snmp_pdus:enc_value('Counter64', Val3),
+ io:format(" => ~w~n", [Enc3]),
{{'Counter64', Val3}, []} = snmp_pdus:dec_value(Enc3),
Val4 = 16#fffffffffffffffe,
- io:format("try encode and decode ~w~n", [Val4]),
+ io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]),
Enc4 = snmp_pdus:enc_value('Counter64', Val4),
+ io:format(" => ~w~n", [Enc4]),
{{'Counter64', Val4}, []} = snmp_pdus:dec_value(Enc4),
Val5 = Val4 + 1,
- io:format("try encode and decode ~w~n", [Val5]),
+ io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]),
Enc5 = snmp_pdus:enc_value('Counter64', Val5),
+ io:format(" => ~w~n", [Enc5]),
{{'Counter64', Val5}, []} = snmp_pdus:dec_value(Enc5),
Val6 = 16#ffffffffffffffff + 1,
- io:format("try and fail to encode ~w~n", [Val6]),
+ io:format("try and fail to encode value 6: ~w (0x~.16b)~n", [Val6, Val6]),
case (catch snmp_pdus:enc_value('Counter64', Val6)) of
{'EXIT', {error, {bad_counter64, Val6}}} ->
ok;
Unexpected6 ->
+ io:format(" => ~w~n", [Unexpected6]),
exit({unexpected_encode_result, Unexpected6, Val6})
end,
Val7 = -1,
- io:format("try and fail to encode ~w~n", [Val7]),
+ io:format("try and fail to encode value 7: ~w~n", [Val7]),
case (catch snmp_pdus:enc_value('Counter64', Val7)) of
{'EXIT', {error, {bad_counter64, Val7}}} ->
ok;
Unexpected7 ->
+ io:format(" => ~w~n", [Unexpected7]),
exit({unexpected_encode_result, Unexpected7, Val7})
end,
@@ -176,51 +184,151 @@ otp8563(Config) when is_list(Config) ->
otp9022(suite) -> [];
-otp9022(doc) -> ["OTP-9022"];
+otp9022(doc) -> ["OTP-9022 - Counter32"];
otp9022(Config) when is_list(Config) ->
- Val1 = 16#7fffffff,
- io:format("try encode and decode ~w~n", [Val1]),
+ Val0 = 2908389204,
+ io:format("try encode and decode value 0: ~w (0x~.16b)~n", [Val0, Val0]),
+ Enc0 = snmp_pdus:enc_value('Counter32', Val0),
+ io:format(" => ~w~n", [Enc0]),
+ {{'Counter32', Val0}, []} = snmp_pdus:dec_value(Enc0),
+
+ Val1 = 0,
+ io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]),
Enc1 = snmp_pdus:enc_value('Counter32', Val1),
+ io:format(" => ~w~n", [Enc1]),
{{'Counter32', Val1}, []} = snmp_pdus:dec_value(Enc1),
Val2 = Val1 + 1,
- io:format("try encode and decode ~w~n", [Val2]),
+ io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]),
Enc2 = snmp_pdus:enc_value('Counter32', Val2),
+ io:format(" => ~w~n", [Enc2]),
{{'Counter32', Val2}, []} = snmp_pdus:dec_value(Enc2),
- Val3 = Val2 + 1,
- io:format("try encode and decode ~w~n", [Val3]),
+ Val3 = 16#7ffffffe,
+ io:format("try encode and decode value 3: ~w (0x~.16b)~n", [Val3, Val3]),
Enc3 = snmp_pdus:enc_value('Counter32', Val3),
+ io:format(" => ~w~n", [Enc3]),
{{'Counter32', Val3}, []} = snmp_pdus:dec_value(Enc3),
- Val4 = 16#fffffffe,
- io:format("try encode and decode ~w~n", [Val4]),
+ Val4 = Val3 + 1,
+ io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]),
Enc4 = snmp_pdus:enc_value('Counter32', Val4),
+ io:format(" => ~w~n", [Enc4]),
{{'Counter32', Val4}, []} = snmp_pdus:dec_value(Enc4),
Val5 = Val4 + 1,
- io:format("try encode and decode ~w~n", [Val5]),
+ io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]),
Enc5 = snmp_pdus:enc_value('Counter32', Val5),
+ io:format(" => ~w~n", [Enc5]),
{{'Counter32', Val5}, []} = snmp_pdus:dec_value(Enc5),
- Val6 = 16#ffffffff + 1,
- io:format("try and fail to encode ~w~n", [Val6]),
- case (catch snmp_pdus:enc_value('Counter32', Val6)) of
- {'EXIT', {error, {bad_counter32, Val6}}} ->
+ Val6 = 16#fffffffe,
+ io:format("try encode and decode value 6: ~w (0x~.16b)~n", [Val6, Val6]),
+ Enc6 = snmp_pdus:enc_value('Counter32', Val6),
+ io:format(" => ~w~n", [Enc6]),
+ {{'Counter32', Val6}, []} = snmp_pdus:dec_value(Enc6),
+
+ Val7 = Val6 + 1,
+ io:format("try encode and decode value 7: ~w (0x~.16b)~n", [Val7, Val7]),
+ Enc7 = snmp_pdus:enc_value('Counter32', Val7),
+ io:format(" => ~w~n", [Enc7]),
+ {{'Counter32', Val7}, []} = snmp_pdus:dec_value(Enc7),
+
+ Val8 = 16#ffffffff + 1,
+ io:format("try and fail to encode value 8: ~w (0x~.16b)~n", [Val8, Val8]),
+ case (catch snmp_pdus:enc_value('Counter32', Val8)) of
+ {'EXIT', {error, {bad_counter32, Val8}}} ->
ok;
- Unexpected6 ->
- exit({unexpected_encode_result, Unexpected6, Val6})
+ Unexpected8 ->
+ io:format(" => ~w~n", [Unexpected8]),
+ exit({unexpected_encode_result, Unexpected8, Val8})
end,
- Val7 = -1,
- io:format("try and fail to encode ~w~n", [Val7]),
- case (catch snmp_pdus:enc_value('Counter32', Val7)) of
- {'EXIT', {error, {bad_counter32, Val7}}} ->
+ Val9 = -1,
+ io:format("try and fail to encode value 9: ~w~n", [Val9]),
+ case (catch snmp_pdus:enc_value('Counter32', Val9)) of
+ {'EXIT', {error, {bad_counter32, Val9}}} ->
ok;
- Unexpected7 ->
- exit({unexpected_encode_result, Unexpected7, Val7})
+ Unexpected9 ->
+ io:format(" => ~w~n", [Unexpected9]),
+ exit({unexpected_encode_result, Unexpected9, Val9})
+ end,
+
+ ok.
+
+
+otp10132(suite) -> [];
+otp10132(doc) -> ["OTP-10132 - TimeTicks"];
+otp10132(Config) when is_list(Config) ->
+ Val0 = 2159001034,
+ io:format("try encode and decode value 0: ~w (0x~.16b)~n", [Val0, Val0]),
+ Enc0 = snmp_pdus:enc_value('TimeTicks', Val0),
+ io:format(" => ~w~n", [Enc0]),
+ {{'TimeTicks', Val0}, []} = snmp_pdus:dec_value(Enc0),
+
+ Val1 = 0,
+ io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]),
+ Enc1 = snmp_pdus:enc_value('TimeTicks', Val1),
+ io:format(" => ~w~n", [Enc1]),
+ {{'TimeTicks', Val1}, []} = snmp_pdus:dec_value(Enc1),
+
+ Val2 = Val1 + 1,
+ io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]),
+ Enc2 = snmp_pdus:enc_value('TimeTicks', Val2),
+ io:format(" => ~w~n", [Enc2]),
+ {{'TimeTicks', Val2}, []} = snmp_pdus:dec_value(Enc2),
+
+ Val3 = 16#7ffffffe,
+ io:format("try encode and decode value 3: ~w (0x~.16b)~n", [Val3, Val3]),
+ Enc3 = snmp_pdus:enc_value('TimeTicks', Val3),
+ io:format(" => ~w~n", [Enc3]),
+ {{'TimeTicks', Val3}, []} = snmp_pdus:dec_value(Enc3),
+
+ Val4 = Val3 + 1,
+ io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]),
+ Enc4 = snmp_pdus:enc_value('TimeTicks', Val4),
+ io:format(" => ~w~n", [Enc4]),
+ {{'TimeTicks', Val4}, []} = snmp_pdus:dec_value(Enc4),
+
+ Val5 = Val4 + 1,
+ io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]),
+ Enc5 = snmp_pdus:enc_value('TimeTicks', Val5),
+ io:format(" => ~w~n", [Enc5]),
+ {{'TimeTicks', Val5}, []} = snmp_pdus:dec_value(Enc5),
+
+ Val6 = 16#fffffffe,
+ io:format("try encode and decode value 6: ~w (0x~.16b)~n", [Val6, Val6]),
+ Enc6 = snmp_pdus:enc_value('TimeTicks', Val6),
+ io:format(" => ~w~n", [Enc6]),
+ {{'TimeTicks', Val6}, []} = snmp_pdus:dec_value(Enc6),
+
+ Val7 = Val6 + 1,
+ io:format("try encode and decode value 7: ~w (0x~.16b)~n", [Val7, Val7]),
+ Enc7 = snmp_pdus:enc_value('TimeTicks', Val7),
+ io:format(" => ~w~n", [Enc7]),
+ {{'TimeTicks', Val7}, []} = snmp_pdus:dec_value(Enc7),
+
+ Val8 = Val7 + 1,
+ io:format("try and fail to encode value 8: ~w (0x~.16b)~n", [Val8, Val8]),
+ case (catch snmp_pdus:enc_value('TimeTicks', Val8)) of
+ {'EXIT', {error, {bad_timeticks, Val8}}} ->
+ ok;
+ Unexpected8 ->
+ io:format(" => ~w~n", [Unexpected8]),
+ exit({unexpected_encode_result, Unexpected8, Val8})
end,
+ Val9 = -1,
+ io:format("try and fail to encode value 9: ~w~n", [Val9]),
+ case (catch snmp_pdus:enc_value('TimeTicks', Val9)) of
+ {'EXIT', {error, {bad_timeticks, Val9}}} ->
+ ok;
+ Unexpected9 ->
+ io:format(" => ~w~n", [Unexpected9]),
+ exit({unexpected_encode_result, Unexpected9, Val9})
+ end,
+
+ io:format("done~n", []),
ok.
diff --git a/lib/snmp/test/snmp_test_lib.erl b/lib/snmp/test/snmp_test_lib.erl
index 26115a0c74..e327456bc4 100644
--- a/lib/snmp/test/snmp_test_lib.erl
+++ b/lib/snmp/test/snmp_test_lib.erl
@@ -516,8 +516,6 @@ warning_msg(F, A) ->
timeout(T) ->
trunc(timeout(T, os:type())).
-timeout(T, vxworks) ->
- 5 * T * timetrap_scale_factor();
timeout(T, _) ->
T * timetrap_scale_factor().
diff --git a/lib/snmp/test/snmp_test_mgr.erl b/lib/snmp/test/snmp_test_mgr.erl
index 499cf7abcf..40fcbce8f1 100644
--- a/lib/snmp/test/snmp_test_mgr.erl
+++ b/lib/snmp/test/snmp_test_mgr.erl
@@ -161,7 +161,6 @@ get_timeout() ->
get_timeout(os:type())
end.
-get_timeout(vxworks) -> 7000;
get_timeout(_) -> 3500.
%%----------------------------------------------------------------------
diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk
index 36b9764bc8..b90dbe4eef 100644
--- a/lib/snmp/vsn.mk
+++ b/lib/snmp/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = snmp
-SNMP_VSN = 4.22
+SNMP_VSN = 4.22.1
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)"
diff --git a/lib/ssh/doc/src/Makefile b/lib/ssh/doc/src/Makefile
index 38782a3b00..da99c4ea0f 100644
--- a/lib/ssh/doc/src/Makefile
+++ b/lib/ssh/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2011. All Rights Reserved.
+# Copyright Ericsson AB 2004-2012. 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/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index a85cada732..36010da07a 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -29,6 +29,76 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 2.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Ssh now only sends one channel close message under all
+ circumstances, before it would sometimes incorrectly send
+ two.</p>
+ <p>
+ Own Id: OTP-10060</p>
+ </item>
+ <item>
+ <p>
+ The options check mistreated the ip_v6_disable-option,
+ and did not handle some, at the moment, undocumented
+ options correctly.</p>
+ <p>
+ Own Id: OTP-10061</p>
+ </item>
+ <item>
+ <p>
+ The channel id in a channel failure message, sent to the
+ peer, is now in all cases the remote channel id</p>
+ <p>
+ Own Id: OTP-10062</p>
+ </item>
+ <item>
+ <p>
+ Improved handling of multiple closes to avoid occasional
+ crashes when a channel is closed more than once.</p>
+ <p>
+ Own Id: OTP-10112</p>
+ </item>
+ <item>
+ <p>
+ Fix lib/src/test/ssh_basic_SUITE.erl to fix IPv6 option
+ typos</p>
+ <p>
+ Fixed incorrect option "ipv6_disable" to "ipv6_disabled"
+ as documented in the ssh manual.</p>
+ <p>
+ Own Id: OTP-10219</p>
+ </item>
+ <item>
+ <p>
+ SSH: Make "auth_methods" server option re-usable</p>
+ <p>
+ The 'auth_methods' option is used by the server side of
+ the SSH code to tell a connecting SSH client about the
+ authentication methods that are supported by the server.
+ The code still extracts and handles the 'auth_methods'
+ option from Opts in appropriate places, but the Opts
+ checking code in ssh.erl didn't allow that option to be
+ specified.</p>
+ <p>
+ Own Id: OTP-10224</p>
+ </item>
+ <item>
+ <p>
+ Use the correct channel id when adjusting the channel
+ window</p>
+ <p>
+ Own Id: OTP-10232</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 2.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index e30c6f1ccc..b84b3a3dcb 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -259,11 +259,17 @@
that identifies the host for ssh. The default is
<c><![CDATA[/etc/ssh]]></c>, note that SSH normally
requires the host files there to be readable only by
- root.</p>
+ root.</p>
+ </item>
+ <tag><c><![CDATA[{auth_methods, string()}]]></c></tag>
+ <item>
+ <p>Comma separated string that determines which authentication methodes that the server
+ should support and in what order they will be tried. Defaults to
+ <c><![CDATA["publickey,keyboard_interactive,password"]]></c></p>
</item>
- <tag><c><![CDATA[{user_passwords, [{string() = User, string() = Password}]}]]></c></tag>
- <item>
- <p>Provide passwords for password authentication.They will
+ <tag><c><![CDATA[{user_passwords, [{string() = User, string() = Password}]}]]></c></tag>
+ <item>
+ <p>Provide passwords for password authentication.They will
be used when someone tries to connect to the server and
public key user authentication fails. The option provides
a list of valid user names and the corresponding password.
diff --git a/lib/ssh/examples/Makefile b/lib/ssh/examples/Makefile
index 84c7edac8c..de019f75b5 100644
--- a/lib/ssh/examples/Makefile
+++ b/lib/ssh/examples/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2010. All Rights Reserved.
+# Copyright Ericsson AB 2005-2012. 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/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src
index 0542054596..d08dbafc32 100644
--- a/lib/ssh/src/ssh.appup.src
+++ b/lib/ssh/src/ssh.appup.src
@@ -18,12 +18,26 @@
%%
{"%VSN%",
- [
- {<<"2\\.*">>, [{restart_application, ssh}]},
- {<<"1\\.*">>, [{restart_application, ssh}]}
+ [
+ {<<"2.1">>, [{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []},
+ {load_module, ssh_connection, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_manager, soft_purge, soft_purge, []},
+ {load_module, ssh_auth, soft_purge, soft_purge, []},
+ {load_module, ssh_channel, soft_purge, soft_purge, []},
+ {load_module, ssh_file, soft_purge, soft_purge, []}]},
+ {load_module, ssh, soft_purge, soft_purge, []}]},
+ {<<"2.0\\.*">>, [{restart_application, ssh}]},
+ {<<"1\\.*">>, [{restart_application, ssh}]}
],
[
- {<<"2\\.*">>, [{restart_application, ssh}]},
- {<<"1\\.*">>, [{restart_application, ssh}]}
+ {<<"2.1">>,[{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []},
+ {load_module, ssh_connection, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_manager, soft_purge, soft_purge, []},
+ {load_module, ssh_auth, soft_purge, soft_purge, []},
+ {load_module, ssh_channel, soft_purge, soft_purge, []},
+ {load_module, ssh_file, soft_purge, soft_purge, []}]},
+ {load_module, ssh, soft_purge, soft_purge, []}]},
+ {<<"2.0\\.*">>, [{restart_application, ssh}]},
+ {<<"1\\.*">>, [{restart_application, ssh}]}
]
}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index f4a40c81a4..3395f73884 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -91,10 +91,8 @@ do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) ->
{ok, ConnectionSup} ->
{ok, Manager} =
ssh_connection_sup:connection_manager(ConnectionSup),
- MRef = erlang:monitor(process, Manager),
receive
{Manager, is_connected} ->
- do_demonitor(MRef, Manager),
{ok, Manager};
%% When the connection fails
%% ssh_connection_sup:connection_manager
@@ -102,30 +100,13 @@ do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) ->
%% could allready have terminated, so we will not
%% match the Manager in this case
{_, not_connected, {error, econnrefused}} when DisableIpv6 == false ->
- do_demonitor(MRef, Manager),
do_connect(Host, Port, proplists:delete(inet6, SocketOptions),
SshOptions, Timeout, true);
{_, not_connected, {error, Reason}} ->
- do_demonitor(MRef, Manager),
{error, Reason};
{_, not_connected, Other} ->
- do_demonitor(MRef, Manager),
- {error, Other};
- {'DOWN', MRef, _, Manager, Reason} when is_pid(Manager) ->
- error_logger:warning_report([{ssh, connect},
- {diagnose,
- "Connection was closed before properly set up."},
- {host, Host},
- {port, Port},
- {reason, Reason}]),
- receive %% Clear EXIT message from queue
- {'EXIT', Manager, _What} ->
- {error, channel_closed}
- after 0 ->
- {error, channel_closed}
- end
+ {error, Other}
after Timeout ->
- do_demonitor(MRef, Manager),
ssh_connection_manager:stop(Manager),
{error, timeout}
end
@@ -134,16 +115,6 @@ do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) ->
{error, ssh_not_started}
end.
-do_demonitor(MRef, Manager) ->
- erlang:demonitor(MRef),
- receive
- {'DOWN', MRef, _, Manager, _} ->
- ok
- after 0 ->
- ok
- end.
-
-
%%--------------------------------------------------------------------
%% Function: close(ConnectionRef) -> ok
%%
@@ -369,6 +340,8 @@ handle_option([{shell, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{exec, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{auth_methods, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions).
@@ -408,6 +381,8 @@ handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module),
is_atom(Function) ->
Opt;
+handle_ssh_option({auth_methods, Value} = Opt) when is_list(Value) ->
+ Opt;
handle_ssh_option({infofun, Value} = Opt) when is_function(Value) ->
Opt;
handle_ssh_option({connectfun, Value} = Opt) when is_function(Value) ->
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index c46f799b6d..e3b8ebfb79 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -436,32 +436,32 @@ handle_msg(#ssh_msg_channel_window_adjust{recipient_channel = ChannelId,
#connection{channel_cache = Cache} = Connection,
ConnectionPid, _) ->
- #channel{send_window_size = Size} =
+ #channel{send_window_size = Size, remote_id = RemoteId} =
Channel0 = ssh_channel:cache_lookup(Cache, ChannelId),
-
+
{SendList, Channel} = %% TODO: Datatype 0 ?
update_send_window(Channel0#channel{send_window_size = Size + Add},
- 0, <<>>, Connection),
+ 0, undefined, Connection),
Replies = lists:map(fun({Type, Data}) ->
{connection_reply, ConnectionPid,
- channel_data_msg(ChannelId, Type, Data)}
+ channel_data_msg(RemoteId, Type, Data)}
end, SendList),
FlowCtrlMsgs = flow_control(Channel, Cache),
{{replies, Replies ++ FlowCtrlMsgs}, Connection};
handle_msg(#ssh_msg_channel_open{channel_type = "session" = Type,
- sender_channel = ChannelId,
+ sender_channel = RemoteId,
initial_window_size = WindowSz,
maximum_packet_size = PacketSz}, Connection0,
ConnectionPid, server) ->
- try setup_session(Connection0, ConnectionPid, ChannelId,
+ try setup_session(Connection0, ConnectionPid, RemoteId,
Type, WindowSz, PacketSz) of
Result ->
Result
catch _:_ ->
- FailMsg = channel_open_failure_msg(ChannelId,
+ FailMsg = channel_open_failure_msg(RemoteId,
?SSH_OPEN_CONNECT_FAILED,
"Connection refused", "en"),
{{replies, [{connection_reply, ConnectionPid, FailMsg}]},
@@ -532,9 +532,9 @@ handle_msg(#ssh_msg_channel_open{channel_type = "forwarded-tcpip",
{{replies, [{connection_reply, ConnectionPid, FailMsg}]}, Connection};
-handle_msg(#ssh_msg_channel_open{sender_channel = ChannelId}, Connection,
+handle_msg(#ssh_msg_channel_open{sender_channel = RemoteId}, Connection,
ConnectionPid, _) ->
- FailMsg = channel_open_failure_msg(ChannelId,
+ FailMsg = channel_open_failure_msg(RemoteId,
?SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
"Not allowed", "en"),
{{replies, [{connection_reply, ConnectionPid, FailMsg}]}, Connection};
@@ -1073,14 +1073,15 @@ request_reply_or_data(#channel{local_id = ChannelId, user = ChannelPid},
false ->
{{channel_data, ChannelPid, Reply}, Connection}
end.
+update_send_window(Channel, _, undefined,
+ #connection{channel_cache = Cache}) ->
+ do_update_send_window(Channel, Channel#channel.send_buf, Cache);
+
+update_send_window(Channel, DataType, Data,
+ #connection{channel_cache = Cache}) ->
+ do_update_send_window(Channel, Channel#channel.send_buf ++ [{DataType, Data}], Cache).
-update_send_window(Channel0, DataType, Data,
- #connection{channel_cache = Cache}) ->
- Buf0 = if Data == <<>> ->
- Channel0#channel.send_buf;
- true ->
- Channel0#channel.send_buf ++ [{DataType, Data}]
- end,
+do_update_send_window(Channel0, Buf0, Cache) ->
{Buf1, NewSz, Buf2} = get_window(Buf0,
Channel0#channel.send_packet_size,
Channel0#channel.send_window_size),
diff --git a/lib/ssh/src/ssh_connection_sup.erl b/lib/ssh/src/ssh_connection_sup.erl
index e3544af1c6..b620056310 100644
--- a/lib/ssh/src/ssh_connection_sup.erl
+++ b/lib/ssh/src/ssh_connection_sup.erl
@@ -48,8 +48,12 @@ start_manager_child(Sup, Args) ->
supervisor:start_child(Sup, Spec).
connection_manager(SupPid) ->
- Children = supervisor:which_children(SupPid),
- {ok, ssh_connection_manager(Children)}.
+ try supervisor:which_children(SupPid) of
+ Children ->
+ {ok, ssh_connection_manager(Children)}
+ catch exit:{noproc,_} ->
+ {ok, undefined}
+ end.
%%%=========================================================================
%%% Supervisor callback
@@ -107,6 +111,8 @@ handler_spec([Role, Socket, Opts]) ->
Type = worker,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
+ssh_connection_manager([]) ->
+ undefined;
ssh_connection_manager([{_, Child, _, [ssh_connection_manager]} | _]) ->
Child;
ssh_connection_manager([_ | Rest]) ->
diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl
index d05fa8e09a..a6b82a7a13 100644
--- a/lib/ssh/src/ssh_file.erl
+++ b/lib/ssh/src/ssh_file.erl
@@ -232,7 +232,7 @@ lookup_host_key_fd(Fd, Host, KeyType) ->
eof ->
{error, not_found};
Line ->
- case public_key:ssh_decode(Line, known_hosts) of
+ case ssh_decode_line(Line, known_hosts) of
[{Key, Attributes}] ->
handle_host(Fd, Host, proplists:get_value(hostnames, Attributes), Key, KeyType);
[] ->
@@ -240,6 +240,13 @@ lookup_host_key_fd(Fd, Host, KeyType) ->
end
end.
+ssh_decode_line(Line, Type) ->
+ try
+ public_key:ssh_decode(Line, Type)
+ catch _:_ ->
+ []
+ end.
+
handle_host(Fd, Host, HostList, Key, KeyType) ->
Host1 = host_name(Host),
case lists:member(Host1, HostList) and key_match(Key, KeyType) of
@@ -285,7 +292,7 @@ lookup_user_key_fd(Fd, Key) ->
eof ->
{error, not_found};
Line ->
- case public_key:ssh_decode(Line, auth_keys) of
+ case ssh_decode_line(Line, auth_keys) of
[{AuthKey, _}] ->
case is_auth_key(Key, AuthKey) of
true ->
diff --git a/lib/ssh/src/ssh_sftpd_file_api.erl b/lib/ssh/src/ssh_sftpd_file_api.erl
index 38371f809d..83d90907f5 100644
--- a/lib/ssh/src/ssh_sftpd_file_api.erl
+++ b/lib/ssh/src/ssh_sftpd_file_api.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -22,40 +22,40 @@
-module(ssh_sftpd_file_api).
%% To be further specified later
--callback close(IoDevice::term(), State::term()) ->
- ok | {error, Reason::term()}.
--callback delete(Path::term(), State::term()) ->
- ok | {error, Reason::term()}.
--callback del_dir(Path::term(), State::term()) ->
- ok | {error, Reason::term()}.
+-callback close(file:io_device(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback delete(file:name(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback del_dir(file:name(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
-callback get_cwd(State::term()) ->
- {ok, Dir::term()} | {error, Reason::term()}.
--callback is_dir(AbsPath::term(), State::term()) ->
- boolean().
--callback list_dir(AbsPath::term(), State::term()) ->
- {ok, Filenames::term()} | {error, Reason::term()}.
+ {{ok, Dir::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback is_dir(file:name(), State::term()) ->
+ {boolean(), State::term()}.
+-callback list_dir(file:name(), State::term()) ->
+ {{ok, Filenames::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
-callback make_dir(Dir::term(), State::term()) ->
- ok | {error, Reason::term()}.
+ {{ok, State::term()},State::term()} | {{error, Reason::term()}, State::term()}.
-callback make_symlink(Path2::term(), Path::term(), State::term()) ->
- ok | {error, Reason::term()}.
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
-callback open(Path::term(), Flags::term(), State::term()) ->
- {ok, IoDevice::term()} | {error, Reason::term()}.
--callback position(IoDevice::term(), Offs::term(), State::term()) ->
- {ok, NewPosition::term()} | {error, Reason::term()}.
--callback read(IoDevice::term(), Len::term(), State::term()) ->
- {ok, Data::term()} | eof | {error, Reason::term()}.
--callback read_link(Path::term(), State::term()) ->
- {ok, FileName::term()} | {error, Reason::term()}.
--callback read_link_info(Path::term(), State::term()) ->
- {ok, FileInfo::term()} | {error, Reason::term()}.
--callback read_file_info(Path::term(), State::term()) ->
- {ok, FileInfo::term()} | {error, Reason::term()}.
--callback rename(Path::term(), Path2::term(), State::term()) ->
- ok | {error, Reason::term()}.
--callback write(IoDevice::term(), Data::term(), State::term()) ->
- ok | {error, Reason::term()}.
--callback write_file_info(Path::term(),Info::term(), State::term()) ->
- ok | {error, Reason::term()}.
+ {{ok, IoDevice::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback position(file:io_device(), Offs::term(), State::term()) ->
+ {{ok, NewPosition::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback read(file:io_device(), Len::term(), State::term()) ->
+ {{ok, Data::term()},State::term()} | {eof, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback read_link(file:name(), State::term()) ->
+ {{ok, FileName::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback read_link_info(file:name(), State::term()) ->
+ {{ok, FileInfo::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback read_file_info(file:name(), State::term()) ->
+ {{ok, FileInfo::term()}, State::term()} | {{error, Reason::term()},State::term()}.
+-callback rename(file:name(), file:name(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback write(file:io_device(), Data::term(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback write_file_info(file:name(),Info::term(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
diff --git a/lib/ssh/test/ssh.spec.vxworks b/lib/ssh/test/ssh.spec.vxworks
deleted file mode 100644
index 81f665283c..0000000000
--- a/lib/ssh/test/ssh.spec.vxworks
+++ /dev/null
@@ -1,3 +0,0 @@
-{topcase, {dir, "../ssh_test"}}.
-{require_nodenames, 1}.
-%{skip, {M, F, "Not yet implemented"}}.
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index c5019425cd..c224e5b800 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -110,12 +110,13 @@ all() ->
{group, rsa_pass_key},
{group, internal_error},
daemon_already_started,
- server_password_option, server_userpassword_option,
+ server_password_option,
+ server_userpassword_option,
close].
groups() ->
- [{dsa_key, [], [exec, exec_compressed, shell, known_hosts]},
- {rsa_key, [], [exec, exec_compressed, shell, known_hosts]},
+ [{dsa_key, [], [send, exec, exec_compressed, shell, known_hosts]},
+ {rsa_key, [], [send, exec, exec_compressed, shell, known_hosts]},
{dsa_pass_key, [], [pass_phrase]},
{rsa_pass_key, [], [pass_phrase]},
{internal_error, [], [internal_error]}
@@ -194,10 +195,10 @@ misc_ssh_options(Config) when is_list(Config) ->
SystemDir = filename:join(?config(priv_dir, Config), system),
UserDir = ?config(priv_dir, Config),
- CMiscOpt0 = [{connecect_timeout, 1000}, {ip_v6_disable, false}, {user_dir, UserDir}],
- CMiscOpt1 = [{connecect_timeout, infinity}, {ip_v6_disable, true}, {user_dir, UserDir}],
- SMiscOpt0 = [{ip_v6_disable, false}, {user_dir, UserDir}, {system_dir, SystemDir}],
- SMiscOpt1 = [{ip_v6_disable, true}, {user_dir, UserDir}, {system_dir, SystemDir}],
+ CMiscOpt0 = [{connecect_timeout, 1000}, {ip_v6_disabled, false}, {user_dir, UserDir}],
+ CMiscOpt1 = [{connecect_timeout, infinity}, {ip_v6_disabled, true}, {user_dir, UserDir}],
+ SMiscOpt0 = [{ip_v6_disabled, false}, {user_dir, UserDir}, {system_dir, SystemDir}],
+ SMiscOpt1 = [{ip_v6_disabled, true}, {user_dir, UserDir}, {system_dir, SystemDir}],
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -532,6 +533,31 @@ internal_error(Config) when is_list(Config) ->
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
+send(doc) ->
+ ["Test ssh_connection:send/3"];
+
+send(suite) ->
+ [];
+
+send(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ SystemDir = filename:join(?config(priv_dir, Config), system),
+ UserDir = ?config(priv_dir, Config),
+
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ ConnectionRef =
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user_dir, UserDir},
+ {user_interaction, false}]),
+ {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:send(ConnectionRef, ChannelId, <<"Data">>),
+ ok = ssh_connection:send(ConnectionRef, ChannelId, << >>),
+ ssh:stop_daemon(Pid).
+
+
+%%--------------------------------------------------------------------
close(doc) ->
["Simulate that we try to close an already closed connection"];
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index bff73a1b40..defa47f824 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 2.1
+SSH_VSN = 2.1.1
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile
index 8736913c72..fb12499ef7 100644
--- a/lib/ssl/doc/src/Makefile
+++ b/lib/ssl/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index a4da939d3e..6c01954010 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1999</year><year>2011</year>
+ <year>1999</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,7 +30,70 @@
</header>
<p>This document describes the changes made to the SSL application.</p>
- <section><title>SSL 5.0.1</title>
+ <section><title>SSL 5.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Sometimes the client process could receive an extra
+ {error, closed} message after ssl:recv had returned
+ {error, closed}.</p>
+ <p>
+ Own Id: OTP-10118</p>
+ </item>
+ <item>
+ <p>
+ ssl v3 alert number 41 (no_certificate_RESERVED) is now
+ recognized</p>
+ <p>
+ Own Id: OTP-10196</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Experimental support for TLS 1.1 is now available, will
+ be officially supported from OTP-R16. Thanks to Andreas
+ Schultz for implementing the first version.</p>
+ <p>
+ Own Id: OTP-8871</p>
+ </item>
+ <item>
+ <p>
+ Experimental support for TLS 1.2 is now available, will
+ be officially supported from OTP-R16. Thanks to Andreas
+ Schultz for implementing the first version.</p>
+ <p>
+ Own Id: OTP-8872</p>
+ </item>
+ <item>
+ <p>
+ Removed some bottlenecks increasing the applications
+ parallelism especially for the client side.</p>
+ <p>
+ Own Id: OTP-10113</p>
+ </item>
+ <item>
+ <p>
+ Workaround for handling certificates that wrongly encode
+ X509countryname in utf-8 when the actual value is a valid
+ ASCCI value of length 2. Such certificates are accepted
+ by many browsers such as Chrome and Fierfox so for
+ interoperability reasons we will too.</p>
+ <p>
+ Own Id: OTP-10222</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 5.0.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 28bf82b406..f0eac76264 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -36,12 +36,16 @@
<list type="bulleted">
<item>ssl requires the crypto and public_key applications.</item>
- <item>Supported SSL/TLS-versions are SSL-3.0 and TLS-1.0 </item>
+ <item>Supported SSL/TLS-versions are SSL-3.0 and TLS-1.0, experimental
+ support for TLS-1.1 and TLS-1.2 is also available (no support for elliptic curve cipher suites yet).</item>
<item>For security reasons sslv2 is not supported.</item>
<item>Ephemeral Diffie-Hellman cipher suites are supported
but not Diffie Hellman Certificates cipher suites.</item>
<item>Export cipher suites are not supported as the
U.S. lifted its export restrictions in early 2000.</item>
+ <item>IDEA cipher suites are not supported as they have
+ become deprecated by the latest TLS spec so there is not any
+ real motivation to implement them.</item>
<item>CRL and policy certificate
extensions are not supported yet. </item>
</list>
@@ -75,7 +79,9 @@
{keyfile, path()} | {password, string()} |
{cacerts, [der_encoded()]} | {cacertfile, path()} |
|{dh, der_encoded()} | {dhfile, path()} | {ciphers, ciphers()} |
- {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()}
+ {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()}
+ {next_protocols_advertised, list(binary()} |
+ {client_preferred_next_protocols, binary(), client | server, list(binary())}
</c></p>
<p><c>transportoption() = {CallbackModule, DataTag, ClosedTag}
@@ -106,7 +112,7 @@
<p><c>sslsocket() - opaque to the user. </c></p>
- <p><c>protocol() = sslv3 | tlsv1 </c></p>
+ <p><c>protocol() = sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2' </c></p>
<p><c>ciphers() = [ciphersuite()] | string() (according to old API)</c></p>
@@ -297,8 +303,29 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
when possible.
</item>
+ <tag>{client_preferred_next_protocols, Precedence:: server | client, ClientPrefs::[binary()]}
+ {client_preferred_next_protocols, Precedence:: server | client, ClientPrefs::[binary()] , Default :: binary()}}</tag>
+
+ <item> <p>Indicates the client will try to perform Next Protocol
+ Negotiation.</p>
+
+ <p>If precedence is server the negaotiated protocol will be the
+ first protocol that appears on the server advertised list that is
+ also on the clients preference list.</p>
+
+ <p>If the precedence is client the negaotiated protocol will be the
+ first protocol that appears on the clients preference list that is
+ also on the server advertised list.</p>
+
+ <p> If the client does not support any of the servers advertised
+ protocols or the server does not advertise any protocols the
+ client will fallback to the first protocol in its list or if a
+ default is supplied it will fallback to that instead. If the
+ server does not support next protocol renegotiation the
+ connection will be aborted if no default protocol is supplied.</p>
+ </item>
</taglist>
- </section>
+ </section>
<section>
<title>SSL OPTION DESCRIPTIONS - SERVER SIDE</title>
@@ -349,6 +376,14 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
SuggestedSessionId is a binary(), PeerCert is a DER encoded
certificate, Compression is an enumeration integer
and CipherSuite is of type ciphersuite().
+ </item>
+
+ <tag>{next_protocols_advertised, Protocols :: list(binary())}</tag>
+ <item>The list of protocols to send to the client if the client indicates
+ it supports the Next Protocol extension. The client may select a protocol
+ that is not on this list. The list of protocols must not contain an empty
+ binary. If the server negotiates a Next Protocol it can be accessed
+ using <c>negotiated_next_protocol/1</c> method.
</item>
</taglist>
@@ -762,8 +797,23 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
ssl application.</p>
</desc>
</func>
+ <func>
+ <name>negotiated_next_protocol(Socket) -> {ok, Protocol} | {error, next_protocol_not_negotiated}</name>
+ <fsummary>Returns the Next Protocol negotiated.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>Protocol = binary()</v>
+ </type>
+ <desc>
+ <p>
+ Returns the Next Protocol negotiated.
+ </p>
+ </desc>
+ </func>
+
+
</funcs>
-
+
<section>
<title>SEE ALSO</title>
<p><seealso marker="kernel:inet">inet(3) </seealso> and
diff --git a/lib/ssl/examples/certs/Makefile b/lib/ssl/examples/certs/Makefile
index 549f11d709..be2546fe82 100644
--- a/lib/ssl/examples/certs/Makefile
+++ b/lib/ssl/examples/certs/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2010. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. 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/lib/ssl/examples/src/Makefile b/lib/ssl/examples/src/Makefile
index 420634c97e..3e033c08d9 100644
--- a/lib/ssl/examples/src/Makefile
+++ b/lib/ssl/examples/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. 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/lib/ssl/examples/src/client_server.erl b/lib/ssl/examples/src/client_server.erl
index baf5a9185e..133a1764bc 100644
--- a/lib/ssl/examples/src/client_server.erl
+++ b/lib/ssl/examples/src/client_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. 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
@@ -21,18 +21,14 @@
-module(client_server).
--export([start/0, start/1, init_connect/1]).
+-export([start/0, init_connect/1]).
start() ->
- start([ssl, subject]).
-
-start(CertOpts) ->
%% Start ssl application
+ application:start(crypto),
+ application:start(public_key),
application:start(ssl),
- %% Always seed
- ssl:seed("ellynatefttidppohjeh"),
-
%% Let the current process be the server that listens and accepts
%% Listen
{ok, LSock} = ssl:listen(0, mk_opts(listen)),
@@ -40,14 +36,14 @@ start(CertOpts) ->
io:fwrite("Listen: port = ~w.~n", [LPort]),
%% Spawn the client process that connects to the server
- spawn(?MODULE, init_connect, [{LPort, CertOpts}]),
+ spawn(?MODULE, init_connect, [LPort]),
%% Accept
{ok, ASock} = ssl:transport_accept(LSock),
ok = ssl:ssl_accept(ASock),
io:fwrite("Accept: accepted.~n"),
- {ok, Cert} = ssl:peercert(ASock, CertOpts),
- io:fwrite("Accept: peer cert:~n~p~n", [Cert]),
+ {ok, Cert} = ssl:peercert(ASock),
+ io:fwrite("Accept: peer cert:~n~p~n", [public_key:pkix_decode_cert(Cert, otp)]),
io:fwrite("Accept: sending \"hello\".~n"),
ssl:send(ASock, "hello"),
{error, closed} = ssl:recv(ASock, 0),
@@ -59,12 +55,12 @@ start(CertOpts) ->
%% Client connect
-init_connect({LPort, CertOpts}) ->
+init_connect(LPort) ->
{ok, Host} = inet:gethostname(),
{ok, CSock} = ssl:connect(Host, LPort, mk_opts(connect)),
io:fwrite("Connect: connected.~n"),
- {ok, Cert} = ssl:peercert(CSock, CertOpts),
- io:fwrite("Connect: peer cert:~n~p~n", [Cert]),
+ {ok, Cert} = ssl:peercert(CSock),
+ io:fwrite("Connect: peer cert:~n~p~n", [public_key:pkix_decode_cert(Cert, otp)]),
{ok, Data} = ssl:recv(CSock, 0),
io:fwrite("Connect: got data: ~p~n", [Data]),
io:fwrite("Connect: closing and terminating.~n"),
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
index a08444b8dd..c5c5bf593a 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index e346b1e9e6..76550fa04b 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,11 +1,13 @@
%% -*- erlang -*-
{"%VSN%",
[
+ {"5.0.1", [{restart_application, ssl}]},
{"5.0", [{restart_application, ssl}]},
{<<"4\\.*">>, [{restart_application, ssl}]},
{<<"3\\.*">>, [{restart_application, ssl}]}
],
[
+ {"5.0.1", [{restart_application, ssl}]},
{"5.0", [{restart_application, ssl}]},
{<<"4\\.*">>, [{restart_application, ssl}]},
{<<"3\\.*">>, [{restart_application, ssl}]}
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 5e3ced144a..9a562aa5a8 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -25,18 +25,21 @@
-export([start/0, start/1, stop/0, transport_accept/1,
transport_accept/2, ssl_accept/1, ssl_accept/2, ssl_accept/3,
- cipher_suites/0, cipher_suites/1, close/1, shutdown/2,
+ cipher_suites/0, cipher_suites/1, suite_definition/1,
+ close/1, shutdown/2,
connect/3, connect/2, connect/4, connection_info/1,
controlling_process/2, listen/2, pid/1, peername/1, peercert/1,
recv/2, recv/3, send/2, getopts/2, setopts/2, sockname/1,
versions/0, session_info/1, format_error/1,
- renegotiate/1, prf/5, clear_pem_cache/0]).
+ renegotiate/1, prf/5, clear_pem_cache/0, random_bytes/1, negotiated_next_protocol/1]).
+
-deprecated({pid, 1, next_major_release}).
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
-include("ssl_cipher.hrl").
+-include("ssl_handshake.hrl").
-include_lib("public_key/include/public_key.hrl").
@@ -64,7 +67,9 @@
{keyfile, path()} | {password, string()} | {cacerts, [Der::binary()]} |
{cacertfile, path()} | {dh, Der::binary()} | {dhfile, path()} |
{ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} |
- {reuse_session, fun()} | {hibernate_after, integer()|undefined}.
+ {reuse_session, fun()} | {hibernate_after, integer()|undefined} |
+ {next_protocols_advertised, list(binary())} |
+ {client_preferred_next_protocols, binary(), client | server, list(binary())}.
-type verify_type() :: verify_none | verify_peer.
-type path() :: string().
@@ -304,6 +309,23 @@ peercert(#sslsocket{pid = Pid}) ->
end.
%%--------------------------------------------------------------------
+-spec suite_definition(cipher_suite()) -> erl_cipher_suite().
+%%
+%% Description: Return erlang cipher suite definition.
+%%--------------------------------------------------------------------
+suite_definition(S) ->
+ {KeyExchange, Cipher, Hash, _} = ssl_cipher:suite_definition(S),
+ {KeyExchange, Cipher, Hash}.
+
+%%--------------------------------------------------------------------
+-spec negotiated_next_protocol(#sslsocket{}) -> {ok, binary()} | {error, reason()}.
+%%
+%% Description: Returns the next protocol that has been negotiated. If no
+%% protocol has been negotiated will return {error, next_protocol_not_negotiated}
+%%--------------------------------------------------------------------
+negotiated_next_protocol(#sslsocket{fd = new_ssl, pid = Pid}) ->
+ ssl_connection:negotiated_next_protocol(Pid).
+
-spec cipher_suites() -> [erl_cipher_suite()].
-spec cipher_suites(erlang | openssl) -> [erl_cipher_suite()] | [string()].
@@ -314,7 +336,7 @@ cipher_suites() ->
cipher_suites(erlang) ->
Version = ssl_record:highest_protocol_version([]),
- [ssl_cipher:suite_definition(S) || S <- ssl_cipher:suites(Version)];
+ [suite_definition(S) || S <- ssl_cipher:suites(Version)];
cipher_suites(openssl) ->
Version = ssl_record:highest_protocol_version([]),
@@ -408,7 +430,7 @@ session_info(#sslsocket{pid = Pid, fd = new_ssl}) ->
versions() ->
Vsns = ssl_record:supported_protocol_versions(),
SupportedVsns = [ssl_record:protocol_version(Vsn) || Vsn <- Vsns],
- AvailableVsns = ?DEFAULT_SUPPORTED_VERSIONS,
+ AvailableVsns = ?ALL_SUPPORTED_VERSIONS,
[{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}].
@@ -474,6 +496,23 @@ format_error(Error) ->
Other
end.
+%%--------------------------------------------------------------------
+-spec random_bytes(integer()) -> binary().
+
+%%
+%% Description: Generates cryptographically secure random sequence if possible
+%% fallbacks on pseudo random function
+%%--------------------------------------------------------------------
+random_bytes(N) ->
+ try crypto:strong_rand_bytes(N) of
+ RandBytes ->
+ RandBytes
+ catch
+ error:low_entropy ->
+ crypto:rand_bytes(N)
+ end.
+
+
%%%--------------------------------------------------------------
%%% Internal functions
%%%--------------------------------------------------------------------
@@ -567,7 +606,9 @@ handle_options(Opts0, _Role) ->
renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT),
debug = handle_option(debug, Opts, []),
hibernate_after = handle_option(hibernate_after, Opts, undefined),
- erl_dist = handle_option(erl_dist, Opts, false)
+ erl_dist = handle_option(erl_dist, Opts, false),
+ next_protocols_advertised = handle_option(next_protocols_advertised, Opts, undefined),
+ next_protocol_selector = make_next_protocol_selector(handle_option(client_preferred_next_protocols, Opts, undefined))
},
CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}),
@@ -576,7 +617,8 @@ handle_options(Opts0, _Role) ->
depth, cert, certfile, key, keyfile,
password, cacerts, cacertfile, dh, dhfile, ciphers,
debug, reuse_session, reuse_sessions, ssl_imp,
- cb_info, renegotiate_at, secure_renegotiate, hibernate_after, erl_dist],
+ cb_info, renegotiate_at, secure_renegotiate, hibernate_after, erl_dist, next_protocols_advertised,
+ client_preferred_next_protocols],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
@@ -701,15 +743,68 @@ validate_option(hibernate_after, undefined) ->
undefined;
validate_option(hibernate_after, Value) when is_integer(Value), Value >= 0 ->
Value;
-validate_option(erl_dist,Value) when Value == true;
+validate_option(erl_dist,Value) when Value == true;
Value == false ->
Value;
+validate_option(client_preferred_next_protocols = Opt, {Precedence, PreferredProtocols} = Value)
+ when is_list(PreferredProtocols) ->
+ case ssl_record:highest_protocol_version([]) of
+ {3,0} ->
+ throw({error, {eoptions, {not_supported_in_sslv3, {Opt, Value}}}});
+ _ ->
+ validate_binary_list(client_preferred_next_protocols, PreferredProtocols),
+ validate_npn_ordering(Precedence),
+ {Precedence, PreferredProtocols, ?NO_PROTOCOL}
+ end;
+validate_option(client_preferred_next_protocols = Opt, {Precedence, PreferredProtocols, Default} = Value)
+ when is_list(PreferredProtocols), is_binary(Default),
+ byte_size(Default) > 0, byte_size(Default) < 256 ->
+ case ssl_record:highest_protocol_version([]) of
+ {3,0} ->
+ throw({error, {eoptions, {not_supported_in_sslv3, {Opt, Value}}}});
+ _ ->
+ validate_binary_list(client_preferred_next_protocols, PreferredProtocols),
+ validate_npn_ordering(Precedence),
+ Value
+ end;
+
+validate_option(client_preferred_next_protocols, undefined) ->
+ undefined;
+validate_option(next_protocols_advertised = Opt, Value) when is_list(Value) ->
+ case ssl_record:highest_protocol_version([]) of
+ {3,0} ->
+ throw({error, {eoptions, {not_supported_in_sslv3, {Opt, Value}}}});
+ _ ->
+ validate_binary_list(next_protocols_advertised, Value),
+ Value
+ end;
+
+validate_option(next_protocols_advertised, undefined) ->
+ undefined;
validate_option(Opt, Value) ->
throw({error, {eoptions, {Opt, Value}}}).
-
+
+validate_npn_ordering(client) ->
+ ok;
+validate_npn_ordering(server) ->
+ ok;
+validate_npn_ordering(Value) ->
+ throw({error, {eoptions, {client_preferred_next_protocols, {invalid_precedence, Value}}}}).
+
+validate_binary_list(Opt, List) ->
+ lists:foreach(
+ fun(Bin) when is_binary(Bin),
+ byte_size(Bin) > 0,
+ byte_size(Bin) < 256 ->
+ ok;
+ (Bin) ->
+ throw({error, {eoptions, {Opt, {invalid_protocol, Bin}}}})
+ end, List).
+
validate_versions([], Versions) ->
Versions;
-validate_versions([Version | Rest], Versions) when Version == 'tlsv1.1';
+validate_versions([Version | Rest], Versions) when Version == 'tlsv1.2';
+ Version == 'tlsv1.1';
Version == tlsv1;
Version == sslv3 ->
validate_versions(Rest, Versions);
@@ -811,6 +906,34 @@ cipher_suites(Version, Ciphers0) ->
no_format(Error) ->
lists:flatten(io_lib:format("No format string for error: \"~p\" available.", [Error])).
+
+detect(_Pred, []) ->
+ undefined;
+detect(Pred, [H|T]) ->
+ case Pred(H) of
+ true ->
+ H;
+ _ ->
+ detect(Pred, T)
+ end.
+
+make_next_protocol_selector(undefined) ->
+ undefined;
+make_next_protocol_selector({client, AllProtocols, DefaultProtocol}) ->
+ fun(AdvertisedProtocols) ->
+ case detect(fun(PreferredProtocol) -> lists:member(PreferredProtocol, AdvertisedProtocols) end, AllProtocols) of
+ undefined -> DefaultProtocol;
+ PreferredProtocol -> PreferredProtocol
+ end
+ end;
+
+make_next_protocol_selector({server, AllProtocols, DefaultProtocol}) ->
+ fun(AdvertisedProtocols) ->
+ case detect(fun(PreferredProtocol) -> lists:member(PreferredProtocol, AllProtocols) end, AdvertisedProtocols) of
+ undefined -> DefaultProtocol;
+ PreferredProtocol -> PreferredProtocol
+ end
+ end.
%% Only used to remove exit messages from old ssl
%% First is a nonsense clause to provide some
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index eb1228afa4..222b3f1ad7 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -84,6 +84,8 @@ description_txt(?DECOMPRESSION_FAILURE) ->
"decompression failure";
description_txt(?HANDSHAKE_FAILURE) ->
"handshake failure";
+description_txt(?NO_CERTIFICATE_RESERVED) ->
+ "No certificate reserved";
description_txt(?BAD_CERTIFICATE) ->
"bad certificate";
description_txt(?UNSUPPORTED_CERTIFICATE) ->
diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl
index 6470b82d50..92548edab7 100644
--- a/lib/ssl/src/ssl_alert.hrl
+++ b/lib/ssl/src/ssl_alert.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -43,6 +43,7 @@
%% record_overflow(22),
%% decompression_failure(30),
%% handshake_failure(40),
+%% no_certificate_RESERVED(41), %% Only sslv3
%% bad_certificate(42),
%% unsupported_certificate(43),
%% certificate_revoked(44),
@@ -69,6 +70,7 @@
-define(RECORD_OVERFLOW, 22).
-define(DECOMPRESSION_FAILURE, 30).
-define(HANDSHAKE_FAILURE, 40).
+-define(NO_CERTIFICATE_RESERVED, 41).
-define(BAD_CERTIFICATE, 42).
-define(UNSUPPORTED_CERTIFICATE, 43).
-define(CERTIFICATE_REVOKED, 44).
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index 0931b86782..86f5617b54 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -103,7 +103,7 @@ certificate_chain(OwnCert, CertDbHandle, CertsDbRef) ->
ErlCert = public_key:pkix_decode_cert(OwnCert, otp),
certificate_chain(ErlCert, OwnCert, CertDbHandle, CertsDbRef, [OwnCert]).
%%--------------------------------------------------------------------
--spec file_to_certificats(string(), term()) -> [der_cert()].
+-spec file_to_certificats(binary(), term()) -> [der_cert()].
%%
%% Description: Return list of DER encoded certificates.
%%--------------------------------------------------------------------
@@ -172,7 +172,12 @@ extensions_list(Extensions) ->
%% Description:
%%--------------------------------------------------------------------
signature_type(RSA) when RSA == ?sha1WithRSAEncryption;
- RSA == ?md5WithRSAEncryption ->
+ RSA == ?md5WithRSAEncryption;
+ RSA == ?sha224WithRSAEncryption;
+ RSA == ?sha256WithRSAEncryption;
+ RSA == ?sha384WithRSAEncryption;
+ RSA == ?sha512WithRSAEncryption
+ ->
rsa;
signature_type(?'id-dsa-with-sha1') ->
dsa.
diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl
index 01ddf056c9..67d00f0da7 100644
--- a/lib/ssl/src/ssl_certificate_db.erl
+++ b/lib/ssl/src/ssl_certificate_db.erl
@@ -106,7 +106,7 @@ add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache] = Db) ->
{ok, Ref};
[Content] ->
Ref = make_ref(),
- insert(Ref, [], 1, RefDb),
+ update_counter(Ref, 1, RefDb),
insert(MD5, {Content, Ref}, PemChache),
add_certs_from_pem(Content, Ref, CertsDb),
{ok, Ref};
@@ -114,8 +114,8 @@ add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache] = Db) ->
new_trusted_cert_entry({MD5, File}, Db)
end.
%%--------------------------------------------------------------------
--spec cache_pem_file(string(), [db_handle()]) -> term().
--spec cache_pem_file(reference(), string(), [db_handle()]) -> term().
+-spec cache_pem_file({binary(), binary()}, [db_handle()]) -> term().
+-spec cache_pem_file(reference(), {binary(), binary()}, [db_handle()]) -> term().
%%
%% Description: Cache file as binary in DB
%%--------------------------------------------------------------------
@@ -204,10 +204,8 @@ insert(Key, Data, Db) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-insert(Key, [], Count, Db) ->
- true = ets:insert(Db, {Key, Count});
-insert(Key, Data, Count, Db) ->
- true = ets:insert(Db, {Key, Count, Data}).
+update_counter(Key, Count, Db) ->
+ true = ets:insert(Db, {Key, Count}).
remove_certs(Ref, CertsDb) ->
ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}).
@@ -236,7 +234,7 @@ add_certs(Cert, Ref, CertsDb) ->
new_trusted_cert_entry(FileRef, [CertsDb, RefDb, _] = Db) ->
Ref = make_ref(),
- insert(Ref, [], 1, RefDb),
+ update_counter(Ref, 1, RefDb),
{ok, Content} = cache_pem_file(Ref, FileRef, Db),
add_certs_from_pem(Content, Ref, CertsDb),
{ok, Ref}.
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index d43d312be8..567690a413 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -28,25 +28,27 @@
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
-include("ssl_cipher.hrl").
+-include("ssl_handshake.hrl").
-include("ssl_alert.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([security_parameters/2, suite_definition/1,
- decipher/5, cipher/4,
+-export([security_parameters/3, suite_definition/1,
+ decipher/5, cipher/5,
suite/1, suites/1, anonymous_suites/0,
- openssl_suite/1, openssl_suite_name/1, filter/2]).
+ openssl_suite/1, openssl_suite_name/1, filter/2,
+ hash_algorithm/1, sign_algorithm/1]).
-compile(inline).
%%--------------------------------------------------------------------
--spec security_parameters(cipher_suite(), #security_parameters{}) ->
+-spec security_parameters(tls_version(), cipher_suite(), #security_parameters{}) ->
#security_parameters{}.
%%
%% Description: Returns a security parameters record where the
%% cipher values has been updated according to <CipherSuite>
%%-------------------------------------------------------------------
-security_parameters(CipherSuite, SecParams) ->
- { _, Cipher, Hash} = suite_definition(CipherSuite),
+security_parameters(Version, CipherSuite, SecParams) ->
+ { _, Cipher, Hash, PrfHashAlg} = suite_definition(CipherSuite),
SecParams#security_parameters{
cipher_suite = CipherSuite,
bulk_cipher_algorithm = bulk_cipher_algorithm(Cipher),
@@ -55,20 +57,21 @@ security_parameters(CipherSuite, SecParams) ->
expanded_key_material_length = expanded_key_material(Cipher),
key_material_length = key_material(Cipher),
iv_size = iv_size(Cipher),
- mac_algorithm = mac_algorithm(Hash),
+ mac_algorithm = hash_algorithm(Hash),
+ prf_algorithm = prf_algorithm(PrfHashAlg, Version),
hash_size = hash_size(Hash)}.
%%--------------------------------------------------------------------
--spec cipher(cipher_enum(), #cipher_state{}, binary(), binary()) ->
+-spec cipher(cipher_enum(), #cipher_state{}, binary(), binary(), tls_version()) ->
{binary(), #cipher_state{}}.
%%
%% Description: Encrypts the data and the MAC using chipher described
%% by cipher_enum() and updating the cipher state
%%-------------------------------------------------------------------
-cipher(?NULL, CipherState, <<>>, Fragment) ->
+cipher(?NULL, CipherState, <<>>, Fragment, _Version) ->
GenStreamCipherList = [Fragment, <<>>],
{GenStreamCipherList, CipherState};
-cipher(?RC4, CipherState, Mac, Fragment) ->
+cipher(?RC4, CipherState, Mac, Fragment, _Version) ->
State0 = case CipherState#cipher_state.state of
undefined -> crypto:rc4_set_key(CipherState#cipher_state.key);
S -> S
@@ -76,32 +79,41 @@ cipher(?RC4, CipherState, Mac, Fragment) ->
GenStreamCipherList = [Fragment, Mac],
{State1, T} = crypto:rc4_encrypt_with_state(State0, GenStreamCipherList),
{T, CipherState#cipher_state{state = State1}};
-cipher(?DES, CipherState, Mac, Fragment) ->
+cipher(?DES, CipherState, Mac, Fragment, Version) ->
block_cipher(fun(Key, IV, T) ->
crypto:des_cbc_encrypt(Key, IV, T)
- end, block_size(des_cbc), CipherState, Mac, Fragment);
-cipher(?'3DES', CipherState, Mac, Fragment) ->
+ end, block_size(des_cbc), CipherState, Mac, Fragment, Version);
+cipher(?'3DES', CipherState, Mac, Fragment, Version) ->
block_cipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) ->
crypto:des3_cbc_encrypt(K1, K2, K3, IV, T)
- end, block_size(des_cbc), CipherState, Mac, Fragment);
-cipher(?AES, CipherState, Mac, Fragment) ->
+ end, block_size(des_cbc), CipherState, Mac, Fragment, Version);
+cipher(?AES, CipherState, Mac, Fragment, Version) ->
block_cipher(fun(Key, IV, T) when byte_size(Key) =:= 16 ->
crypto:aes_cbc_128_encrypt(Key, IV, T);
(Key, IV, T) when byte_size(Key) =:= 32 ->
crypto:aes_cbc_256_encrypt(Key, IV, T)
- end, block_size(aes_128_cbc), CipherState, Mac, Fragment).
-%% cipher(?IDEA, CipherState, Mac, Fragment) ->
-%% block_cipher(fun(Key, IV, T) ->
-%% crypto:idea_cbc_encrypt(Key, IV, T)
-%% end, block_size(idea_cbc), CipherState, Mac, Fragment);
-
-block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0,
- Mac, Fragment) ->
+ end, block_size(aes_128_cbc), CipherState, Mac, Fragment, Version).
+
+build_cipher_block(BlockSz, Mac, Fragment) ->
TotSz = byte_size(Mac) + erlang:iolist_size(Fragment) + 1,
{PaddingLength, Padding} = get_padding(TotSz, BlockSz),
- L = [Fragment, Mac, PaddingLength, Padding],
+ [Fragment, Mac, PaddingLength, Padding].
+
+block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0,
+ Mac, Fragment, {3, N})
+ when N == 0; N == 1 ->
+ L = build_cipher_block(BlockSz, Mac, Fragment),
T = Fun(Key, IV, L),
NextIV = next_iv(T, IV),
+ {T, CS0#cipher_state{iv=NextIV}};
+
+block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0,
+ Mac, Fragment, {3, N})
+ when N == 2; N == 3 ->
+ NextIV = random_iv(IV),
+ L0 = build_cipher_block(BlockSz, Mac, Fragment),
+ L = [NextIV|L0],
+ T = Fun(Key, IV, L),
{T, CS0#cipher_state{iv=NextIV}}.
%%--------------------------------------------------------------------
@@ -147,19 +159,16 @@ decipher(?AES, HashSz, CipherState, Fragment, Version) ->
(Key, IV, T) when byte_size(Key) =:= 32 ->
crypto:aes_cbc_256_decrypt(Key, IV, T)
end, CipherState, HashSz, Fragment, Version).
-%% decipher(?IDEA, HashSz, CipherState, Fragment, Version) ->
-%% block_decipher(fun(Key, IV, T) ->
-%% crypto:idea_cbc_decrypt(Key, IV, T)
-%% end, CipherState, HashSz, Fragment, Version);
block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0,
HashSz, Fragment, Version) ->
try
Text = Fun(Key, IV, Fragment),
- GBC = generic_block_cipher_from_bin(Text, HashSz),
+ NextIV = next_iv(Fragment, IV),
+ GBC = generic_block_cipher_from_bin(Version, Text, NextIV, HashSz),
Content = GBC#generic_block_cipher.content,
Mac = GBC#generic_block_cipher.mac,
- CipherState1 = CipherState0#cipher_state{iv=next_iv(Fragment, IV)},
+ CipherState1 = CipherState0#cipher_state{iv=GBC#generic_block_cipher.next_iv},
case is_correct_padding(GBC, Version) of
true ->
{Content, Mac, CipherState1};
@@ -187,8 +196,8 @@ block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0,
%%--------------------------------------------------------------------
suites({3, 0}) ->
ssl_ssl3:suites();
-suites({3, N}) when N == 1; N == 2 ->
- ssl_tls1:suites().
+suites({3, N}) ->
+ ssl_tls1:suites(N).
%%--------------------------------------------------------------------
-spec anonymous_suites() -> [cipher_suite()].
@@ -201,10 +210,12 @@ anonymous_suites() ->
?TLS_DH_anon_WITH_DES_CBC_SHA,
?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
?TLS_DH_anon_WITH_AES_128_CBC_SHA,
- ?TLS_DH_anon_WITH_AES_256_CBC_SHA].
+ ?TLS_DH_anon_WITH_AES_256_CBC_SHA,
+ ?TLS_DH_anon_WITH_AES_128_CBC_SHA256,
+ ?TLS_DH_anon_WITH_AES_256_CBC_SHA256].
%%--------------------------------------------------------------------
--spec suite_definition(cipher_suite()) -> erl_cipher_suite().
+-spec suite_definition(cipher_suite()) -> int_cipher_suite().
%%
%% Description: Return erlang cipher suite definition.
%% Note: Currently not supported suites are commented away.
@@ -212,56 +223,81 @@ anonymous_suites() ->
%%-------------------------------------------------------------------
%% TLS v1.1 suites
suite_definition(?TLS_NULL_WITH_NULL_NULL) ->
- {null, null, null};
+ {null, null, null, null};
%% suite_definition(?TLS_RSA_WITH_NULL_MD5) ->
-%% {rsa, null, md5};
+%% {rsa, null, md5, default_prf};
%% suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
-%% {rsa, null, sha};
+%% {rsa, null, sha, default_prf};
suite_definition(?TLS_RSA_WITH_RC4_128_MD5) ->
- {rsa, rc4_128, md5};
-suite_definition(?TLS_RSA_WITH_RC4_128_SHA) ->
- {rsa, rc4_128, sha};
-%% suite_definition(?TLS_RSA_WITH_IDEA_CBC_SHA) ->
-%% {rsa, idea_cbc, sha};
-suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) ->
- {rsa, des_cbc, sha};
+ {rsa, rc4_128, md5, default_prf};
+suite_definition(?TLS_RSA_WITH_RC4_128_SHA) ->
+ {rsa, rc4_128, sha, default_prf};
+suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) ->
+ {rsa, des_cbc, sha, default_prf};
suite_definition(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) ->
- {rsa, '3des_ede_cbc', sha};
+ {rsa, '3des_ede_cbc', sha, default_prf};
suite_definition(?TLS_DHE_DSS_WITH_DES_CBC_SHA) ->
- {dhe_dss, des_cbc, sha};
+ {dhe_dss, des_cbc, sha, default_prf};
suite_definition(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) ->
- {dhe_dss, '3des_ede_cbc', sha};
+ {dhe_dss, '3des_ede_cbc', sha, default_prf};
suite_definition(?TLS_DHE_RSA_WITH_DES_CBC_SHA) ->
- {dhe_rsa, des_cbc, sha};
+ {dhe_rsa, des_cbc, sha, default_prf};
suite_definition(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) ->
- {dhe_rsa, '3des_ede_cbc', sha};
+ {dhe_rsa, '3des_ede_cbc', sha, default_prf};
%%% TSL V1.1 AES suites
suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA) ->
- {rsa, aes_128_cbc, sha};
+ {rsa, aes_128_cbc, sha, default_prf};
suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) ->
- {dhe_dss, aes_128_cbc, sha};
+ {dhe_dss, aes_128_cbc, sha, default_prf};
suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) ->
- {dhe_rsa, aes_128_cbc, sha};
+ {dhe_rsa, aes_128_cbc, sha, default_prf};
suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA) ->
- {rsa, aes_256_cbc, sha};
+ {rsa, aes_256_cbc, sha, default_prf};
suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) ->
- {dhe_dss, aes_256_cbc, sha};
+ {dhe_dss, aes_256_cbc, sha, default_prf};
suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
- {dhe_rsa, aes_256_cbc, sha};
+ {dhe_rsa, aes_256_cbc, sha, default_prf};
+
+%% TLS v1.2 suites
+
+%% suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
+%% {rsa, null, sha, default_prf};
+suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA256) ->
+ {rsa, aes_128_cbc, sha256, default_prf};
+suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA256) ->
+ {rsa, aes_256_cbc, sha256, default_prf};
+suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) ->
+ {dhe_dss, aes_128_cbc, sha256, default_prf};
+suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) ->
+ {dhe_rsa, aes_128_cbc, sha256, default_prf};
+suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) ->
+ {dhe_dss, aes_256_cbc, sha256, default_prf};
+suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) ->
+ {dhe_rsa, aes_256_cbc, sha256, default_prf};
+
+%% not defined YET:
+%% TLS_DH_DSS_WITH_AES_128_CBC_SHA256 DH_DSS AES_128_CBC SHA256
+%% TLS_DH_RSA_WITH_AES_128_CBC_SHA256 DH_RSA AES_128_CBC SHA256
+%% TLS_DH_DSS_WITH_AES_256_CBC_SHA256 DH_DSS AES_256_CBC SHA256
+%% TLS_DH_RSA_WITH_AES_256_CBC_SHA256 DH_RSA AES_256_CBC SHA256
%%% DH-ANON deprecated by TLS spec and not available
%%% by default, but good for testing purposes.
suite_definition(?TLS_DH_anon_WITH_RC4_128_MD5) ->
- {dh_anon, rc4_128, md5};
+ {dh_anon, rc4_128, md5, default_prf};
suite_definition(?TLS_DH_anon_WITH_DES_CBC_SHA) ->
- {dh_anon, des_cbc, sha};
+ {dh_anon, des_cbc, sha, default_prf};
suite_definition(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) ->
- {dh_anon, '3des_ede_cbc', sha};
+ {dh_anon, '3des_ede_cbc', sha, default_prf};
suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA) ->
- {dh_anon, aes_128_cbc, sha};
+ {dh_anon, aes_128_cbc, sha, default_prf};
suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA) ->
- {dh_anon, aes_256_cbc, sha}.
+ {dh_anon, aes_256_cbc, sha, default_prf};
+suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA256) ->
+ {dh_anon, aes_128_cbc, sha256, default_prf};
+suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA256) ->
+ {dh_anon, aes_256_cbc, sha256, default_prf}.
%%--------------------------------------------------------------------
-spec suite(erl_cipher_suite()) -> cipher_suite().
@@ -278,8 +314,6 @@ suite({rsa, rc4_128, md5}) ->
?TLS_RSA_WITH_RC4_128_MD5;
suite({rsa, rc4_128, sha}) ->
?TLS_RSA_WITH_RC4_128_SHA;
-%% suite({rsa, idea_cbc, sha}) ->
-%% ?TLS_RSA_WITH_IDEA_CBC_SHA;
suite({rsa, des_cbc, sha}) ->
?TLS_RSA_WITH_DES_CBC_SHA;
suite({rsa, '3des_ede_cbc', sha}) ->
@@ -315,7 +349,28 @@ suite({dhe_dss, aes_256_cbc, sha}) ->
suite({dhe_rsa, aes_256_cbc, sha}) ->
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
suite({dh_anon, aes_256_cbc, sha}) ->
- ?TLS_DH_anon_WITH_AES_256_CBC_SHA.
+ ?TLS_DH_anon_WITH_AES_256_CBC_SHA;
+
+%% TLS v1.2 suites
+
+%% suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
+%% {rsa, null, sha, sha256};
+suite({rsa, aes_128_cbc, sha256}) ->
+ ?TLS_RSA_WITH_AES_128_CBC_SHA256;
+suite({rsa, aes_256_cbc, sha256}) ->
+ ?TLS_RSA_WITH_AES_256_CBC_SHA256;
+suite({dhe_dss, aes_128_cbc, sha256}) ->
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256;
+suite({dhe_rsa, aes_128_cbc, sha256}) ->
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
+suite({dhe_dss, aes_256_cbc, sha256}) ->
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256;
+suite({dhe_rsa, aes_256_cbc, sha256}) ->
+ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
+suite({dh_anon, aes_128_cbc, sha256}) ->
+ ?TLS_DH_anon_WITH_AES_128_CBC_SHA256;
+suite({dh_anon, aes_256_cbc, sha256}) ->
+ ?TLS_DH_anon_WITH_AES_256_CBC_SHA256.
%%--------------------------------------------------------------------
-spec openssl_suite(openssl_cipher_suite()) -> cipher_suite().
@@ -323,6 +378,18 @@ suite({dh_anon, aes_256_cbc, sha}) ->
%% Description: Return TLS cipher suite definition.
%%--------------------------------------------------------------------
%% translate constants <-> openssl-strings
+openssl_suite("DHE-RSA-AES256-SHA256") ->
+ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
+openssl_suite("DHE-DSS-AES256-SHA256") ->
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256;
+openssl_suite("AES256-SHA256") ->
+ ?TLS_RSA_WITH_AES_256_CBC_SHA256;
+openssl_suite("DHE-RSA-AES128-SHA256") ->
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
+openssl_suite("DHE-DSS-AES128-SHA256") ->
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256;
+openssl_suite("AES128-SHA256") ->
+ ?TLS_RSA_WITH_AES_128_CBC_SHA256;
openssl_suite("DHE-RSA-AES256-SHA") ->
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
openssl_suite("DHE-DSS-AES256-SHA") ->
@@ -341,8 +408,6 @@ openssl_suite("DHE-DSS-AES128-SHA") ->
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA;
openssl_suite("AES128-SHA") ->
?TLS_RSA_WITH_AES_128_CBC_SHA;
-%%openssl_suite("IDEA-CBC-SHA") ->
-%% ?TLS_RSA_WITH_IDEA_CBC_SHA;
openssl_suite("RC4-SHA") ->
?TLS_RSA_WITH_RC4_128_SHA;
openssl_suite("RC4-MD5") ->
@@ -374,8 +439,6 @@ openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) ->
"DHE-DSS-AES128-SHA";
openssl_suite_name(?TLS_RSA_WITH_AES_128_CBC_SHA) ->
"AES128-SHA";
-%% openssl_suite_name(?TLS_RSA_WITH_IDEA_CBC_SHA) ->
-%% "IDEA-CBC-SHA";
openssl_suite_name(?TLS_RSA_WITH_RC4_128_SHA) ->
"RC4-SHA";
openssl_suite_name(?TLS_RSA_WITH_RC4_128_MD5) ->
@@ -384,6 +447,28 @@ openssl_suite_name(?TLS_DHE_RSA_WITH_DES_CBC_SHA) ->
"EDH-RSA-DES-CBC-SHA";
openssl_suite_name(?TLS_RSA_WITH_DES_CBC_SHA) ->
"DES-CBC-SHA";
+openssl_suite_name(?TLS_RSA_WITH_NULL_SHA256) ->
+ "NULL-SHA256";
+openssl_suite_name(?TLS_RSA_WITH_AES_128_CBC_SHA256) ->
+ "AES128-SHA256";
+openssl_suite_name(?TLS_RSA_WITH_AES_256_CBC_SHA256) ->
+ "AES256-SHA256";
+openssl_suite_name(?TLS_DH_DSS_WITH_AES_128_CBC_SHA256) ->
+ "DH-DSS-AES128-SHA256";
+openssl_suite_name(?TLS_DH_RSA_WITH_AES_128_CBC_SHA256) ->
+ "DH-RSA-AES128-SHA256";
+openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) ->
+ "DHE-DSS-AES128-SHA256";
+openssl_suite_name(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) ->
+ "DHE-RSA-AES128-SHA256";
+openssl_suite_name(?TLS_DH_DSS_WITH_AES_256_CBC_SHA256) ->
+ "DH-DSS-AES256-SHA256";
+openssl_suite_name(?TLS_DH_RSA_WITH_AES_256_CBC_SHA256) ->
+ "DH-RSA-AES256-SHA256";
+openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) ->
+ "DHE-DSS-AES256-SHA256";
+openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) ->
+ "DHE-RSA-AES256-SHA256";
%% No oppenssl name
openssl_suite_name(Cipher) ->
suite_definition(Cipher).
@@ -411,9 +496,6 @@ filter(DerCert, Ciphers) ->
bulk_cipher_algorithm(null) ->
?NULL;
-%% Not supported yet
-%% bulk_cipher_algorithm(idea_cbc) ->
-%% ?IDEA;
bulk_cipher_algorithm(rc4_128) ->
?RC4;
bulk_cipher_algorithm(des_cbc) ->
@@ -428,8 +510,7 @@ type(Cipher) when Cipher == null;
Cipher == rc4_128 ->
?STREAM;
-type(Cipher) when Cipher == idea_cbc;
- Cipher == des_cbc;
+type(Cipher) when Cipher == des_cbc;
Cipher == '3des_ede_cbc';
Cipher == aes_128_cbc;
Cipher == aes_256_cbc ->
@@ -437,8 +518,7 @@ type(Cipher) when Cipher == idea_cbc;
key_material(null) ->
0;
-key_material(Cipher) when Cipher == idea_cbc;
- Cipher == rc4_128 ->
+key_material(rc4_128) ->
16;
key_material(des_cbc) ->
8;
@@ -451,8 +531,7 @@ key_material(aes_256_cbc) ->
expanded_key_material(null) ->
0;
-expanded_key_material(Cipher) when Cipher == idea_cbc;
- Cipher == rc4_128 ->
+expanded_key_material(rc4_128) ->
16;
expanded_key_material(Cipher) when Cipher == des_cbc ->
8;
@@ -467,8 +546,7 @@ effective_key_bits(null) ->
0;
effective_key_bits(des_cbc) ->
56;
-effective_key_bits(Cipher) when Cipher == idea_cbc;
- Cipher == rc4_128;
+effective_key_bits(Cipher) when Cipher == rc4_128;
Cipher == aes_128_cbc ->
128;
effective_key_bits('3des_ede_cbc') ->
@@ -482,8 +560,7 @@ iv_size(Cipher) when Cipher == null;
iv_size(Cipher) ->
block_size(Cipher).
-block_size(Cipher) when Cipher == idea_cbc;
- Cipher == des_cbc;
+block_size(Cipher) when Cipher == des_cbc;
Cipher == '3des_ede_cbc' ->
8;
@@ -491,19 +568,51 @@ block_size(Cipher) when Cipher == aes_128_cbc;
Cipher == aes_256_cbc ->
16.
-mac_algorithm(null) ->
- ?NULL;
-mac_algorithm(md5) ->
- ?MD5;
-mac_algorithm(sha) ->
- ?SHA.
+prf_algorithm(default_prf, {3, N}) when N >= 3 ->
+ ?SHA256;
+prf_algorithm(default_prf, {3, _}) ->
+ ?MD5SHA;
+prf_algorithm(Algo, _) ->
+ hash_algorithm(Algo).
+
+hash_algorithm(null) -> ?NULL;
+hash_algorithm(md5) -> ?MD5;
+hash_algorithm(sha) -> ?SHA; %% Only sha always refers to "SHA-1"
+hash_algorithm(sha224) -> ?SHA224;
+hash_algorithm(sha256) -> ?SHA256;
+hash_algorithm(sha384) -> ?SHA384;
+hash_algorithm(sha512) -> ?SHA512;
+hash_algorithm(?NULL) -> null;
+hash_algorithm(?MD5) -> md5;
+hash_algorithm(?SHA) -> sha;
+hash_algorithm(?SHA224) -> sha224;
+hash_algorithm(?SHA256) -> sha256;
+hash_algorithm(?SHA384) -> sha384;
+hash_algorithm(?SHA512) -> sha512.
+
+sign_algorithm(anon) -> ?ANON;
+sign_algorithm(rsa) -> ?RSA;
+sign_algorithm(dsa) -> ?DSA;
+sign_algorithm(ecdsa) -> ?ECDSA;
+sign_algorithm(?ANON) -> anon;
+sign_algorithm(?RSA) -> rsa;
+sign_algorithm(?DSA) -> dsa;
+sign_algorithm(?ECDSA) -> ecdsa.
hash_size(null) ->
0;
hash_size(md5) ->
16;
hash_size(sha) ->
- 20.
+ 20;
+hash_size(sha256) ->
+ 32.
+%% Currently no supported cipher suites defaults to sha384 or sha512
+%% so these clauses are not needed at the moment.
+%% hash_size(sha384) ->
+%% 48;
+%% hash_size(sha512) ->
+%% 64.
%% RFC 5246: 6.2.3.2. CBC Block Cipher
%%
@@ -525,7 +634,8 @@ hash_size(sha) ->
%% We return the original (possibly invalid) PadLength in any case.
%% An invalid PadLength will be caught by is_correct_padding/2
%%
-generic_block_cipher_from_bin(T, HashSize) ->
+generic_block_cipher_from_bin({3, N}, T, IV, HashSize)
+ when N == 0; N == 1 ->
Sz1 = byte_size(T) - 1,
<<_:Sz1/binary, ?BYTE(PadLength0)>> = T,
PadLength = if
@@ -536,7 +646,20 @@ generic_block_cipher_from_bin(T, HashSize) ->
<<Content:CompressedLength/binary, Mac:HashSize/binary,
Padding:PadLength/binary, ?BYTE(PadLength0)>> = T,
#generic_block_cipher{content=Content, mac=Mac,
- padding=Padding, padding_length=PadLength0}.
+ padding=Padding, padding_length=PadLength0,
+ next_iv = IV};
+
+generic_block_cipher_from_bin({3, N}, T, IV, HashSize)
+ when N == 2; N == 3 ->
+ Sz1 = byte_size(T) - 1,
+ <<_:Sz1/binary, ?BYTE(PadLength)>> = T,
+ IVLength = byte_size(IV),
+ CompressedLength = byte_size(T) - IVLength - PadLength - 1 - HashSize,
+ <<NextIV:IVLength/binary, Content:CompressedLength/binary, Mac:HashSize/binary,
+ Padding:PadLength/binary, ?BYTE(PadLength)>> = T,
+ #generic_block_cipher{content=Content, mac=Mac,
+ padding=Padding, padding_length=PadLength,
+ next_iv = NextIV}.
generic_stream_cipher_from_bin(T, HashSz) ->
Sz = byte_size(T),
@@ -567,6 +690,10 @@ get_padding_aux(BlockSize, PadLength) ->
N = BlockSize - PadLength,
{N, list_to_binary(lists:duplicate(N, N))}.
+random_iv(IV) ->
+ IVSz = byte_size(IV),
+ ssl:random_bytes(IVSz).
+
next_iv(Bin, IV) ->
BinSz = byte_size(Bin),
IVSz = byte_size(IV),
@@ -578,16 +705,19 @@ rsa_signed_suites() ->
dhe_rsa_suites() ++ rsa_suites().
dhe_rsa_suites() ->
- [?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+ [?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
+ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
?TLS_DHE_RSA_WITH_DES_CBC_SHA].
rsa_suites() ->
- [?TLS_RSA_WITH_AES_256_CBC_SHA,
+ [?TLS_RSA_WITH_AES_256_CBC_SHA256,
+ ?TLS_RSA_WITH_AES_256_CBC_SHA,
?TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_RSA_WITH_AES_128_CBC_SHA256,
?TLS_RSA_WITH_AES_128_CBC_SHA,
- %%?TLS_RSA_WITH_IDEA_CBC_SHA,
?TLS_RSA_WITH_RC4_128_SHA,
?TLS_RSA_WITH_RC4_128_MD5,
?TLS_RSA_WITH_DES_CBC_SHA].
@@ -596,8 +726,10 @@ dsa_signed_suites() ->
dhe_dss_suites().
dhe_dss_suites() ->
- [?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+ [?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA].
diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl
index 8bd68cc190..0f439f8ed5 100644
--- a/lib/ssl/src/ssl_cipher.hrl
+++ b/lib/ssl/src/ssl_cipher.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -28,8 +28,9 @@
-type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc'
| aes_128_cbc | aes_256_cbc.
--type hash() :: null | sha | md5.
+-type hash() :: null | sha | md5 | sha256 | sha384 | sha512.
-type erl_cipher_suite() :: {key_algo(), cipher(), hash()}.
+-type int_cipher_suite() :: {key_algo(), cipher(), hash(), hash()}.
-type cipher_suite() :: binary().
-type cipher_enum() :: integer().
-type openssl_cipher_suite() :: string().
@@ -177,6 +178,47 @@
%% TLS_DH_anon_WITH_AES_256_CBC_SHA = { 0x00, 0x3A };
-define(TLS_DH_anon_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#3A)>>).
+%%% TLS 1.2 Cipher Suites RFC 5246
+
+%% TLS_RSA_WITH_NULL_SHA256 = { 0x00,0x3B };
+-define(TLS_RSA_WITH_NULL_SHA256, <<?BYTE(16#00), ?BYTE(16#3B)>>).
+
+%% TLS_RSA_WITH_AES_128_CBC_SHA256 = { 0x00,0x3C };
+-define(TLS_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3C)>>).
+
+%% TLS_RSA_WITH_AES_256_CBC_SHA256 = { 0x00,0x3D };
+-define(TLS_RSA_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3D)>>).
+
+%% TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = { 0x00,0x3E };
+-define(TLS_DH_DSS_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3E)>>).
+
+%% TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = { 0x00,0x3F };
+-define(TLS_DH_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3F)>>).
+
+%% TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = { 0x00,0x40 };
+-define(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#40)>>).
+
+%% TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = { 0x00,0x67 };
+-define(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#67)>>).
+
+%% TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = { 0x00,0x68 };
+-define(TLS_DH_DSS_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#68)>>).
+
+%% TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = { 0x00,0x69 };
+-define(TLS_DH_RSA_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#69)>>).
+
+%% TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = { 0x00,0x6A };
+-define(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6A)>>).
+
+%% TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = { 0x00,0x6B };
+-define(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6B)>>).
+
+%% TLS_DH_anon_WITH_AES_128_CBC_SHA256 = { 0x00,0x6C };
+-define(TLS_DH_anon_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6C)>>).
+
+%% TLS_DH_anon_WITH_AES_256_CBC_SHA256 = { 0x00,0x6D };
+-define(TLS_DH_anon_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6D)>>).
+
%%% Kerberos Cipher Suites
%% TLS_KRB5_WITH_DES_CBC_SHA = { 0x00,0x1E };
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index c57930e821..23f22987df 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -41,7 +41,7 @@
socket_control/3, close/1, shutdown/2,
new_user/2, get_opts/2, set_opts/2, info/1, session_info/1,
peer_certificate/1, sockname/1, peername/1, renegotiation/1,
- prf/5]).
+ negotiated_next_protocol/1, prf/5]).
%% Called by ssl_connection_sup
-export([start_link/7]).
@@ -67,8 +67,7 @@
tls_packets = [], % Not yet handled decode ssl/tls packets.
tls_record_buffer, % binary() buffer of incomplete records
tls_handshake_buffer, % binary() buffer of incomplete handshakes
- %% {{md5_hash, sha_hash}, {prev_md5, prev_sha}} (binary())
- tls_handshake_hashes, % see above
+ tls_handshake_history, % tls_handshake_history()
tls_cipher_texts, % list() received but not deciphered yet
cert_db, %
session, % #session{} from ssl_handshake.hrl
@@ -78,6 +77,7 @@
supported_protocol_versions, % [atom()]
client_certificate_requested = false,
key_algorithm, % atom as defined by cipher_suite
+ hashsign_algorithm, % atom as defined by cipher_suite
public_key_info, % PKIX: {Algorithm, PublicKey, PublicKeyParams}
private_key, % PKIX: #'RSAPrivateKey'{}
diffie_hellman_params, % PKIX: #'DHParameter'{} relevant for server side
@@ -92,7 +92,9 @@
start_or_recv_from, % "gen_fsm From"
send_queue, % queue()
terminated = false, %
- allow_renegotiate = true
+ allow_renegotiate = true,
+ expecting_next_protocol_negotiation = false :: boolean(),
+ next_protocol = undefined :: undefined | binary()
}).
-define(DEFAULT_DIFFIE_HELLMAN_PARAMS,
@@ -221,6 +223,13 @@ new_user(ConnectionPid, User) ->
sockname(ConnectionPid) ->
sync_send_all_state_event(ConnectionPid, sockname).
%%--------------------------------------------------------------------
+-spec negotiated_next_protocol(pid()) -> {ok, binary()} | {error, reason()}.
+%%
+%% Description: Returns the negotiated protocol
+%%--------------------------------------------------------------------
+negotiated_next_protocol(ConnectionPid) ->
+ sync_send_all_state_event(ConnectionPid, negotiated_next_protocol).
+%%--------------------------------------------------------------------
-spec peername(pid()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}.
%%
%% Description: Same as inet:peername/1
@@ -301,12 +310,13 @@ start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) ->
State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
- Hashes0 = ssl_handshake:init_hashes(),
+ Handshake = ssl_handshake:init_handshake_history(),
TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
try ssl_init(SSLOpts0, Role) of
{ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} ->
Session = State0#state.session,
- State = State0#state{tls_handshake_hashes = Hashes0,
+ State = State0#state{
+ tls_handshake_history = Handshake,
session = Session#session{own_certificate = OwnCert,
time_stamp = TimeStamp},
file_ref_db = FileRefHandle,
@@ -344,15 +354,15 @@ hello(start, #state{host = Host, port = Port, role = client,
Cache, CacheCb, Renegotiation, Cert),
Version = Hello#client_hello.client_version,
- Hashes0 = ssl_handshake:init_hashes(),
- {BinMsg, ConnectionStates, Hashes} =
- encode_handshake(Hello, Version, ConnectionStates0, Hashes0),
+ Handshake0 = ssl_handshake:init_handshake_history(),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Hello, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State1 = State0#state{connection_states = ConnectionStates,
- negotiated_version = Version, %% Requested version at this point
+ negotiated_version = Version, %% Requested version
session =
Session0#session{session_id = Hello#client_hello.session_id},
- tls_handshake_hashes = Hashes},
+ tls_handshake_history = Handshake},
{Record, State} = next_record(State1),
next_state(hello, hello, Record, State);
@@ -373,30 +383,41 @@ hello(#server_hello{cipher_suite = CipherSuite,
renegotiation = {Renegotiation, _},
ssl_options = SslOptions} = State0) ->
case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
- {Version, NewId, ConnectionStates} ->
- {KeyAlgorithm, _, _} =
+ #alert{} = Alert ->
+ handle_own_alert(Alert, ReqVersion, hello, State0),
+ {stop, normal, State0};
+
+ {Version, NewId, ConnectionStates, NextProtocol} ->
+ {KeyAlgorithm, _, _, _} =
ssl_cipher:suite_definition(CipherSuite),
-
+
PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm),
-
+
+ NewNextProtocol = case NextProtocol of
+ undefined ->
+ State0#state.next_protocol;
+ _ ->
+ NextProtocol
+ end,
+
State = State0#state{key_algorithm = KeyAlgorithm,
+ hashsign_algorithm = default_hashsign(Version, KeyAlgorithm),
negotiated_version = Version,
connection_states = ConnectionStates,
- premaster_secret = PremasterSecret},
-
+ premaster_secret = PremasterSecret,
+ expecting_next_protocol_negotiation = NextProtocol =/= undefined,
+ next_protocol = NewNextProtocol},
+
case ssl_session:is_new(OldId, NewId) of
true ->
handle_new_session(NewId, CipherSuite, Compression,
State#state{connection_states = ConnectionStates});
false ->
- handle_resumed_session(NewId, State#state{connection_states = ConnectionStates})
- end;
- #alert{} = Alert ->
- handle_own_alert(Alert, ReqVersion, hello, State0),
- {stop, normal, State0}
+ handle_resumed_session(NewId, State#state{connection_states = ConnectionStates})
+ end
end;
-hello(Hello = #client_hello{client_version = ClientVersion},
+hello(Hello = #client_hello{client_version = ClientVersion},
State = #state{connection_states = ConnectionStates0,
port = Port, session = #session{own_certificate = Cert} = Session0,
renegotiation = {Renegotiation, _},
@@ -405,8 +426,8 @@ hello(Hello = #client_hello{client_version = ClientVersion},
ssl_options = SslOpts}) ->
case ssl_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
ConnectionStates0, Cert}, Renegotiation) of
- {Version, {Type, Session}, ConnectionStates} ->
- do_server_hello(Type, State#state{connection_states =
+ {Version, {Type, Session}, ConnectionStates, ProtocolsToAdvertise} ->
+ do_server_hello(Type, ProtocolsToAdvertise, State#state{connection_states =
ConnectionStates,
negotiated_version = Version,
session = Session});
@@ -431,12 +452,13 @@ abbreviated(#hello_request{}, State0) ->
abbreviated(#finished{verify_data = Data} = Finished,
#state{role = server,
negotiated_version = Version,
- tls_handshake_hashes = Hashes,
+ tls_handshake_history = Handshake,
session = #session{master_secret = MasterSecret},
connection_states = ConnectionStates0} =
State) ->
case ssl_handshake:verify_connection(Version, Finished, client,
- MasterSecret, Hashes) of
+ get_current_connection_state_prf(ConnectionStates0, write),
+ MasterSecret, Handshake) of
verified ->
ConnectionStates = ssl_record:set_client_verify_data(current_both, Data, ConnectionStates0),
next_state_connection(abbreviated,
@@ -447,18 +469,19 @@ abbreviated(#finished{verify_data = Data} = Finished,
end;
abbreviated(#finished{verify_data = Data} = Finished,
- #state{role = client, tls_handshake_hashes = Hashes0,
+ #state{role = client, tls_handshake_history = Handshake0,
session = #session{master_secret = MasterSecret},
negotiated_version = Version,
connection_states = ConnectionStates0} = State) ->
case ssl_handshake:verify_connection(Version, Finished, server,
- MasterSecret, Hashes0) of
+ get_pending_connection_state_prf(ConnectionStates0, write),
+ MasterSecret, Handshake0) of
verified ->
ConnectionStates1 = ssl_record:set_server_verify_data(current_read, Data, ConnectionStates0),
- {ConnectionStates, Hashes} =
+ {ConnectionStates, Handshake} =
finalize_handshake(State#state{connection_states = ConnectionStates1}, abbreviated),
next_state_connection(abbreviated,
- ack_connection(State#state{tls_handshake_hashes = Hashes,
+ ack_connection(State#state{tls_handshake_history = Handshake,
connection_states =
ConnectionStates}));
#alert{} = Alert ->
@@ -589,6 +612,7 @@ certify(#client_key_exchange{exchange_keys = Keys},
{stop, normal, State}
end;
+
certify(timeout, State) ->
{ next_state, certify, State, hibernate };
@@ -636,15 +660,20 @@ cipher(#hello_request{}, State0) ->
{Record, State} = next_record(State0),
next_state(cipher, hello, Record, State);
-cipher(#certificate_verify{signature = Signature},
+cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashSign},
#state{role = server,
public_key_info = PublicKeyInfo,
negotiated_version = Version,
session = #session{master_secret = MasterSecret},
- tls_handshake_hashes = Hashes
+ hashsign_algorithm = ConnectionHashSign,
+ tls_handshake_history = Handshake
} = State0) ->
+ HashSign = case CertHashSign of
+ {_, _} -> CertHashSign;
+ _ -> ConnectionHashSign
+ end,
case ssl_handshake:certificate_verify(Signature, PublicKeyInfo,
- Version, MasterSecret, Hashes) of
+ Version, HashSign, MasterSecret, Handshake) of
valid ->
{Record, State} = next_record(State0),
next_state(cipher, cipher, Record, State);
@@ -653,6 +682,12 @@ cipher(#certificate_verify{signature = Signature},
{stop, normal, State0}
end;
+% client must send a next protocol message if we are expecting it
+cipher(#finished{}, #state{role = server, expecting_next_protocol_negotiation = true,
+ next_protocol = undefined, negotiated_version = Version} = State0) ->
+ handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, cipher, State0),
+ {stop, normal, State0};
+
cipher(#finished{verify_data = Data} = Finished,
#state{negotiated_version = Version,
host = Host,
@@ -660,10 +695,12 @@ cipher(#finished{verify_data = Data} = Finished,
role = Role,
session = #session{master_secret = MasterSecret}
= Session0,
- tls_handshake_hashes = Hashes0} = State) ->
+ connection_states = ConnectionStates0,
+ tls_handshake_history = Handshake0} = State) ->
case ssl_handshake:verify_connection(Version, Finished,
opposite_role(Role),
- MasterSecret, Hashes0) of
+ get_current_connection_state_prf(ConnectionStates0, read),
+ MasterSecret, Handshake0) of
verified ->
Session = register_session(Role, Host, Port, Session0),
cipher_role(Role, Data, Session, State);
@@ -672,6 +709,13 @@ cipher(#finished{verify_data = Data} = Finished,
{stop, normal, State}
end;
+% only allowed to send next_protocol message after change cipher spec
+% & before finished message and it is not allowed during renegotiation
+cipher(#next_protocol{selected_protocol = SelectedProtocol},
+ #state{role = server, expecting_next_protocol_negotiation = true} = State0) ->
+ {Record, State} = next_record(State0#state{next_protocol = SelectedProtocol}),
+ next_state(cipher, cipher, Record, State);
+
cipher(timeout, State) ->
{ next_state, cipher, State, hibernate };
@@ -691,17 +735,17 @@ connection(#hello_request{}, #state{host = Host, port = Port,
transport_cb = Transport,
connection_states = ConnectionStates0,
renegotiation = {Renegotiation, _},
- tls_handshake_hashes = Hashes0} = State0) ->
+ tls_handshake_history = Handshake0} = State0) ->
Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert),
- {BinMsg, ConnectionStates, Hashes} =
- encode_handshake(Hello, Version, ConnectionStates0, Hashes0),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Hello, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
{Record, State} = next_record(State0#state{connection_states =
- ConnectionStates,
+ ConnectionStates,
session = Session0#session{session_id = Hello#client_hello.session_id},
- tls_handshake_hashes = Hashes}),
+ tls_handshake_history = Handshake}),
next_state(connection, hello, Record, State);
connection(#client_hello{} = Hello, #state{role = server, allow_renegotiate = true} = State) ->
%% Mitigate Computational DoS attack
@@ -831,6 +875,10 @@ handle_sync_event(sockname, _From, StateName,
SockNameReply = inet:sockname(Socket),
{reply, SockNameReply, StateName, State, get_timeout(State)};
+handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = undefined} = State) ->
+ {reply, {error, next_protocol_not_negotiated}, StateName, State, get_timeout(State)};
+handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = NextProtocol} = State) ->
+ {reply, {ok, NextProtocol}, StateName, State, get_timeout(State)};
handle_sync_event(peername, _From, StateName,
#state{socket = Socket} = State) ->
PeerNameReply = inet:peername(Socket),
@@ -908,14 +956,14 @@ handle_sync_event(info, _, StateName,
session = #session{cipher_suite = Suite}} = State) ->
AtomVersion = ssl_record:protocol_version(Version),
- {reply, {ok, {AtomVersion, ssl_cipher:suite_definition(Suite)}},
+ {reply, {ok, {AtomVersion, ssl:suite_definition(Suite)}},
StateName, State, get_timeout(State)};
handle_sync_event(session_info, _, StateName,
#state{session = #session{session_id = Id,
cipher_suite = Suite}} = State) ->
{reply, [{session_id, Id},
- {cipher_suite, ssl_cipher:suite_definition(Suite)}],
+ {cipher_suite, ssl:suite_definition(Suite)}],
StateName, State, get_timeout(State)};
handle_sync_event(peer_certificate, _, StateName,
@@ -1224,13 +1272,13 @@ certify_client(#state{client_certificate_requested = true, role = client,
cert_db_ref = CertDbRef,
session = #session{own_certificate = OwnCert},
socket = Socket,
- tls_handshake_hashes = Hashes0} = State) ->
+ tls_handshake_history = Handshake0} = State) ->
Certificate = ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, client),
- {BinCert, ConnectionStates, Hashes} =
- encode_handshake(Certificate, Version, ConnectionStates0, Hashes0),
+ {BinCert, ConnectionStates, Handshake} =
+ encode_handshake(Certificate, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinCert),
State#state{connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes};
+ tls_handshake_history = Handshake};
certify_client(#state{client_certificate_requested = false} = State) ->
State.
@@ -1242,17 +1290,19 @@ verify_client_cert(#state{client_certificate_requested = true, role = client,
private_key = PrivateKey,
session = #session{master_secret = MasterSecret,
own_certificate = OwnCert},
- tls_handshake_hashes = Hashes0} = State) ->
+ hashsign_algorithm = HashSign,
+ tls_handshake_history = Handshake0} = State) ->
+ %%TODO: for TLS 1.2 we can choose a different/stronger HashSign combination for this.
case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret,
- Version, PrivateKey, Hashes0) of
+ Version, HashSign, PrivateKey, Handshake0) of
#certificate_verify{} = Verified ->
- {BinVerified, ConnectionStates, Hashes} =
+ {BinVerified, ConnectionStates, Handshake} =
encode_handshake(Verified, Version,
- ConnectionStates0, Hashes0),
+ ConnectionStates0, Handshake0),
Transport:send(Socket, BinVerified),
State#state{connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes};
+ tls_handshake_history = Handshake};
ignore ->
State;
#alert{} = Alert ->
@@ -1261,17 +1311,18 @@ verify_client_cert(#state{client_certificate_requested = true, role = client,
verify_client_cert(#state{client_certificate_requested = false} = State) ->
State.
-do_server_hello(Type, #state{negotiated_version = Version,
- session = #session{session_id = SessId},
- connection_states = ConnectionStates0,
- renegotiation = {Renegotiation, _}}
- = State0) when is_atom(Type) ->
+do_server_hello(Type, NextProtocolsToSend, #state{negotiated_version = Version,
+ session = #session{session_id = SessId},
+ connection_states = ConnectionStates0,
+ renegotiation = {Renegotiation, _}}
+ = State0) when is_atom(Type) ->
ServerHello =
ssl_handshake:server_hello(SessId, Version,
- ConnectionStates0, Renegotiation),
- State = server_hello(ServerHello, State0),
-
+ ConnectionStates0, Renegotiation, NextProtocolsToSend),
+ State = server_hello(ServerHello,
+ State0#state{expecting_next_protocol_negotiation =
+ NextProtocolsToSend =/= undefined}),
case Type of
new ->
new_server_hello(ServerHello, State);
@@ -1308,11 +1359,11 @@ resumed_server_hello(#state{session = Session,
{_, ConnectionStates1} ->
State1 = State0#state{connection_states = ConnectionStates1,
session = Session},
- {ConnectionStates, Hashes} =
+ {ConnectionStates, Handshake} =
finalize_handshake(State1, abbreviated),
State2 = State1#state{connection_states =
ConnectionStates,
- tls_handshake_hashes = Hashes},
+ tls_handshake_history = Handshake},
{Record, State} = next_record(State2),
next_state(hello, abbreviated, Record, State);
#alert{} = Alert ->
@@ -1351,11 +1402,11 @@ client_certify_and_key_exchange(#state{negotiated_version = Version} =
State0) ->
try do_client_certify_and_key_exchange(State0) of
State1 = #state{} ->
- {ConnectionStates, Hashes} = finalize_handshake(State1, certify),
+ {ConnectionStates, Handshake} = finalize_handshake(State1, certify),
State2 = State1#state{connection_states = ConnectionStates,
%% Reinitialize
client_certificate_requested = false,
- tls_handshake_hashes = Hashes},
+ tls_handshake_history = Handshake},
{Record, State} = next_record(State2),
next_state(certify, cipher, Record, State)
catch
@@ -1378,29 +1429,30 @@ server_hello(ServerHello, #state{transport_cb = Transport,
socket = Socket,
negotiated_version = Version,
connection_states = ConnectionStates0,
- tls_handshake_hashes = Hashes0} = State) ->
+ tls_handshake_history = Handshake0} = State) ->
CipherSuite = ServerHello#server_hello.cipher_suite,
- {KeyAlgorithm, _, _} = ssl_cipher:suite_definition(CipherSuite),
- {BinMsg, ConnectionStates1, Hashes1} =
- encode_handshake(ServerHello, Version, ConnectionStates0, Hashes0),
+ {KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite),
+ {BinMsg, ConnectionStates1, Handshake1} =
+ encode_handshake(ServerHello, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State#state{connection_states = ConnectionStates1,
- tls_handshake_hashes = Hashes1,
- key_algorithm = KeyAlgorithm}.
+ tls_handshake_history = Handshake1,
+ key_algorithm = KeyAlgorithm,
+ hashsign_algorithm = default_hashsign(Version, KeyAlgorithm)}.
server_hello_done(#state{transport_cb = Transport,
socket = Socket,
negotiated_version = Version,
connection_states = ConnectionStates0,
- tls_handshake_hashes = Hashes0} = State) ->
+ tls_handshake_history = Handshake0} = State) ->
HelloDone = ssl_handshake:server_hello_done(),
- {BinHelloDone, ConnectionStates, Hashes} =
- encode_handshake(HelloDone, Version, ConnectionStates0, Hashes0),
+ {BinHelloDone, ConnectionStates, Handshake} =
+ encode_handshake(HelloDone, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinHelloDone),
State#state{connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes}.
+ tls_handshake_history = Handshake}.
certify_server(#state{key_algorithm = dh_anon} = State) ->
State;
@@ -1409,17 +1461,17 @@ certify_server(#state{transport_cb = Transport,
socket = Socket,
negotiated_version = Version,
connection_states = ConnectionStates0,
- tls_handshake_hashes = Hashes0,
+ tls_handshake_history = Handshake0,
cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
session = #session{own_certificate = OwnCert}} = State) ->
case ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, server) of
CertMsg = #certificate{} ->
- {BinCertMsg, ConnectionStates, Hashes} =
- encode_handshake(CertMsg, Version, ConnectionStates0, Hashes0),
+ {BinCertMsg, ConnectionStates, Handshake} =
+ encode_handshake(CertMsg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinCertMsg),
State#state{connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes
+ tls_handshake_history = Handshake
};
Alert = #alert{} ->
throw(Alert)
@@ -1428,11 +1480,12 @@ certify_server(#state{transport_cb = Transport,
key_exchange(#state{role = server, key_algorithm = rsa} = State) ->
State;
key_exchange(#state{role = server, key_algorithm = Algo,
+ hashsign_algorithm = HashSignAlgo,
diffie_hellman_params = #'DHParameter'{prime = P, base = G} = Params,
private_key = PrivateKey,
connection_states = ConnectionStates0,
negotiated_version = Version,
- tls_handshake_hashes = Hashes0,
+ tls_handshake_history = Handshake0,
socket = Socket,
transport_cb = Transport
} = State)
@@ -1445,16 +1498,16 @@ key_exchange(#state{role = server, key_algorithm = Algo,
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = ssl_handshake:key_exchange(server, {dh, Keys, Params,
- Algo, ClientRandom,
+ Msg = ssl_handshake:key_exchange(server, Version, {dh, Keys, Params,
+ HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
- {BinMsg, ConnectionStates, Hashes} =
- encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State#state{connection_states = ConnectionStates,
diffie_hellman_keys = Keys,
- tls_handshake_hashes = Hashes};
+ tls_handshake_history = Handshake};
key_exchange(#state{role = client,
connection_states = ConnectionStates0,
@@ -1463,67 +1516,93 @@ key_exchange(#state{role = client,
negotiated_version = Version,
premaster_secret = PremasterSecret,
socket = Socket, transport_cb = Transport,
- tls_handshake_hashes = Hashes0} = State) ->
- Msg = rsa_key_exchange(PremasterSecret, PublicKeyInfo),
- {BinMsg, ConnectionStates, Hashes} =
- encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
+ tls_handshake_history = Handshake0} = State) ->
+ Msg = rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State#state{connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes};
+ tls_handshake_history = Handshake};
key_exchange(#state{role = client,
connection_states = ConnectionStates0,
key_algorithm = Algorithm,
negotiated_version = Version,
diffie_hellman_keys = {DhPubKey, _},
socket = Socket, transport_cb = Transport,
- tls_handshake_hashes = Hashes0} = State)
+ tls_handshake_history = Handshake0} = State)
when Algorithm == dhe_dss;
Algorithm == dhe_rsa;
Algorithm == dh_anon ->
- Msg = ssl_handshake:key_exchange(client, {dh, DhPubKey}),
- {BinMsg, ConnectionStates, Hashes} =
- encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
+ Msg = ssl_handshake:key_exchange(client, Version, {dh, DhPubKey}),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State#state{connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes}.
+ tls_handshake_history = Handshake}.
-rsa_key_exchange(PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
+rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
when Algorithm == ?rsaEncryption;
Algorithm == ?md2WithRSAEncryption;
Algorithm == ?md5WithRSAEncryption;
- Algorithm == ?sha1WithRSAEncryption ->
- ssl_handshake:key_exchange(client,
+ Algorithm == ?sha1WithRSAEncryption;
+ Algorithm == ?sha224WithRSAEncryption;
+ Algorithm == ?sha256WithRSAEncryption;
+ Algorithm == ?sha384WithRSAEncryption;
+ Algorithm == ?sha512WithRSAEncryption
+ ->
+ ssl_handshake:key_exchange(client, Version,
{premaster_secret, PremasterSecret,
PublicKeyInfo});
-rsa_key_exchange(_, _) ->
+rsa_key_exchange(_, _, _) ->
throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)).
request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer},
connection_states = ConnectionStates0,
cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
- tls_handshake_hashes = Hashes0,
+ tls_handshake_history = Handshake0,
negotiated_version = Version,
socket = Socket,
transport_cb = Transport} = State) ->
Msg = ssl_handshake:certificate_request(ConnectionStates0, CertDbHandle, CertDbRef),
- {BinMsg, ConnectionStates, Hashes} =
- encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State#state{client_certificate_requested = true,
connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes};
+ tls_handshake_history = Handshake};
request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} =
State) ->
State.
finalize_handshake(State, StateName) ->
- ConnectionStates0 = cipher_protocol(State),
+ ConnectionStates0 = cipher_protocol(State),
+
ConnectionStates =
ssl_record:activate_pending_connection_state(ConnectionStates0,
write),
- finished(State#state{connection_states = ConnectionStates}, StateName).
-
+
+ State1 = State#state{connection_states = ConnectionStates},
+ State2 = next_protocol(State1),
+ finished(State2, StateName).
+
+next_protocol(#state{role = server} = State) ->
+ State;
+next_protocol(#state{next_protocol = undefined} = State) ->
+ State;
+next_protocol(#state{expecting_next_protocol_negotiation = false} = State) ->
+ State;
+next_protocol(#state{transport_cb = Transport, socket = Socket,
+ negotiated_version = Version,
+ next_protocol = NextProtocol,
+ connection_states = ConnectionStates0,
+ tls_handshake_history = Handshake0} = State) ->
+ NextProtocolMessage = ssl_handshake:next_protocol(NextProtocol),
+ {BinMsg, ConnectionStates, Handshake} = encode_handshake(NextProtocolMessage, Version, ConnectionStates0, Handshake0),
+ Transport:send(Socket, BinMsg),
+ State#state{connection_states = ConnectionStates,
+ tls_handshake_history = Handshake}.
+
cipher_protocol(#state{connection_states = ConnectionStates0,
socket = Socket,
negotiated_version = Version,
@@ -1538,14 +1617,16 @@ finished(#state{role = Role, socket = Socket, negotiated_version = Version,
transport_cb = Transport,
session = Session,
connection_states = ConnectionStates0,
- tls_handshake_hashes = Hashes0}, StateName) ->
+ tls_handshake_history = Handshake0}, StateName) ->
MasterSecret = Session#session.master_secret,
- Finished = ssl_handshake:finished(Version, Role, MasterSecret, Hashes0),
+ Finished = ssl_handshake:finished(Version, Role,
+ get_current_connection_state_prf(ConnectionStates0, write),
+ MasterSecret, Handshake0),
ConnectionStates1 = save_verify_data(Role, Finished, ConnectionStates0, StateName),
- {BinFinished, ConnectionStates, Hashes} =
- encode_handshake(Finished, Version, ConnectionStates1, Hashes0),
+ {BinFinished, ConnectionStates, Handshake} =
+ encode_handshake(Finished, Version, ConnectionStates1, Handshake0),
Transport:send(Socket, BinFinished),
- {ConnectionStates, Hashes}.
+ {ConnectionStates, Handshake}.
save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, certify) ->
ssl_record:set_client_verify_data(current_write, Data, ConnectionStates);
@@ -1569,36 +1650,41 @@ handle_server_key(
#server_dh_params{dh_p = P,
dh_g = G,
dh_y = ServerPublicDhKey},
- signed_params = Signed},
- #state{public_key_info = PubKeyInfo,
- key_algorithm = KeyAlgo,
+ signed_params = Signed,
+ hashsign = HashSign},
+ #state{negotiated_version = Version,
+ public_key_info = PubKeyInfo,
connection_states = ConnectionStates} = State) ->
PLen = size(P),
GLen = size(G),
YLen = size(ServerPublicDhKey),
+ HashAlgo = connection_hash_algo(HashSign, State),
ConnectionState =
ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Hash = ssl_handshake:server_key_exchange_hash(KeyAlgo,
+ Hash = ssl_handshake:server_key_exchange_hash(HashAlgo,
<<ClientRandom/binary,
ServerRandom/binary,
?UINT16(PLen), P/binary,
?UINT16(GLen), G/binary,
?UINT16(YLen),
ServerPublicDhKey/binary>>),
-
- case verify_dh_params(Signed, Hash, PubKeyInfo) of
+
+ case verify_dh_params(Version, Signed, Hash, HashAlgo, PubKeyInfo) of
true ->
dh_master_secret(P, G, ServerPublicDhKey, undefined, State);
false ->
?ALERT_REC(?FATAL, ?DECRYPT_ERROR)
end.
-verify_dh_params(Signed, Hashes, {?rsaEncryption, PubKey, _PubKeyParams}) ->
+verify_dh_params({3, Minor}, Signed, Hashes, HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams})
+ when Minor >= 3 ->
+ public_key:verify({digest, Hashes}, HashAlgo, Signed, PubKey);
+verify_dh_params(_Version, Signed, Hashes, _HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams}) ->
case public_key:decrypt_public(Signed, PubKey,
[{rsa_pad, rsa_pkcs1_padding}]) of
Hashes ->
@@ -1606,8 +1692,8 @@ verify_dh_params(Signed, Hashes, {?rsaEncryption, PubKey, _PubKeyParams}) ->
_ ->
false
end;
-verify_dh_params(Signed, Hash, {?'id-dsa', PublicKey, PublicKeyParams}) ->
- public_key:verify(Hash, none, Signed, {PublicKey, PublicKeyParams}).
+verify_dh_params(_Version, Signed, Hash, HashAlgo, {?'id-dsa', PublicKey, PublicKeyParams}) ->
+ public_key:verify({digest, Hash}, HashAlgo, Signed, {PublicKey, PublicKeyParams}).
dh_master_secret(Prime, Base, PublicDhKey, undefined, State) ->
PMpint = mpint_binary(Prime),
@@ -1641,26 +1727,26 @@ cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0}
cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0} = State) ->
ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0),
- {ConnectionStates, Hashes} =
+ {ConnectionStates, Handshake} =
finalize_handshake(State#state{connection_states = ConnectionStates1,
session = Session}, cipher),
next_state_connection(cipher, ack_connection(State#state{connection_states =
ConnectionStates,
session = Session,
- tls_handshake_hashes =
- Hashes})).
+ tls_handshake_history =
+ Handshake})).
encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
ssl_record:encode_alert_record(Alert, Version, ConnectionStates).
encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) ->
ssl_record:encode_change_cipher_spec(Version, ConnectionStates).
-encode_handshake(HandshakeRec, Version, ConnectionStates0, Hashes0) ->
+encode_handshake(HandshakeRec, Version, ConnectionStates0, Handshake0) ->
Frag = ssl_handshake:encode_handshake(HandshakeRec, Version),
- Hashes1 = ssl_handshake:update_hashes(Hashes0, Frag),
+ Handshake1 = ssl_handshake:update_handshake_history(Handshake0, Frag),
{E, ConnectionStates1} =
ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
- {E, ConnectionStates1, Hashes1}.
+ {E, ConnectionStates1, Handshake1}.
encode_packet(Data, #socket_options{packet=Packet}) ->
case Packet of
@@ -1857,7 +1943,7 @@ format_reply(list, _,_, Data) ->
binary_to_list(Data).
header(0, <<>>) ->
- <<>>;
+ [];
header(_, <<>>) ->
[];
header(0, Binary) ->
@@ -1913,25 +1999,26 @@ next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) ->
%% This message should not be included in handshake
%% message hashes. Starts new handshake (renegotiation)
- Hs0 = ssl_handshake:init_hashes(),
- ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs0,
+ Hs0 = ssl_handshake:init_handshake_history(),
+ ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs0,
renegotiation = {true, peer}});
({#hello_request{} = Packet, _}, {next_state, SName, State}) ->
%% This message should not be included in handshake
%% message hashes. Already in negotiation so it will be ignored!
?MODULE:SName(Packet, State);
({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) ->
- Hs0 = ssl_handshake:init_hashes(),
- Hs1 = ssl_handshake:update_hashes(Hs0, Raw),
- ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1,
+ Version = Packet#client_hello.client_version,
+ Hs0 = ssl_handshake:init_handshake_history(),
+ Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
+ ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1,
renegotiation = {true, peer}});
- ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_hashes=Hs0}}) ->
- Hs1 = ssl_handshake:update_hashes(Hs0, Raw),
- ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1});
+ ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_history=Hs0}}) ->
+ Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
+ ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1});
(_, StopState) -> StopState
end,
try
- {Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0),
+ {Packets, Buf} = ssl_handshake:get_tls_handshake(Version,Data,Buf0),
State = State0#state{tls_packets = Packets, tls_handshake_buffer = Buf},
handle_tls_handshake(Handle, Next, State)
catch throw:#alert{} = Alert ->
@@ -2011,7 +2098,7 @@ next_state_connection(StateName, #state{send_queue = Queue0,
next_state_is_connection(StateName, State)
end.
-%% In next_state_is_connection/1: clear tls_handshake_hashes,
+%% In next_state_is_connection/1: clear tls_handshake,
%% premaster_secret and public_key_info (only needed during handshake)
%% to reduce memory foot print of a connection.
next_state_is_connection(_, State =
@@ -2020,13 +2107,13 @@ next_state_is_connection(_, State =
#socket_options{active = false}}) when RecvFrom =/= undefined ->
passive_receive(State#state{premaster_secret = undefined,
public_key_info = undefined,
- tls_handshake_hashes = {<<>>, <<>>}}, connection);
+ tls_handshake_history = ssl_handshake:init_handshake_history()}, connection);
next_state_is_connection(StateName, State0) ->
{Record, State} = next_record_if_active(State0),
next_state(StateName, connection, Record, State#state{premaster_secret = undefined,
- public_key_info = undefined,
- tls_handshake_hashes = {<<>>, <<>>}}).
+ public_key_info = undefined,
+ tls_handshake_history = ssl_handshake:init_handshake_history()}).
register_session(client, Host, Port, #session{is_resumable = new} = Session0) ->
Session = Session0#session{is_resumable = true},
@@ -2280,7 +2367,7 @@ handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = Stat
{stop, normal, State}.
make_premaster_secret({MajVer, MinVer}, rsa) ->
- Rand = crypto:rand_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
+ Rand = ssl:random_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
<<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>;
make_premaster_secret(_, _) ->
undefined.
@@ -2307,8 +2394,8 @@ ack_connection(State) ->
renegotiate(#state{role = client} = State) ->
%% Handle same way as if server requested
%% the renegotiation
- Hs0 = ssl_handshake:init_hashes(),
- connection(#hello_request{}, State#state{tls_handshake_hashes = Hs0});
+ Hs0 = ssl_handshake:init_handshake_history(),
+ connection(#hello_request{}, State#state{tls_handshake_history = Hs0});
renegotiate(#state{role = server,
socket = Socket,
transport_cb = Transport,
@@ -2316,13 +2403,13 @@ renegotiate(#state{role = server,
connection_states = ConnectionStates0} = State0) ->
HelloRequest = ssl_handshake:hello_request(),
Frag = ssl_handshake:encode_handshake(HelloRequest, Version),
- Hs0 = ssl_handshake:init_hashes(),
+ Hs0 = ssl_handshake:init_handshake_history(),
{BinMsg, ConnectionStates} =
ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
Transport:send(Socket, BinMsg),
{Record, State} = next_record(State0#state{connection_states =
ConnectionStates,
- tls_handshake_hashes = Hs0}),
+ tls_handshake_history = Hs0}),
next_state(connection, hello, Record, State#state{allow_renegotiate = true}).
notify_senders(SendQueue) ->
@@ -2392,3 +2479,48 @@ handle_trusted_certs_db(#state{cert_db_ref = Ref,
_ ->
ok
end.
+
+get_current_connection_state_prf(CStates, Direction) ->
+ CS = ssl_record:current_connection_state(CStates, Direction),
+ CS#connection_state.security_parameters#security_parameters.prf_algorithm.
+get_pending_connection_state_prf(CStates, Direction) ->
+ CS = ssl_record:pending_connection_state(CStates, Direction),
+ CS#connection_state.security_parameters#security_parameters.prf_algorithm.
+
+connection_hash_algo({HashAlgo, _}, _State) ->
+ HashAlgo;
+connection_hash_algo(_, #state{hashsign_algorithm = {HashAlgo, _}}) ->
+ HashAlgo.
+
+%% RFC 5246, Sect. 7.4.1.4.1. Signature Algorithms
+%% If the client does not send the signature_algorithms extension, the
+%% server MUST do the following:
+%%
+%% - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA,
+%% DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), behave as if client had
+%% sent the value {sha1,rsa}.
+%%
+%% - If the negotiated key exchange algorithm is one of (DHE_DSS,
+%% DH_DSS), behave as if the client had sent the value {sha1,dsa}.
+%%
+%% - If the negotiated key exchange algorithm is one of (ECDH_ECDSA,
+%% ECDHE_ECDSA), behave as if the client had sent value {sha1,ecdsa}.
+
+default_hashsign(_Version = {Major, Minor}, KeyExchange)
+ when Major == 3 andalso Minor >= 3 andalso
+ (KeyExchange == rsa orelse
+ KeyExchange == dhe_rsa orelse
+ KeyExchange == dh_rsa) ->
+ {sha, rsa};
+default_hashsign(_Version, KeyExchange)
+ when KeyExchange == rsa;
+ KeyExchange == dhe_rsa;
+ KeyExchange == dh_rsa ->
+ {md5sha, rsa};
+default_hashsign(_Version, KeyExchange)
+ when KeyExchange == dhe_dss;
+ KeyExchange == dh_dss ->
+ {sha, dsa};
+default_hashsign(_Version, KeyExchange)
+ when KeyExchange == dh_anon ->
+ {null, anon}.
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 06d45966c1..fa1784714f 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -30,21 +30,21 @@
-include("ssl_internal.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([master_secret/4, client_hello/8, server_hello/4, hello/4,
+-export([master_secret/4, client_hello/8, server_hello/5, hello/4,
hello_request/0, certify/7, certificate/4,
- client_certificate_verify/5, certificate_verify/5,
- certificate_request/3, key_exchange/2, server_key_exchange_hash/2,
- finished/4, verify_connection/5, get_tls_handshake/2,
+ client_certificate_verify/6, certificate_verify/6,
+ certificate_request/3, key_exchange/3, server_key_exchange_hash/2,
+ finished/5, verify_connection/6, get_tls_handshake/3,
decode_client_key/3, server_hello_done/0,
- encode_handshake/2, init_hashes/0, update_hashes/2,
- decrypt_premaster_secret/2, prf/5]).
+ encode_handshake/2, init_handshake_history/0, update_handshake_history/2,
+ decrypt_premaster_secret/2, prf/5, next_protocol/1]).
-export([dec_hello_extensions/2]).
-type tls_handshake() :: #client_hello{} | #server_hello{} |
#server_hello_done{} | #certificate{} | #certificate_request{} |
#client_key_exchange{} | #finished{} | #certificate_verify{} |
- #hello_request{}.
+ #hello_request{} | #next_protocol{}.
%%====================================================================
%% Internal application API
@@ -77,17 +77,31 @@ client_hello(Host, Port, ConnectionStates,
cipher_suites = cipher_suites(Ciphers, Renegotiation),
compression_methods = ssl_record:compressions(),
random = SecParams#security_parameters.client_random,
+
renegotiation_info =
- renegotiation_info(client, ConnectionStates, Renegotiation)
+ renegotiation_info(client, ConnectionStates, Renegotiation),
+ hash_signs = default_hash_signs(),
+ next_protocol_negotiation =
+ encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector, Renegotiation)
}.
+encode_protocol(Protocol, Acc) ->
+ Len = byte_size(Protocol),
+ <<Acc/binary, ?BYTE(Len), Protocol/binary>>.
+
+encode_protocols_advertised_on_server(undefined) ->
+ undefined;
+
+encode_protocols_advertised_on_server(Protocols) ->
+ #next_protocol_negotiation{extension_data = lists:foldl(fun encode_protocol/2, <<>>, Protocols)}.
+
%%--------------------------------------------------------------------
-spec server_hello(session_id(), tls_version(), #connection_states{},
- boolean()) -> #server_hello{}.
+ boolean(), [binary()] | undefined) -> #server_hello{}.
%%
%% Description: Creates a server hello message.
%%--------------------------------------------------------------------
-server_hello(SessionId, Version, ConnectionStates, Renegotiation) ->
+server_hello(SessionId, Version, ConnectionStates, Renegotiation, ProtocolsAdvertisedOnServer) ->
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = Pending#connection_state.security_parameters,
#server_hello{server_version = Version,
@@ -97,7 +111,8 @@ server_hello(SessionId, Version, ConnectionStates, Renegotiation) ->
random = SecParams#security_parameters.server_random,
session_id = SessionId,
renegotiation_info =
- renegotiation_info(server, ConnectionStates, Renegotiation)
+ renegotiation_info(server, ConnectionStates, Renegotiation),
+ next_protocol_negotiation = encode_protocols_advertised_on_server(ProtocolsAdvertisedOnServer)
}.
%%--------------------------------------------------------------------
@@ -112,28 +127,35 @@ hello_request() ->
%%--------------------------------------------------------------------
-spec hello(#server_hello{} | #client_hello{}, #ssl_options{},
#connection_states{} | {inet:port_number(), #session{}, db_handle(),
- atom(), #connection_states{}, binary()},
- boolean()) -> {tls_version(), session_id(), #connection_states{}}|
- {tls_version(), {resumed | new, #session{}},
- #connection_states{}} | #alert{}.
+ atom(), #connection_states{}, binary()},
+ boolean()) ->
+ {tls_version(), session_id(), #connection_states{}, binary() | undefined}|
+ {tls_version(), {resumed | new, #session{}}, #connection_states{}, list(binary()) | undefined} |
+ #alert{}.
%%
%% Description: Handles a recieved hello message
%%--------------------------------------------------------------------
hello(#server_hello{cipher_suite = CipherSuite, server_version = Version,
compression_method = Compression, random = Random,
- session_id = SessionId, renegotiation_info = Info},
- #ssl_options{secure_renegotiate = SecureRenegotation},
+ session_id = SessionId, renegotiation_info = Info,
+ hash_signs = _HashSigns} = Hello,
+ #ssl_options{secure_renegotiate = SecureRenegotation, next_protocol_selector = NextProtocolSelector},
ConnectionStates0, Renegotiation) ->
-
+ %%TODO: select hash and signature algorigthm
case ssl_record:is_acceptable_version(Version) of
true ->
case handle_renegotiation_info(client, Info, ConnectionStates0,
Renegotiation, SecureRenegotation, []) of
{ok, ConnectionStates1} ->
ConnectionStates =
- hello_pending_connection_states(client, CipherSuite, Random,
+ hello_pending_connection_states(client, Version, CipherSuite, Random,
Compression, ConnectionStates1),
- {Version, SessionId, ConnectionStates};
+ case handle_next_protocol(Hello, NextProtocolSelector, Renegotiation) of
+ #alert{} = Alert ->
+ Alert;
+ Protocol ->
+ {Version, SessionId, ConnectionStates, Protocol}
+ end;
#alert{} = Alert ->
Alert
end;
@@ -144,9 +166,10 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version,
hello(#client_hello{client_version = ClientVersion, random = Random,
cipher_suites = CipherSuites,
renegotiation_info = Info} = Hello,
- #ssl_options{versions = Versions,
+ #ssl_options{versions = Versions,
secure_renegotiate = SecureRenegotation} = SslOpts,
{Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) ->
+%% TODO: select hash and signature algorithm
Version = select_version(ClientVersion, Versions),
case ssl_record:is_acceptable_version(Version) of
true ->
@@ -164,11 +187,17 @@ hello(#client_hello{client_version = ClientVersion, random = Random,
{ok, ConnectionStates1} ->
ConnectionStates =
hello_pending_connection_states(server,
+ Version,
CipherSuite,
Random,
Compression,
ConnectionStates1),
- {Version, {Type, Session}, ConnectionStates};
+ case handle_next_protocol_on_server(Hello, Renegotiation, SslOpts) of
+ #alert{} = Alert ->
+ Alert;
+ ProtocolsToAdvertise ->
+ {Version, {Type, Session}, ConnectionStates, ProtocolsToAdvertise}
+ end;
#alert{} = Alert ->
Alert
end
@@ -215,18 +244,23 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef,
end, {Role, UserState0}}
end,
- {TrustedErlCert, CertPath} =
- ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef),
-
- case public_key:pkix_path_validation(TrustedErlCert,
- CertPath,
- [{max_path_length,
- MaxPathLen},
- {verify_fun, ValidationFunAndState}]) of
- {ok, {PublicKeyInfo,_}} ->
- {PeerCert, PublicKeyInfo};
- {error, Reason} ->
- path_validation_alert(Reason)
+ try
+ {TrustedErlCert, CertPath} =
+ ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef),
+ case public_key:pkix_path_validation(TrustedErlCert,
+ CertPath,
+ [{max_path_length,
+ MaxPathLen},
+ {verify_fun, ValidationFunAndState}]) of
+ {ok, {PublicKeyInfo,_}} ->
+ {PeerCert, PublicKeyInfo};
+ {error, Reason} ->
+ path_validation_alert(Reason)
+ end
+ catch
+ error:_ ->
+ %% ASN-1 decode of certificate somehow failed
+ ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN)
end.
%%--------------------------------------------------------------------
@@ -257,54 +291,51 @@ certificate(OwnCert, CertDbHandle, CertDbRef, server) ->
%%--------------------------------------------------------------------
-spec client_certificate_verify(undefined | der_cert(), binary(),
- tls_version(), private_key(),
- {{binary(), binary()},{binary(), binary()}}) ->
+ tls_version(), term(), private_key(),
+ tls_handshake_history()) ->
#certificate_verify{} | ignore | #alert{}.
%%
%% Description: Creates a certificate_verify message, called by the client.
%%--------------------------------------------------------------------
-client_certificate_verify(undefined, _, _, _, _) ->
+client_certificate_verify(undefined, _, _, _, _, _) ->
ignore;
-client_certificate_verify(_, _, _, undefined, _) ->
+client_certificate_verify(_, _, _, _, undefined, _) ->
ignore;
client_certificate_verify(OwnCert, MasterSecret, Version,
- PrivateKey, {Hashes0, _}) ->
+ {HashAlgo, SignAlgo},
+ PrivateKey, {Handshake, _}) ->
case public_key:pkix_is_fixed_dh_cert(OwnCert) of
true ->
?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE);
- false ->
- Hashes =
- calc_certificate_verify(Version, MasterSecret,
- alg_oid(PrivateKey), Hashes0),
- Signed = digitally_signed(Hashes, PrivateKey),
- #certificate_verify{signature = Signed}
+ false ->
+ Hashes =
+ calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
+ Signed = digitally_signed(Version, Hashes, HashAlgo, PrivateKey),
+ #certificate_verify{signature = Signed, hashsign_algorithm = {HashAlgo, SignAlgo}}
end.
%%--------------------------------------------------------------------
--spec certificate_verify(binary(), public_key_info(), tls_version(),
- binary(), {_, {binary(), binary()}}) -> valid | #alert{}.
+-spec certificate_verify(binary(), public_key_info(), tls_version(), term(),
+ binary(), tls_handshake_history()) -> valid | #alert{}.
%%
%% Description: Checks that the certificate_verify message is valid.
%%--------------------------------------------------------------------
-certificate_verify(Signature, {?'rsaEncryption'= Algorithm, PublicKey, _}, Version,
- MasterSecret, {_, Hashes0}) ->
- Hashes = calc_certificate_verify(Version, MasterSecret,
- Algorithm, Hashes0),
- case public_key:decrypt_public(Signature, PublicKey,
- [{rsa_pad, rsa_pkcs1_padding}]) of
- Hashes ->
+certificate_verify(Signature, {?'rsaEncryption', PublicKey, _}, Version,
+ {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) ->
+ Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
+ case certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, Version) of
+ true ->
valid;
_ ->
?ALERT_REC(?FATAL, ?BAD_CERTIFICATE)
end;
-certificate_verify(Signature, {?'id-dsa' = Algorithm, PublicKey, PublicKeyParams}, Version,
- MasterSecret, {_, Hashes0}) ->
- Hashes = calc_certificate_verify(Version, MasterSecret,
- Algorithm, Hashes0),
- case public_key:verify(Hashes, none, Signature, {PublicKey, PublicKeyParams}) of
- true ->
- valid;
- false ->
+certificate_verify(Signature, {?'id-dsa', PublicKey, PublicKeyParams}, Version,
+ {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) ->
+ Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
+ case public_key:verify({digest, Hashes}, sha, Signature, {PublicKey, PublicKeyParams}) of
+ true ->
+ valid;
+ false ->
?ALERT_REC(?FATAL, ?BAD_CERTIFICATE)
end.
@@ -320,36 +351,38 @@ certificate_request(ConnectionStates, CertDbHandle, CertDbRef) ->
#security_parameters{cipher_suite = CipherSuite}} =
ssl_record:pending_connection_state(ConnectionStates, read),
Types = certificate_types(CipherSuite),
+ HashSigns = default_hash_signs(),
Authorities = certificate_authorities(CertDbHandle, CertDbRef),
#certificate_request{
certificate_types = Types,
+ hashsign_algorithms = HashSigns,
certificate_authorities = Authorities
}.
%%--------------------------------------------------------------------
--spec key_exchange(client | server,
+-spec key_exchange(client | server, tls_version(),
{premaster_secret, binary(), public_key_info()} |
{dh, binary()} |
- {dh, {binary(), binary()}, #'DHParameter'{}, key_algo(),
+ {dh, {binary(), binary()}, #'DHParameter'{}, {HashAlgo::atom(), SignAlgo::atom()},
binary(), binary(), private_key()}) ->
#client_key_exchange{} | #server_key_exchange{}.
%%
%% Description: Creates a keyexchange message.
%%--------------------------------------------------------------------
-key_exchange(client, {premaster_secret, Secret, {_, PublicKey, _}}) ->
+key_exchange(client, _Version, {premaster_secret, Secret, {_, PublicKey, _}}) ->
EncPremasterSecret =
encrypted_premaster_secret(Secret, PublicKey),
#client_key_exchange{exchange_keys = EncPremasterSecret};
-key_exchange(client, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) ->
+key_exchange(client, _Version, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) ->
#client_key_exchange{
exchange_keys = #client_diffie_hellman_public{
dh_public = PublicKey}
};
-key_exchange(server, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _},
+key_exchange(server, Version, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _},
#'DHParameter'{prime = P, base = G},
- KeyAlgo, ClientRandom, ServerRandom, PrivateKey}) ->
+ {HashAlgo, SignAlgo}, ClientRandom, ServerRandom, PrivateKey}) ->
<<?UINT32(_), PBin/binary>> = crypto:mpint(P),
<<?UINT32(_), GBin/binary>> = crypto:mpint(G),
PLen = byte_size(PBin),
@@ -358,20 +391,22 @@ key_exchange(server, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _},
ServerDHParams = #server_dh_params{dh_p = PBin,
dh_g = GBin, dh_y = PublicKey},
- case KeyAlgo of
- dh_anon ->
+ case HashAlgo of
+ null ->
#server_key_exchange{params = ServerDHParams,
- signed_params = <<>>};
+ signed_params = <<>>,
+ hashsign = {null, anon}};
_ ->
Hash =
- server_key_exchange_hash(KeyAlgo, <<ClientRandom/binary,
+ server_key_exchange_hash(HashAlgo, <<ClientRandom/binary,
ServerRandom/binary,
?UINT16(PLen), PBin/binary,
?UINT16(GLen), GBin/binary,
?UINT16(YLen), PublicKey/binary>>),
- Signed = digitally_signed(Hash, PrivateKey),
+ Signed = digitally_signed(Version, Hash, HashAlgo, PrivateKey),
#server_key_exchange{params = ServerDHParams,
- signed_params = Signed}
+ signed_params = Signed,
+ hashsign = {HashAlgo, SignAlgo}}
end.
%%--------------------------------------------------------------------
@@ -401,10 +436,11 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) ->
ConnectionState =
ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = ConnectionState#connection_state.security_parameters,
- #security_parameters{client_random = ClientRandom,
+ #security_parameters{prf_algorithm = PrfAlgo,
+ client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
try master_secret(Version,
- calc_master_secret(Version,PremasterSecret,
+ calc_master_secret(Version,PrfAlgo,PremasterSecret,
ClientRandom, ServerRandom),
SecParams, ConnectionStates, Role)
catch
@@ -415,27 +451,32 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) ->
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
end.
+-spec next_protocol(binary()) -> #next_protocol{}.
+
+next_protocol(SelectedProtocol) ->
+ #next_protocol{selected_protocol = SelectedProtocol}.
+
%%--------------------------------------------------------------------
--spec finished(tls_version(), client | server, binary(), {{binary(), binary()},_}) ->
+-spec finished(tls_version(), client | server, integer(), binary(), tls_handshake_history()) ->
#finished{}.
%%
%% Description: Creates a handshake finished message
%%-------------------------------------------------------------------
-finished(Version, Role, MasterSecret, {Hashes, _}) -> % use the current hashes
+finished(Version, Role, PrfAlgo, MasterSecret, {Handshake, _}) -> % use the current handshake
#finished{verify_data =
- calc_finished(Version, Role, MasterSecret, Hashes)}.
+ calc_finished(Version, Role, PrfAlgo, MasterSecret, Handshake)}.
%%--------------------------------------------------------------------
--spec verify_connection(tls_version(), #finished{}, client | server, binary(),
- {_, {binary(), binary()}}) -> verified | #alert{}.
+-spec verify_connection(tls_version(), #finished{}, client | server, integer(), binary(),
+ tls_handshake_history()) -> verified | #alert{}.
%%
%% Description: Checks the ssl handshake finished message to verify
%% the connection.
%%-------------------------------------------------------------------
verify_connection(Version, #finished{verify_data = Data},
- Role, MasterSecret, {_, {MD5, SHA}}) ->
+ Role, PrfAlgo, MasterSecret, {_, Handshake}) ->
%% use the previous hashes
- case calc_finished(Version, Role, MasterSecret, {MD5, SHA}) of
+ case calc_finished(Version, Role, PrfAlgo, MasterSecret, Handshake) of
Data ->
verified;
_ ->
@@ -460,17 +501,17 @@ encode_handshake(Package, Version) ->
[MsgType, ?uint24(Len), Bin].
%%--------------------------------------------------------------------
--spec get_tls_handshake(binary(), binary() | iolist()) ->
+-spec get_tls_handshake(tls_version(), binary(), binary() | iolist()) ->
{[tls_handshake()], binary()}.
%%
%% Description: Given buffered and new data from ssl_record, collects
%% and returns it as a list of handshake messages, also returns leftover
%% data.
%%--------------------------------------------------------------------
-get_tls_handshake(Data, <<>>) ->
- get_tls_handshake_aux(Data, []);
-get_tls_handshake(Data, Buffer) ->
- get_tls_handshake_aux(list_to_binary([Buffer, Data]), []).
+get_tls_handshake(Version, Data, <<>>) ->
+ get_tls_handshake_aux(Version, Data, []);
+get_tls_handshake(Version, Data, Buffer) ->
+ get_tls_handshake_aux(Version, list_to_binary([Buffer, Data]), []).
%%--------------------------------------------------------------------
-spec decode_client_key(binary(), key_algo(), tls_version()) ->
@@ -482,39 +523,34 @@ decode_client_key(ClientKey, Type, Version) ->
dec_client_key(ClientKey, key_exchange_alg(Type), Version).
%%--------------------------------------------------------------------
--spec init_hashes() ->{{binary(), binary()}, {binary(), binary()}}.
+-spec init_handshake_history() -> tls_handshake_history().
%%
-%% Description: Calls crypto hash (md5 and sha) init functions to
-%% initalize the hash context.
+%% Description: Initialize the empty handshake history buffer.
%%--------------------------------------------------------------------
-init_hashes() ->
- T = {crypto:md5_init(), crypto:sha_init()},
- {T, T}.
+init_handshake_history() ->
+ {[], []}.
%%--------------------------------------------------------------------
--spec update_hashes({{binary(), binary()}, {binary(), binary()}}, Data ::term()) ->
- {{binary(), binary()}, {binary(), binary()}}.
+-spec update_handshake_history(tls_handshake_history(), Data ::term()) ->
+ tls_handshake_history().
%%
-%% Description: Calls crypto hash (md5 and sha) update functions to
-%% update the hash context with Data.
+%% Description: Update the handshake history buffer with Data.
%%--------------------------------------------------------------------
-update_hashes(Hashes, % special-case SSL2 client hello
- <<?CLIENT_HELLO, ?UINT24(_), ?BYTE(Major), ?BYTE(Minor),
- ?UINT16(CSLength), ?UINT16(0),
- ?UINT16(CDLength),
- CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>) ->
- update_hashes(Hashes,
- <<?CLIENT_HELLO, ?BYTE(Major), ?BYTE(Minor),
- ?UINT16(CSLength), ?UINT16(0),
- ?UINT16(CDLength),
- CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>);
-update_hashes({{MD50, SHA0}, _Prev}, Data) ->
- {MD51, SHA1} = {crypto:md5_update(MD50, Data),
- crypto:sha_update(SHA0, Data)},
- {{MD51, SHA1}, {MD50, SHA0}}.
+update_handshake_history(Handshake, % special-case SSL2 client hello
+ <<?CLIENT_HELLO, ?UINT24(_), ?BYTE(Major), ?BYTE(Minor),
+ ?UINT16(CSLength), ?UINT16(0),
+ ?UINT16(CDLength),
+ CipherSuites:CSLength/binary,
+ ChallengeData:CDLength/binary>>) ->
+ update_handshake_history(Handshake,
+ <<?CLIENT_HELLO, ?BYTE(Major), ?BYTE(Minor),
+ ?UINT16(CSLength), ?UINT16(0),
+ ?UINT16(CDLength),
+ CipherSuites:CSLength/binary,
+ ChallengeData:CDLength/binary>>);
+update_handshake_history({Handshake0, _Prev}, Data) ->
+ {[Data|Handshake0], Handshake0}.
%%--------------------------------------------------------------------
-spec decrypt_premaster_secret(binary(), #'RSAPrivateKey'{}) -> binary().
@@ -527,23 +563,22 @@ decrypt_premaster_secret(Secret, RSAPrivateKey) ->
[{rsa_pad, rsa_pkcs1_padding}])
catch
_:_ ->
+ io:format("decrypt_premaster_secret error"),
throw(?ALERT_REC(?FATAL, ?DECRYPT_ERROR))
end.
%%--------------------------------------------------------------------
--spec server_key_exchange_hash(rsa | dhe_rsa| dhe_dss | dh_anon, binary()) -> binary().
-
+-spec server_key_exchange_hash(md5sha | md5 | sha | sha224 |sha256 | sha384 | sha512, binary()) -> binary().
%%
%% Description: Calculate server key exchange hash
%%--------------------------------------------------------------------
-server_key_exchange_hash(Algorithm, Value) when Algorithm == rsa;
- Algorithm == dhe_rsa ->
+server_key_exchange_hash(md5sha, Value) ->
MD5 = crypto:md5(Value),
- SHA = crypto:sha(Value),
+ SHA = crypto:sha(Value),
<<MD5/binary, SHA/binary>>;
-server_key_exchange_hash(dhe_dss, Value) ->
- crypto:sha(Value).
+server_key_exchange_hash(Hash, Value) ->
+ crypto:hash(Hash, Value).
%%--------------------------------------------------------------------
-spec prf(tls_version(), binary(), binary(), [binary()], non_neg_integer()) ->
@@ -553,19 +588,20 @@ server_key_exchange_hash(dhe_dss, Value) ->
%%--------------------------------------------------------------------
prf({3,0}, _, _, _, _) ->
{error, undefined};
-prf({3,N}, Secret, Label, Seed, WantedLength)
- when N == 1; N == 2 ->
- {ok, ssl_tls1:prf(Secret, Label, Seed, WantedLength)}.
+prf({3,1}, Secret, Label, Seed, WantedLength) ->
+ {ok, ssl_tls1:prf(?MD5SHA, Secret, Label, Seed, WantedLength)};
+prf({3,_N}, Secret, Label, Seed, WantedLength) ->
+ {ok, ssl_tls1:prf(?SHA256, Secret, Label, Seed, WantedLength)}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length),
+get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length),
Body:Length/binary,Rest/binary>>, Acc) ->
Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>,
- H = dec_hs(Type, Body),
- get_tls_handshake_aux(Rest, [{H,Raw} | Acc]);
-get_tls_handshake_aux(Data, Acc) ->
+ H = dec_hs(Version, Type, Body),
+ get_tls_handshake_aux(Version, Rest, [{H,Raw} | Acc]);
+get_tls_handshake_aux(_Version, Data, Acc) ->
{lists:reverse(Acc), Data}.
path_validation_alert({bad_cert, cert_expired}) ->
@@ -653,6 +689,57 @@ renegotiation_info(server, ConnectionStates, true) ->
#renegotiation_info{renegotiated_connection = undefined}
end.
+decode_next_protocols({next_protocol_negotiation, Protocols}) ->
+ decode_next_protocols(Protocols, []).
+decode_next_protocols(<<>>, Acc) ->
+ lists:reverse(Acc);
+decode_next_protocols(<<?BYTE(Len), Protocol:Len/binary, Rest/binary>>, Acc) ->
+ case Len of
+ 0 ->
+ {error, invalid_next_protocols};
+ _ ->
+ decode_next_protocols(Rest, [Protocol|Acc])
+ end;
+decode_next_protocols(_Bytes, _Acc) ->
+ {error, invalid_next_protocols}.
+
+next_protocol_extension_allowed(NextProtocolSelector, Renegotiating) ->
+ NextProtocolSelector =/= undefined andalso not Renegotiating.
+
+handle_next_protocol_on_server(#client_hello{next_protocol_negotiation = undefined}, _Renegotiation, _SslOpts) ->
+ undefined;
+
+handle_next_protocol_on_server(#client_hello{next_protocol_negotiation = {next_protocol_negotiation, <<>>}},
+ false, #ssl_options{next_protocols_advertised = Protocols}) ->
+ Protocols;
+
+handle_next_protocol_on_server(_Hello, _Renegotiation, _SSLOpts) ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE). % unexpected next protocol extension
+
+handle_next_protocol(#server_hello{next_protocol_negotiation = undefined},
+ _NextProtocolSelector, _Renegotiating) ->
+ undefined;
+
+handle_next_protocol(#server_hello{next_protocol_negotiation = Protocols},
+ NextProtocolSelector, Renegotiating) ->
+
+ case next_protocol_extension_allowed(NextProtocolSelector, Renegotiating) of
+ true ->
+ select_next_protocol(decode_next_protocols(Protocols), NextProtocolSelector);
+ false ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE) % unexpected next protocol extension
+ end.
+
+select_next_protocol({error, _Reason}, _NextProtocolSelector) ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
+select_next_protocol(Protocols, NextProtocolSelector) ->
+ case NextProtocolSelector(Protocols) of
+ ?NO_PROTOCOL ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
+ Protocol when is_binary(Protocol) ->
+ Protocol
+ end.
+
handle_renegotiation_info(_, #renegotiation_info{renegotiated_connection = ?byte(0)},
ConnectionStates, false, _, _) ->
{ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
@@ -722,7 +809,7 @@ handle_renegotiation_info(ConnectionStates, SecureRenegotation) ->
%% hello messages
%% NOTE : Role is the role of the receiver of the hello message
%% currently being processed.
-hello_pending_connection_states(Role, CipherSuite, Random, Compression,
+hello_pending_connection_states(Role, Version, CipherSuite, Random, Compression,
ConnectionStates) ->
ReadState =
ssl_record:pending_connection_state(ConnectionStates, read),
@@ -730,30 +817,30 @@ hello_pending_connection_states(Role, CipherSuite, Random, Compression,
ssl_record:pending_connection_state(ConnectionStates, write),
NewReadSecParams =
- hello_security_parameters(Role, ReadState, CipherSuite,
+ hello_security_parameters(Role, Version, ReadState, CipherSuite,
Random, Compression),
NewWriteSecParams =
- hello_security_parameters(Role, WriteState, CipherSuite,
+ hello_security_parameters(Role, Version, WriteState, CipherSuite,
Random, Compression),
ssl_record:update_security_params(NewReadSecParams,
NewWriteSecParams,
ConnectionStates).
-hello_security_parameters(client, ConnectionState, CipherSuite, Random,
+hello_security_parameters(client, Version, ConnectionState, CipherSuite, Random,
Compression) ->
SecParams = ConnectionState#connection_state.security_parameters,
- NewSecParams = ssl_cipher:security_parameters(CipherSuite, SecParams),
+ NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams),
NewSecParams#security_parameters{
server_random = Random,
compression_algorithm = Compression
};
-hello_security_parameters(server, ConnectionState, CipherSuite, Random,
+hello_security_parameters(server, Version, ConnectionState, CipherSuite, Random,
Compression) ->
SecParams = ConnectionState#connection_state.security_parameters,
- NewSecParams = ssl_cipher:security_parameters(CipherSuite, SecParams),
+ NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams),
NewSecParams#security_parameters{
client_random = Random,
compression_algorithm = Compression
@@ -787,13 +874,14 @@ master_secret(Version, MasterSecret, #security_parameters{
client_random = ClientRandom,
server_random = ServerRandom,
hash_size = HashSize,
+ prf_algorithm = PrfAlgo,
key_material_length = KML,
expanded_key_material_length = EKML,
iv_size = IVS},
ConnectionStates, Role) ->
{ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
ServerWriteKey, ClientIV, ServerIV} =
- setup_keys(Version, MasterSecret, ServerRandom,
+ setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom,
ClientRandom, HashSize, KML, EKML, IVS),
ConnStates1 = ssl_record:set_master_secret(MasterSecret, ConnectionStates),
@@ -808,17 +896,21 @@ master_secret(Version, MasterSecret, #security_parameters{
ServerCipherState, Role)}.
-dec_hs(?HELLO_REQUEST, <<>>) ->
+dec_hs(_, ?NEXT_PROTOCOL, <<?BYTE(SelectedProtocolLength), SelectedProtocol:SelectedProtocolLength/binary,
+ ?BYTE(PaddingLength), _Padding:PaddingLength/binary>>) ->
+ #next_protocol{selected_protocol = SelectedProtocol};
+
+dec_hs(_, ?HELLO_REQUEST, <<>>) ->
#hello_request{};
%% Client hello v2.
%% The server must be able to receive such messages, from clients that
%% are willing to use ssl v3 or higher, but have ssl v2 compatibility.
-dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor),
- ?UINT16(CSLength), ?UINT16(0),
- ?UINT16(CDLength),
- CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>) ->
+dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor),
+ ?UINT16(CSLength), ?UINT16(0),
+ ?UINT16(CDLength),
+ CipherSuites:CSLength/binary,
+ ChallengeData:CDLength/binary>>) ->
#client_hello{client_version = {Major, Minor},
random = ssl_ssl2:client_random(ChallengeData, CDLength),
session_id = 0,
@@ -826,24 +918,29 @@ dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor),
compression_methods = [?NULL],
renegotiation_info = undefined
};
-dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
?UINT16(Cs_length), CipherSuites:Cs_length/binary,
?BYTE(Cm_length), Comp_methods:Cm_length/binary,
Extensions/binary>>) ->
-
- RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions),
- undefined),
- #client_hello{
- client_version = {Major,Minor},
- random = Random,
- session_id = Session_ID,
- cipher_suites = from_2bytes(CipherSuites),
- compression_methods = Comp_methods,
- renegotiation_info = RenegotiationInfo
- };
-dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+ DecodedExtensions = dec_hello_extensions(Extensions),
+ RenegotiationInfo = proplists:get_value(renegotiation_info, DecodedExtensions, undefined),
+ HashSigns = proplists:get_value(hash_signs, DecodedExtensions, undefined),
+ NextProtocolNegotiation = proplists:get_value(next_protocol_negotiation, DecodedExtensions, undefined),
+
+ #client_hello{
+ client_version = {Major,Minor},
+ random = Random,
+ session_id = Session_ID,
+ cipher_suites = from_2bytes(CipherSuites),
+ compression_methods = Comp_methods,
+ renegotiation_info = RenegotiationInfo,
+ hash_signs = HashSigns,
+ next_protocol_negotiation = NextProtocolNegotiation
+ };
+
+dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
Cipher_suite:2/binary, ?BYTE(Comp_method)>>) ->
#server_hello{
@@ -852,53 +949,84 @@ dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
session_id = Session_ID,
cipher_suite = Cipher_suite,
compression_method = Comp_method,
- renegotiation_info = undefined};
+ renegotiation_info = undefined,
+ hash_signs = undefined};
-dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
- Cipher_suite:2/binary, ?BYTE(Comp_method),
+ Cipher_suite:2/binary, ?BYTE(Comp_method),
?UINT16(ExtLen), Extensions:ExtLen/binary>>) ->
- RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions, []),
- undefined),
+ HelloExtensions = dec_hello_extensions(Extensions, []),
+ RenegotiationInfo = proplists:get_value(renegotiation_info, HelloExtensions,
+ undefined),
+ HashSigns = proplists:get_value(hash_signs, HelloExtensions,
+ undefined),
+ NextProtocolNegotiation = proplists:get_value(next_protocol_negotiation, HelloExtensions, undefined),
+
#server_hello{
server_version = {Major,Minor},
random = Random,
session_id = Session_ID,
cipher_suite = Cipher_suite,
compression_method = Comp_method,
- renegotiation_info = RenegotiationInfo};
-dec_hs(?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) ->
+ renegotiation_info = RenegotiationInfo,
+ hash_signs = HashSigns,
+ next_protocol_negotiation = NextProtocolNegotiation};
+dec_hs(_Version, ?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) ->
#certificate{asn1_certificates = certs_to_list(ASN1Certs)};
-dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
+dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
?UINT16(GLen), G:GLen/binary,
?UINT16(YLen), Y:YLen/binary,
?UINT16(0)>>) -> %% May happen if key_algorithm is dh_anon
#server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
dh_y = Y},
- signed_params = <<>>};
-dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
+ signed_params = <<>>, hashsign = {null, anon}};
+dec_hs({Major, Minor}, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
+ ?UINT16(GLen), G:GLen/binary,
+ ?UINT16(YLen), Y:YLen/binary,
+ ?BYTE(HashAlgo), ?BYTE(SignAlgo),
+ ?UINT16(Len), Sig:Len/binary>>)
+ when Major == 3, Minor >= 3 ->
+ #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
+ dh_y = Y},
+ signed_params = Sig,
+ hashsign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}};
+dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
?UINT16(GLen), G:GLen/binary,
?UINT16(YLen), Y:YLen/binary,
?UINT16(Len), Sig:Len/binary>>) ->
#server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
dh_y = Y},
- signed_params = Sig};
-dec_hs(?CERTIFICATE_REQUEST,
+ signed_params = Sig, hashsign = undefined};
+dec_hs({Major, Minor}, ?CERTIFICATE_REQUEST,
+ <<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary,
+ ?UINT16(HashSignsLen), HashSigns:HashSignsLen/binary,
+ ?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>)
+ when Major == 3, Minor >= 3 ->
+ HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} ||
+ <<?BYTE(Hash), ?BYTE(Sign)>> <= HashSigns],
+ #certificate_request{certificate_types = CertTypes,
+ hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos},
+ certificate_authorities = CertAuths};
+dec_hs(_Version, ?CERTIFICATE_REQUEST,
<<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary,
?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>) ->
#certificate_request{certificate_types = CertTypes,
certificate_authorities = CertAuths};
-dec_hs(?SERVER_HELLO_DONE, <<>>) ->
+dec_hs(_Version, ?SERVER_HELLO_DONE, <<>>) ->
#server_hello_done{};
-dec_hs(?CERTIFICATE_VERIFY,<<?UINT16(_), Signature/binary>>)->
+dec_hs({Major, Minor}, ?CERTIFICATE_VERIFY,<<HashSign:2/binary, ?UINT16(SignLen), Signature:SignLen/binary>>)
+ when Major == 3, Minor >= 3 ->
+ #certificate_verify{hashsign_algorithm = hashsign_dec(HashSign), signature = Signature};
+dec_hs(_Version, ?CERTIFICATE_VERIFY,<<?UINT16(SignLen), Signature:SignLen/binary>>)->
#certificate_verify{signature = Signature};
-dec_hs(?CLIENT_KEY_EXCHANGE, PKEPMS) ->
+dec_hs(_Version, ?CLIENT_KEY_EXCHANGE, PKEPMS) ->
#client_key_exchange{exchange_keys = PKEPMS};
-dec_hs(?FINISHED, VerifyData) ->
+dec_hs(_Version, ?FINISHED, VerifyData) ->
#finished{verify_data = VerifyData};
-dec_hs(_, _) ->
+dec_hs(_, _, _) ->
throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
dec_client_key(PKEPMS, ?KEY_EXCHANGE_RSA, {3, 0}) ->
@@ -920,6 +1048,9 @@ dec_hello_extensions(_) ->
dec_hello_extensions(<<>>, Acc) ->
Acc;
+dec_hello_extensions(<<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), ExtensionData:Len/binary, Rest/binary>>, Acc) ->
+ Prop = {next_protocol_negotiation, #next_protocol_negotiation{extension_data = ExtensionData}},
+ dec_hello_extensions(Rest, [Prop | Acc]);
dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binary, Rest/binary>>, Acc) ->
RenegotiateInfo = case Len of
1 -> % Initial handshake
@@ -932,8 +1063,18 @@ dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binar
dec_hello_extensions(Rest, [{renegotiation_info,
#renegotiation_info{renegotiated_connection = RenegotiateInfo}} | Acc]);
+dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len),
+ ExtData:Len/binary, Rest/binary>>, Acc) ->
+ SignAlgoListLen = Len - 2,
+ <<?UINT16(SignAlgoListLen), SignAlgoList/binary>> = ExtData,
+ HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} ||
+ <<?BYTE(Hash), ?BYTE(Sign)>> <= SignAlgoList],
+ dec_hello_extensions(Rest, [{hash_signs,
+ #hash_sign_algos{hash_sign_algos = HashSignAlgos}} | Acc]);
+
%% Ignore data following the ClientHello (i.e.,
%% extensions) if not understood.
+
dec_hello_extensions(<<?UINT16(_), ?UINT16(Len), _Unknown:Len/binary, Rest/binary>>, Acc) ->
dec_hello_extensions(Rest, Acc);
%% This theoretically should not happen if the protocol is followed, but if it does it is ignored.
@@ -966,6 +1107,11 @@ certs_from_list(ACList) ->
<<?UINT24(CertLen), Cert/binary>>
end || Cert <- ACList]).
+enc_hs(#next_protocol{selected_protocol = SelectedProtocol}, _Version) ->
+ PaddingLength = 32 - ((byte_size(SelectedProtocol) + 2) rem 32),
+
+ {?NEXT_PROTOCOL, <<?BYTE((byte_size(SelectedProtocol))), SelectedProtocol/binary,
+ ?BYTE(PaddingLength), 0:(PaddingLength * 8)>>};
enc_hs(#hello_request{}, _Version) ->
{?HELLO_REQUEST, <<>>};
enc_hs(#client_hello{client_version = {Major, Minor},
@@ -973,15 +1119,22 @@ enc_hs(#client_hello{client_version = {Major, Minor},
session_id = SessionID,
cipher_suites = CipherSuites,
compression_methods = CompMethods,
- renegotiation_info = RenegotiationInfo}, _Version) ->
+ renegotiation_info = RenegotiationInfo,
+ hash_signs = HashSigns,
+ next_protocol_negotiation = NextProtocolNegotiation}, _Version) ->
SIDLength = byte_size(SessionID),
BinCompMethods = list_to_binary(CompMethods),
CmLength = byte_size(BinCompMethods),
BinCipherSuites = list_to_binary(CipherSuites),
CsLength = byte_size(BinCipherSuites),
- Extensions = hello_extensions(RenegotiationInfo),
- ExtensionsBin = enc_hello_extensions(Extensions),
- {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+ Extensions0 = hello_extensions(RenegotiationInfo, NextProtocolNegotiation),
+ Extensions1 = if
+ Major == 3, Minor >=3 -> Extensions0 ++ hello_extensions(HashSigns);
+ true -> Extensions0
+ end,
+ ExtensionsBin = enc_hello_extensions(Extensions1),
+
+ {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SIDLength), SessionID/binary,
?UINT16(CsLength), BinCipherSuites/binary,
?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
@@ -991,9 +1144,10 @@ enc_hs(#server_hello{server_version = {Major, Minor},
session_id = Session_ID,
cipher_suite = Cipher_suite,
compression_method = Comp_method,
- renegotiation_info = RenegotiationInfo}, _Version) ->
+ renegotiation_info = RenegotiationInfo,
+ next_protocol_negotiation = NextProtocolNegotiation}, _Version) ->
SID_length = byte_size(Session_ID),
- Extensions = hello_extensions(RenegotiationInfo),
+ Extensions = hello_extensions(RenegotiationInfo, NextProtocolNegotiation),
ExtensionsBin = enc_hello_extensions(Extensions),
{?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID/binary,
@@ -1004,15 +1158,30 @@ enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version) ->
{?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>};
enc_hs(#server_key_exchange{params = #server_dh_params{
dh_p = P, dh_g = G, dh_y = Y},
- signed_params = SignedParams}, _Version) ->
+ signed_params = SignedParams, hashsign = HashSign}, Version) ->
PLen = byte_size(P),
GLen = byte_size(G),
YLen = byte_size(Y),
- SignedLen = byte_size(SignedParams),
+ Signature = enc_sign(HashSign, SignedParams, Version),
{?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P/binary,
?UINT16(GLen), G/binary,
?UINT16(YLen), Y/binary,
- ?UINT16(SignedLen), SignedParams/binary>>
+ Signature/binary>>
+ };
+enc_hs(#certificate_request{certificate_types = CertTypes,
+ hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos},
+ certificate_authorities = CertAuths},
+ {Major, Minor})
+ when Major == 3, Minor >= 3 ->
+ HashSigns= << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> ||
+ {Hash, Sign} <- HashSignAlgos >>,
+ CertTypesLen = byte_size(CertTypes),
+ HashSignsLen = byte_size(HashSigns),
+ CertAuthsLen = byte_size(CertAuths),
+ {?CERTIFICATE_REQUEST,
+ <<?BYTE(CertTypesLen), CertTypes/binary,
+ ?UINT16(HashSignsLen), HashSigns/binary,
+ ?UINT16(CertAuthsLen), CertAuths/binary>>
};
enc_hs(#certificate_request{certificate_types = CertTypes,
certificate_authorities = CertAuths},
@@ -1027,8 +1196,8 @@ enc_hs(#server_hello_done{}, _Version) ->
{?SERVER_HELLO_DONE, <<>>};
enc_hs(#client_key_exchange{exchange_keys = ExchangeKeys}, Version) ->
{?CLIENT_KEY_EXCHANGE, enc_cke(ExchangeKeys, Version)};
-enc_hs(#certificate_verify{signature = BinSig}, _) ->
- EncSig = enc_bin_sig(BinSig),
+enc_hs(#certificate_verify{signature = BinSig, hashsign_algorithm = HashSign}, Version) ->
+ EncSig = enc_sign(HashSign, BinSig, Version),
{?CERTIFICATE_VERIFY, EncSig};
enc_hs(#finished{verify_data = VerifyData}, _Version) ->
{?FINISHED, VerifyData}.
@@ -1042,14 +1211,29 @@ enc_cke(#client_diffie_hellman_public{dh_public = DHPublic}, _) ->
Len = byte_size(DHPublic),
<<?UINT16(Len), DHPublic/binary>>.
-enc_bin_sig(BinSig) ->
- Size = byte_size(BinSig),
- <<?UINT16(Size), BinSig/binary>>.
+enc_sign({HashAlg, SignAlg}, Signature, _Version = {Major, Minor})
+ when Major == 3, Minor >= 3->
+ SignLen = byte_size(Signature),
+ HashSign = hashsign_enc(HashAlg, SignAlg),
+ <<HashSign/binary, ?UINT16(SignLen), Signature/binary>>;
+enc_sign(_HashSign, Sign, _Version) ->
+ SignLen = byte_size(Sign),
+ <<?UINT16(SignLen), Sign/binary>>.
+
+hello_extensions(RenegotiationInfo, NextProtocolNegotiation) ->
+ hello_extensions(RenegotiationInfo) ++ next_protocol_extension(NextProtocolNegotiation).
-%% Renegotiation info, only current extension
+%% Renegotiation info
hello_extensions(#renegotiation_info{renegotiated_connection = undefined}) ->
[];
hello_extensions(#renegotiation_info{} = Info) ->
+ [Info];
+hello_extensions(#hash_sign_algos{} = Info) ->
+ [Info].
+
+next_protocol_extension(undefined) ->
+ [];
+next_protocol_extension(#next_protocol_negotiation{} = Info) ->
[Info].
enc_hello_extensions(Extensions) ->
@@ -1060,6 +1244,9 @@ enc_hello_extensions([], Acc) ->
Size = byte_size(Acc),
<<?UINT16(Size), Acc/binary>>;
+enc_hello_extensions([#next_protocol_negotiation{extension_data = ExtensionData} | Rest], Acc) ->
+ Len = byte_size(ExtensionData),
+ enc_hello_extensions(Rest, <<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), ExtensionData/binary, Acc/binary>>);
enc_hello_extensions([#renegotiation_info{renegotiated_connection = ?byte(0) = Info} | Rest], Acc) ->
Len = byte_size(Info),
enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info/binary, Acc/binary>>);
@@ -1067,8 +1254,22 @@ enc_hello_extensions([#renegotiation_info{renegotiated_connection = ?byte(0) = I
enc_hello_extensions([#renegotiation_info{renegotiated_connection = Info} | Rest], Acc) ->
InfoLen = byte_size(Info),
Len = InfoLen +1,
- enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>).
-
+ enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>);
+
+enc_hello_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) ->
+ SignAlgoList = << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> ||
+ {Hash, Sign} <- HashSignAlgos >>,
+ ListLen = byte_size(SignAlgoList),
+ Len = ListLen + 2,
+ enc_hello_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_EXT),
+ ?UINT16(Len), ?UINT16(ListLen), SignAlgoList/binary, Acc/binary>>).
+
+encode_client_protocol_negotiation(undefined, _) ->
+ undefined;
+encode_client_protocol_negotiation(_, false) ->
+ #next_protocol_negotiation{extension_data = <<>>};
+encode_client_protocol_negotiation(_, _) ->
+ undefined.
from_3bytes(Bin3) ->
from_3bytes(Bin3, []).
@@ -1095,6 +1296,14 @@ certificate_types({KeyExchange, _, _, _})
certificate_types(_) ->
<<?BYTE(?RSA_SIGN)>>.
+hashsign_dec(<<?BYTE(HashAlgo), ?BYTE(SignAlgo)>>) ->
+ {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}.
+
+hashsign_enc(HashAlgo, SignAlgo) ->
+ Hash = ssl_cipher:hash_algorithm(HashAlgo),
+ Sign = ssl_cipher:sign_algorithm(SignAlgo),
+ <<?BYTE(Hash), ?BYTE(Sign)>>.
+
certificate_authorities(CertDbHandle, CertDbRef) ->
Authorities = certificate_authorities_from_db(CertDbHandle, CertDbRef),
Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) ->
@@ -1113,43 +1322,43 @@ certificate_authorities_from_db(CertDbHandle, CertDbRef) ->
[Cert | Acc];
(_, Acc) ->
Acc
- end,
+ end,
ssl_certificate_db:foldl(ConnectionCerts, [], CertDbHandle).
-digitally_signed(Hash, #'RSAPrivateKey'{} = Key) ->
+
+digitally_signed({3, Minor}, Hash, HashAlgo, Key) when Minor >= 3 ->
+ public_key:sign({digest, Hash}, HashAlgo, Key);
+digitally_signed(_Version, Hash, _HashAlgo, #'DSAPrivateKey'{} = Key) ->
+ public_key:sign({digest, Hash}, sha, Key);
+digitally_signed(_Version, Hash, _HashAlgo, #'RSAPrivateKey'{} = Key) ->
public_key:encrypt_private(Hash, Key,
- [{rsa_pad, rsa_pkcs1_padding}]);
-digitally_signed(Hash, #'DSAPrivateKey'{} = Key) ->
- public_key:sign(Hash, none, Key).
-
-calc_master_secret({3,0}, PremasterSecret, ClientRandom, ServerRandom) ->
+ [{rsa_pad, rsa_pkcs1_padding}]).
+
+calc_master_secret({3,0}, _PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) ->
ssl_ssl3:master_secret(PremasterSecret, ClientRandom, ServerRandom);
-calc_master_secret({3,N},PremasterSecret, ClientRandom, ServerRandom)
- when N == 1; N == 2 ->
- ssl_tls1:master_secret(PremasterSecret, ClientRandom, ServerRandom).
+calc_master_secret({3,_}, PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) ->
+ ssl_tls1:master_secret(PrfAlgo, PremasterSecret, ClientRandom, ServerRandom).
-setup_keys({3,0}, MasterSecret,
+setup_keys({3,0}, _PrfAlgo, MasterSecret,
ServerRandom, ClientRandom, HashSize, KML, EKML, IVS) ->
- ssl_ssl3:setup_keys(MasterSecret, ServerRandom,
+ ssl_ssl3:setup_keys(MasterSecret, ServerRandom,
ClientRandom, HashSize, KML, EKML, IVS);
-setup_keys({3,1}, MasterSecret,
+setup_keys({3,N}, PrfAlgo, MasterSecret,
ServerRandom, ClientRandom, HashSize, KML, _EKML, IVS) ->
- ssl_tls1:setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize,
+ ssl_tls1:setup_keys(N, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
KML, IVS).
-calc_finished({3, 0}, Role, MasterSecret, Hashes) ->
- ssl_ssl3:finished(Role, MasterSecret, Hashes);
-calc_finished({3, N}, Role, MasterSecret, Hashes)
- when N == 1; N == 2 ->
- ssl_tls1:finished(Role, MasterSecret, Hashes).
+calc_finished({3, 0}, Role, _PrfAlgo, MasterSecret, Handshake) ->
+ ssl_ssl3:finished(Role, MasterSecret, lists:reverse(Handshake));
+calc_finished({3, N}, Role, PrfAlgo, MasterSecret, Handshake) ->
+ ssl_tls1:finished(Role, N, PrfAlgo, MasterSecret, lists:reverse(Handshake)).
-calc_certificate_verify({3, 0}, MasterSecret, Algorithm, Hashes) ->
- ssl_ssl3:certificate_verify(Algorithm, MasterSecret, Hashes);
-calc_certificate_verify({3, N}, _, Algorithm, Hashes)
- when N == 1; N == 2 ->
- ssl_tls1:certificate_verify(Algorithm, Hashes).
+calc_certificate_verify({3, 0}, HashAlgo, MasterSecret, Handshake) ->
+ ssl_ssl3:certificate_verify(HashAlgo, MasterSecret, lists:reverse(Handshake));
+calc_certificate_verify({3, N}, HashAlgo, _MasterSecret, Handshake) ->
+ ssl_tls1:certificate_verify(HashAlgo, N, lists:reverse(Handshake)).
key_exchange_alg(rsa) ->
?KEY_EXCHANGE_RSA;
@@ -1169,7 +1378,29 @@ apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) ->
{unknown, {SslState, UserState}}
end.
-alg_oid(#'RSAPrivateKey'{}) ->
- ?'rsaEncryption';
-alg_oid(#'DSAPrivateKey'{}) ->
- ?'id-dsa'.
+certificate_verify_rsa(Hashes, sha, Signature, PublicKey, {Major, Minor})
+ when Major == 3, Minor >= 3 ->
+ public_key:verify({digest, Hashes}, sha, Signature, PublicKey);
+certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, {Major, Minor})
+ when Major == 3, Minor >= 3 ->
+ public_key:verify({digest, Hashes}, HashAlgo, Signature, PublicKey);
+certificate_verify_rsa(Hashes, _HashAlgo, Signature, PublicKey, _Version) ->
+ case public_key:decrypt_public(Signature, PublicKey,
+ [{rsa_pad, rsa_pkcs1_padding}]) of
+ Hashes -> true;
+ _ -> false
+ end.
+
+-define(TLSEXT_SIGALG_RSA(MD), {MD, rsa}).
+-define(TLSEXT_SIGALG_DSA(MD), {MD, dsa}).
+
+-define(TLSEXT_SIGALG(MD), ?TLSEXT_SIGALG_RSA(MD)).
+
+default_hash_signs() ->
+ #hash_sign_algos{hash_sign_algos =
+ [?TLSEXT_SIGALG(sha512),
+ ?TLSEXT_SIGALG(sha384),
+ ?TLSEXT_SIGALG(sha256),
+ ?TLSEXT_SIGALG(sha),
+ ?TLSEXT_SIGALG_DSA(sha),
+ ?TLSEXT_SIGALG_RSA(md5)]}.
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index fb0ebac7d1..9af6511d68 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -31,6 +31,15 @@
-type algo_oid() :: ?'rsaEncryption' | ?'id-dsa'.
-type public_key_params() :: #'Dss-Parms'{} | term().
-type public_key_info() :: {algo_oid(), #'RSAPublicKey'{} | integer() , public_key_params()}.
+-type tls_handshake_history() :: {[binary()], [binary()]}.
+
+-define(NO_PROTOCOL, <<>>).
+
+%% Signature algorithms
+-define(ANON, 0).
+-define(RSA, 1).
+-define(DSA, 2).
+-define(ECDSA, 3).
-record(session, {
session_id,
@@ -89,7 +98,9 @@
session_id, % opaque SessionID<0..32>
cipher_suites, % cipher_suites<2..2^16-1>
compression_methods, % compression_methods<1..2^8-1>,
- renegotiation_info
+ renegotiation_info,
+ hash_signs, % supported combinations of hashes/signature algos
+ next_protocol_negotiation = undefined % [binary()]
}).
-record(server_hello, {
@@ -98,7 +109,9 @@
session_id, % opaque SessionID<0..32>
cipher_suite, % cipher_suites
compression_method, % compression_method
- renegotiation_info
+ renegotiation_info,
+ hash_signs, % supported combinations of hashes/signature algos
+ next_protocol_negotiation = undefined % [binary()]
}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -129,7 +142,8 @@
-record(server_key_exchange, {
params, %% #server_rsa_params{} | #server_dh_params{}
- signed_params %% #signature{}
+ signed_params, %% #signature{}
+ hashsign %% term(atom(), atom())
}).
%% enum { anonymous, rsa, dsa } SignatureAlgorithm;
@@ -159,6 +173,7 @@
-record(certificate_request, {
certificate_types, %ClientCertificateType <1..2^8-1>
+ hashsign_algorithms, %%SignatureAndHashAlgorithm <2^16-1>;
certificate_authorities %DistinguishedName <0..2^16-1>
}).
@@ -193,6 +208,7 @@
%%% Certificate verify - RFC 4346 section 7.4.8
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-record(certificate_verify, {
+ hashsign_algorithm,
signature % binary()
}).
@@ -213,6 +229,27 @@
renegotiated_connection
}).
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Signature Algorithms RFC 5746 section 7.4.1.4.1.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-define(SIGNATURE_ALGORITHMS_EXT, 13).
+
+-record(hash_sign_algos, {
+ hash_sign_algos
+ }).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Next Protocol Negotiation
+%% (http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-02)
+%% (http://technotes.googlecode.com/git/nextprotoneg.html)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(NEXTPROTONEG_EXT, 13172).
+-define(NEXT_PROTOCOL, 67).
+-record(next_protocol_negotiation, {extension_data}).
+
+-record(next_protocol, {selected_protocol}).
+
-endif. % -ifdef(ssl_handshake).
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 18cfcdcd68..a5db2dcee7 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -34,7 +34,7 @@
-type host() :: inet:ip_address() | inet:hostname().
-type session_id() :: 0 | binary().
-type tls_version() :: {integer(), integer()}.
--type tls_atom_version() :: sslv3 | tlsv1.
+-type tls_atom_version() :: sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'.
-type certdb_ref() :: reference().
-type db_handle() :: term().
-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | dh_anon.
@@ -69,11 +69,11 @@
-define(TRUE, 0).
-define(FALSE, 1).
--define(DEFAULT_SUPPORTED_VERSIONS, [tlsv1, sslv3]). % TODO: This is temporary
-%-define(DEFAULT_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1, sslv3]).
+-define(DEFAULT_SUPPORTED_VERSIONS, [tlsv1, sslv3]). %% Add 'tlsv1.1' in R16
+-define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1, sslv3]).
-record(ssl_options, {
- versions, % 'tlsv1.1' | tlsv1 | sslv3
+ versions, % 'tlsv1.2' | 'tlsv1.1' | tlsv1 | sslv3
verify, % verify_none | verify_peer
verify_fun, % fun(CertVerifyErrors) -> boolean()
fail_if_no_peer_cert, % boolean()
@@ -106,7 +106,9 @@
% after which ssl_connection will
% go into hibernation
%% This option should only be set to true by inet_tls_dist
- erl_dist = false
+ erl_dist = false,
+ next_protocols_advertised = undefined, %% [binary()],
+ next_protocol_selector = undefined %% fun([binary()]) -> binary())
}).
-record(socket_options,
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index 3e947af2c9..0cf4f2ce33 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -86,7 +86,7 @@ start_link_dist(Opts) ->
%%--------------------------------------------------------------------
-spec connection_init(binary()| {der, list()}, client | server) ->
- {ok, certdb_ref(), db_handle(), db_handle()}.
+ {ok, certdb_ref(), db_handle(), db_handle(), db_handle(), db_handle()}.
%%
%% Description: Do necessary initializations for a new connection.
%%--------------------------------------------------------------------
@@ -191,7 +191,7 @@ init([Name, Opts]) ->
proplists:get_value(session_lifetime, Opts, ?'24H_in_sec'),
CertDb = ssl_certificate_db:create(),
SessionCache = CacheCb:init(proplists:get_value(session_cb_init_args, Opts, [])),
- Timer = erlang:send_after(SessionLifeTime * 1000,
+ Timer = erlang:send_after(SessionLifeTime * 1000 + 5000,
self(), validate_sessions),
erlang:send_after(?CLEAR_PEM_CACHE, self(), clear_pem_cache),
{ok, #state{certificate_db = CertDb,
@@ -325,7 +325,7 @@ handle_info({clean_cert_db, Ref, File},
case ssl_certificate_db:ref_count(Ref, RefDb, 0) of
0 ->
MD5 = crypto:md5(File),
- case ssl_certificate_db:lookup_cached_pem(MD5, PemCache) of
+ case ssl_certificate_db:lookup_cached_pem(PemCache, MD5) of
[{Content, Ref}] ->
ssl_certificate_db:insert(MD5, Content, PemCache);
undefined ->
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 830026c825..8e93ce4634 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -383,6 +383,8 @@ get_tls_records_aux(Data, Acc) ->
%% Description: Creates a protocol version record from a version atom
%% or vice versa.
%%--------------------------------------------------------------------
+protocol_version('tlsv1.2') ->
+ {3, 3};
protocol_version('tlsv1.1') ->
{3, 2};
protocol_version(tlsv1) ->
@@ -391,6 +393,8 @@ protocol_version(sslv3) ->
{3, 0};
protocol_version(sslv2) -> %% Backwards compatibility
{2, 0};
+protocol_version({3, 3}) ->
+ 'tlsv1.2';
protocol_version({3, 2}) ->
'tlsv1.1';
protocol_version({3, 1}) ->
@@ -445,9 +449,9 @@ supported_protocol_versions() ->
end,
case application:get_env(ssl, protocol_version) of
undefined ->
- lists:map(Fun, ?DEFAULT_SUPPORTED_VERSIONS);
+ lists:map(Fun, supported_protocol_versions([]));
{ok, []} ->
- lists:map(Fun, ?DEFAULT_SUPPORTED_VERSIONS);
+ lists:map(Fun, supported_protocol_versions([]));
{ok, Vsns} when is_list(Vsns) ->
Versions = lists:filter(fun is_acceptable_version/1, lists:map(Fun, Vsns)),
supported_protocol_versions(Versions);
@@ -457,7 +461,16 @@ supported_protocol_versions() ->
end.
supported_protocol_versions([]) ->
- ?DEFAULT_SUPPORTED_VERSIONS;
+ Vsns = case sufficient_tlsv1_2_crypto_support() of
+ true ->
+ %%?ALL_SUPPORTED_VERSIONS; %% Add TlS-1.2 as default in R16
+ ?DEFAULT_SUPPORTED_VERSIONS;
+ false ->
+ ?DEFAULT_SUPPORTED_VERSIONS
+ end,
+ application:set_env(ssl, protocol_version, Vsns),
+ Vsns;
+
supported_protocol_versions([_|_] = Vsns) ->
Vsns.
@@ -561,14 +574,14 @@ highest_protocol_version() ->
initial_connection_state(ConnectionEnd) ->
#connection_state{security_parameters =
- initial_security_params(ConnectionEnd),
+ initial_security_params(ConnectionEnd),
sequence_number = 0
}.
initial_security_params(ConnectionEnd) ->
SecParams = #security_parameters{connection_end = ConnectionEnd,
compression_algorithm = ?NULL},
- ssl_cipher:security_parameters(?TLS_NULL_WITH_NULL_NULL,
+ ssl_cipher:security_parameters(highest_protocol_version(), ?TLS_NULL_WITH_NULL_NULL,
SecParams).
empty_connection_state(ConnectionEnd) ->
@@ -633,7 +646,7 @@ cipher(Type, Version, Fragment, CS0) ->
BCA}
}} =
hash_and_bump_seqno(CS0, Type, Version, Length, Fragment),
- {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment),
+ {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment, Version),
CS2 = CS1#connection_state{cipher_state=CipherS1},
{Ciphered, CS2}.
@@ -687,6 +700,17 @@ mac_hash({_,_}, ?NULL, _MacSecret, _SeqNo, _Type,
mac_hash({3, 0}, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
ssl_ssl3:mac_hash(MacAlg, MacSecret, SeqNo, Type, Length, Fragment);
mac_hash({3, N} = Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment)
- when N =:= 1; N =:= 2 ->
+ when N =:= 1; N =:= 2; N =:= 3 ->
ssl_tls1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version,
Length, Fragment).
+
+sufficient_tlsv1_2_crypto_support() ->
+ Data = "Sampl",
+ Data2 = "e #1",
+ Key = <<0,1,2,3,16,17,18,19,32,33,34,35,48,49,50,51,4,5,6,7,20,21,22,23,36,37,38,39,
+ 52,53,54,55,8,9,10,11,24,25,26,27,40,41,42,43,56,57,58,59>>,
+ try
+ crypto:sha256_mac(Key, lists:flatten([Data, Data2])),
+ true
+ catch _:_ -> false
+ end.
diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl
index 282d642138..f73da92a52 100644
--- a/lib/ssl/src/ssl_record.hrl
+++ b/lib/ssl/src/ssl_record.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -47,6 +47,7 @@
key_material_length, % unit 8
expanded_key_material_length, % unit 8
mac_algorithm, % unit 8
+ prf_algorithm, % unit 8
hash_size, % unit 8
compression_algorithm, % unit 8
master_secret, % opaque 48
@@ -97,10 +98,15 @@
%-define(TRUE, 0). %% Already defined by ssl_internal.hrl
%-define(FALSE, 1). %% Already defined by ssl_internal.hrl
-%% MACAlgorithm
+%% MAC and PRF Algorithms
%-define(NULL, 0). %% Already defined by ssl_internal.hrl
-define(MD5, 1).
-define(SHA, 2).
+-define(MD5SHA, 4711). %% Not defined in protocol used to represent old prf
+-define(SHA224, 3).
+-define(SHA256, 4).
+-define(SHA384, 5).
+-define(SHA512, 6).
%% CompressionMethod
% -define(NULL, 0). %% Already defined by ssl_internal.hrl
@@ -176,7 +182,8 @@
content, % opaque content[TLSCompressed.length];
mac, % opaque MAC[CipherSpec.hash_size];
padding, % unit 8 padding[GenericBlockCipher.padding_length];
- padding_length % uint8 padding_length;
+ padding_length, % uint8 padding_length;
+ next_iv % opaque IV[SecurityParameters.record_iv_length];
}).
-endif. % -ifdef(ssl_record).
diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl
index f2926b2d2f..a11c5b8c0c 100644
--- a/lib/ssl/src/ssl_ssl3.erl
+++ b/lib/ssl/src/ssl_ssl3.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -54,9 +54,9 @@ master_secret(PremasterSecret, ClientRandom, ServerRandom) ->
Block = generate_keyblock(PremasterSecret, ClientRandom, ServerRandom, 48),
Block.
--spec finished(client | server, binary(), {binary(), binary()}) -> binary().
+-spec finished(client | server, binary(), [binary()]) -> binary().
-finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
+finished(Role, MasterSecret, Handshake) ->
%% draft-ietf-tls-ssl-version3-00 - 5.6.9 Finished
%% struct {
%% opaque md5_hash[16];
@@ -70,13 +70,13 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
%% SHA(handshake_messages + Sender +
%% master_secret + pad1));
Sender = get_sender(Role),
- MD5 = handshake_hash(?MD5, MasterSecret, Sender, MD5Hash),
- SHA = handshake_hash(?SHA, MasterSecret, Sender, SHAHash),
+ MD5 = handshake_hash(?MD5, MasterSecret, Sender, Handshake),
+ SHA = handshake_hash(?SHA, MasterSecret, Sender, Handshake),
<<MD5/binary, SHA/binary>>.
--spec certificate_verify(OID::tuple(), binary(), {binary(), binary()}) -> binary().
+-spec certificate_verify(md5sha | sha, binary(), [binary()]) -> binary().
-certificate_verify(?'rsaEncryption', MasterSecret, {MD5Hash, SHAHash}) ->
+certificate_verify(md5sha, MasterSecret, Handshake) ->
%% md5_hash
%% MD5(master_secret + pad_2 +
%% MD5(handshake_messages + master_secret + pad_1));
@@ -84,15 +84,16 @@ certificate_verify(?'rsaEncryption', MasterSecret, {MD5Hash, SHAHash}) ->
%% SHA(master_secret + pad_2 +
%% SHA(handshake_messages + master_secret + pad_1));
- MD5 = handshake_hash(?MD5, MasterSecret, undefined, MD5Hash),
- SHA = handshake_hash(?SHA, MasterSecret, undefined, SHAHash),
+ MD5 = handshake_hash(?MD5, MasterSecret, undefined, Handshake),
+ SHA = handshake_hash(?SHA, MasterSecret, undefined, Handshake),
<<MD5/binary, SHA/binary>>;
-certificate_verify(?'id-dsa', MasterSecret, {_, SHAHash}) ->
+certificate_verify(sha, MasterSecret, Handshake) ->
%% sha_hash
%% SHA(master_secret + pad_2 +
%% SHA(handshake_messages + master_secret + pad_1));
- handshake_hash(?SHA, MasterSecret, undefined, SHAHash).
+
+ handshake_hash(?SHA, MasterSecret, undefined, Handshake).
-spec mac_hash(integer(), binary(), integer(), integer(), integer(), binary()) -> binary().
@@ -152,28 +153,17 @@ suites() ->
%%% Internal functions
%%--------------------------------------------------------------------
-hash(?MD5, Data) ->
+hash(?MD5, Data) ->
crypto:md5(Data);
-hash(?SHA, Data) ->
+hash(?SHA, Data) ->
crypto:sha(Data).
-hash_update(?MD5, Context, Data) ->
- crypto:md5_update(Context, Data);
-hash_update(?SHA, Context, Data) ->
- crypto:sha_update(Context, Data).
-
-hash_final(?MD5, Context) ->
- crypto:md5_final(Context);
-hash_final(?SHA, Context) ->
- crypto:sha_final(Context).
-
%%pad_1(?NULL) ->
%% "";
pad_1(?MD5) ->
<<"666666666666666666666666666666666666666666666666">>;
pad_1(?SHA) ->
<<"6666666666666666666666666666666666666666">>.
-
%%pad_2(?NULL) ->
%% "";
pad_2(?MD5) ->
@@ -189,19 +179,11 @@ mac_hash(Method, Secret, Data) ->
InnerHash = hash(Method, [Secret, pad_1(Method), Data]),
hash(Method, [Secret, pad_2(Method), InnerHash]).
-handshake_hash(Method, HandshakeHash, Extra) ->
- HSH = hash_update(Method, HandshakeHash, Extra),
- hash_final(Method, HSH).
-
-handshake_hash(Method, MasterSecret, undefined, HandshakeHash) ->
- InnerHash =
- handshake_hash(Method, HandshakeHash,
- [MasterSecret, pad_1(Method)]),
+handshake_hash(Method, MasterSecret, undefined, Handshake) ->
+ InnerHash = hash(Method, [Handshake, MasterSecret, pad_1(Method)]),
hash(Method, [MasterSecret, pad_2(Method), InnerHash]);
-handshake_hash(Method, MasterSecret, Sender, HandshakeHash) ->
- InnerHash =
- handshake_hash(Method, HandshakeHash,
- [Sender, MasterSecret, pad_1(Method)]),
+handshake_hash(Method, MasterSecret, Sender, Handshake) ->
+ InnerHash = hash(Method, [Handshake, Sender, MasterSecret, pad_1(Method)]),
hash(Method, [MasterSecret, pad_2(Method), InnerHash]).
get_sender(client) -> "CLNT";
diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl
index c8aae34892..41dc1bf0dc 100644
--- a/lib/ssl/src/ssl_tls1.erl
+++ b/lib/ssl/src/ssl_tls1.erl
@@ -26,27 +26,29 @@
-include("ssl_cipher.hrl").
-include("ssl_internal.hrl").
--include("ssl_record.hrl").
+-include("ssl_record.hrl").
--export([master_secret/3, finished/3, certificate_verify/2, mac_hash/7,
- setup_keys/6, suites/0, prf/4]).
+-export([master_secret/4, finished/5, certificate_verify/3, mac_hash/7,
+ setup_keys/8, suites/1, prf/5]).
%%====================================================================
%% Internal application API
%%====================================================================
--spec master_secret(binary(), binary(), binary()) -> binary().
+-spec master_secret(integer(), binary(), binary(), binary()) -> binary().
-master_secret(PreMasterSecret, ClientRandom, ServerRandom) ->
- %% RFC 2246 & 4346 - 8.1 %% master_secret = PRF(pre_master_secret,
- %% "master secret", ClientHello.random +
- %% ServerHello.random)[0..47];
- prf(PreMasterSecret, <<"master secret">>,
+master_secret(PrfAlgo, PreMasterSecret, ClientRandom, ServerRandom) ->
+ %% RFC 2246 & 4346 && RFC 5246 - 8.1 %% master_secret = PRF(pre_master_secret,
+ %% "master secret", ClientHello.random +
+ %% ServerHello.random)[0..47];
+
+ prf(PrfAlgo, PreMasterSecret, <<"master secret">>,
[ClientRandom, ServerRandom], 48).
--spec finished(client | server, binary(), {binary(), binary()}) -> binary().
+-spec finished(client | server, integer(), integer(), binary(), [binary()]) -> binary().
-finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
+finished(Role, Version, PrfAlgo, MasterSecret, Handshake)
+ when Version == 1; Version == 2; PrfAlgo == ?MD5SHA ->
%% RFC 2246 & 4346 - 7.4.9. Finished
%% struct {
%% opaque verify_data[12];
@@ -55,26 +57,39 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
%% verify_data
%% PRF(master_secret, finished_label, MD5(handshake_messages) +
%% SHA-1(handshake_messages)) [0..11];
- MD5 = hash_final(?MD5, MD5Hash),
- SHA = hash_final(?SHA, SHAHash),
- prf(MasterSecret, finished_label(Role), [MD5, SHA], 12).
+ MD5 = crypto:md5(Handshake),
+ SHA = crypto:sha(Handshake),
+ prf(?MD5SHA, MasterSecret, finished_label(Role), [MD5, SHA], 12);
+
+finished(Role, Version, PrfAlgo, MasterSecret, Handshake)
+ when Version == 3 ->
+ %% RFC 5246 - 7.4.9. Finished
+ %% struct {
+ %% opaque verify_data[12];
+ %% } Finished;
+ %%
+ %% verify_data
+ %% PRF(master_secret, finished_label, Hash(handshake_messages)) [0..11];
+ Hash = crypto:hash(mac_algo(PrfAlgo), Handshake),
+ prf(PrfAlgo, MasterSecret, finished_label(Role), Hash, 12).
--spec certificate_verify(OID::tuple(), {binary(), binary()}) -> binary().
+-spec certificate_verify(md5sha | sha, integer(), [binary()]) -> binary().
-certificate_verify(?'rsaEncryption', {MD5Hash, SHAHash}) ->
- MD5 = hash_final(?MD5, MD5Hash),
- SHA = hash_final(?SHA, SHAHash),
+certificate_verify(md5sha, _Version, Handshake) ->
+ MD5 = crypto:md5(Handshake),
+ SHA = crypto:sha(Handshake),
<<MD5/binary, SHA/binary>>;
-certificate_verify(?'id-dsa', {_, SHAHash}) ->
- hash_final(?SHA, SHAHash).
+certificate_verify(HashAlgo, _Version, Handshake) ->
+ crypto:hash(HashAlgo, Handshake).
--spec setup_keys(binary(), binary(), binary(), integer(),
- integer(), integer()) -> {binary(), binary(), binary(),
+-spec setup_keys(integer(), integer(), binary(), binary(), binary(), integer(),
+ integer(), integer()) -> {binary(), binary(), binary(),
binary(), binary(), binary()}.
-setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize,
- KeyMatLen, IVSize) ->
+setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
+ KeyMatLen, IVSize)
+ when Version == 1 ->
%% RFC 2246 - 6.3. Key calculation
%% key_block = PRF(SecurityParameters.master_secret,
%% "key expansion",
@@ -88,36 +103,67 @@ setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize,
%% client_write_IV[SecurityParameters.IV_size]
%% server_write_IV[SecurityParameters.IV_size]
WantedLength = 2 * (HashSize + KeyMatLen + IVSize),
- KeyBlock = prf(MasterSecret, "key expansion",
+ KeyBlock = prf(?MD5SHA, MasterSecret, "key expansion",
[ServerRandom, ClientRandom], WantedLength),
<<ClientWriteMacSecret:HashSize/binary,
ServerWriteMacSecret:HashSize/binary,
ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary,
ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock,
{ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
- ServerWriteKey, ClientIV, ServerIV}.
+ ServerWriteKey, ClientIV, ServerIV};
+
+%% TLS v1.1
+setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
+ KeyMatLen, IVSize)
+ when Version == 2 ->
+ %% RFC 4346 - 6.3. Key calculation
+ %% key_block = PRF(SecurityParameters.master_secret,
+ %% "key expansion",
+ %% SecurityParameters.server_random +
+ %% SecurityParameters.client_random);
+ %% Then the key_block is partitioned as follows:
+ %% client_write_MAC_secret[SecurityParameters.hash_size]
+ %% server_write_MAC_secret[SecurityParameters.hash_size]
+ %% client_write_key[SecurityParameters.key_material_length]
+ %% server_write_key[SecurityParameters.key_material_length]
+ %%
+ %% RFC 4346 is incomplete, the client and server IVs have to
+ %% be generated just like for TLS 1.0
+ WantedLength = 2 * (HashSize + KeyMatLen + IVSize),
+ KeyBlock = prf(?MD5SHA, MasterSecret, "key expansion",
+ [ServerRandom, ClientRandom], WantedLength),
+ <<ClientWriteMacSecret:HashSize/binary,
+ ServerWriteMacSecret:HashSize/binary,
+ ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary,
+ ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock,
+ {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
+ ServerWriteKey, ClientIV, ServerIV};
-%% TLS v1.1 uncomment when supported.
-%% setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, KeyMatLen) ->
-%% %% RFC 4346 - 6.3. Key calculation
-%% %% key_block = PRF(SecurityParameters.master_secret,
-%% %% "key expansion",
-%% %% SecurityParameters.server_random +
-%% %% SecurityParameters.client_random);
-%% %% Then the key_block is partitioned as follows:
-%% %% client_write_MAC_secret[SecurityParameters.hash_size]
-%% %% server_write_MAC_secret[SecurityParameters.hash_size]
-%% %% client_write_key[SecurityParameters.key_material_length]
-%% %% server_write_key[SecurityParameters.key_material_length]
-%% WantedLength = 2 * (HashSize + KeyMatLen),
-%% KeyBlock = prf(MasterSecret, "key expansion",
-%% [ServerRandom, ClientRandom], WantedLength),
-%% <<ClientWriteMacSecret:HashSize/binary,
-%% ServerWriteMacSecret:HashSize/binary,
-%% ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary>>
-%% = KeyBlock,
-%% {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
-%% ServerWriteKey, undefined, undefined}.
+%% TLS v1.2
+setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
+ KeyMatLen, IVSize)
+ when Version == 3 ->
+ %% RFC 5246 - 6.3. Key calculation
+ %% key_block = PRF(SecurityParameters.master_secret,
+ %% "key expansion",
+ %% SecurityParameters.server_random +
+ %% SecurityParameters.client_random);
+ %% Then the key_block is partitioned as follows:
+ %% client_write_MAC_secret[SecurityParameters.hash_size]
+ %% server_write_MAC_secret[SecurityParameters.hash_size]
+ %% client_write_key[SecurityParameters.key_material_length]
+ %% server_write_key[SecurityParameters.key_material_length]
+ %% client_write_IV[SecurityParameters.fixed_iv_length]
+ %% server_write_IV[SecurityParameters.fixed_iv_length]
+ WantedLength = 2 * (HashSize + KeyMatLen + IVSize),
+ KeyBlock = prf(PrfAlgo, MasterSecret, "key expansion",
+ [ServerRandom, ClientRandom], WantedLength),
+ <<ClientWriteMacSecret:HashSize/binary,
+ ServerWriteMacSecret:HashSize/binary,
+ ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary,
+ ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock,
+ {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
+ ServerWriteKey, ClientIV, ServerIV}.
-spec mac_hash(integer(), binary(), integer(), integer(), tls_version(),
integer(), binary()) -> binary().
@@ -134,9 +180,9 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor},
Fragment]),
Mac.
--spec suites() -> [cipher_suite()].
+-spec suites(1|2|3) -> [cipher_suite()].
-suites() ->
+suites(Minor) when Minor == 1; Minor == 2->
[
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
@@ -152,7 +198,19 @@ suites() ->
?TLS_RSA_WITH_RC4_128_MD5,
?TLS_DHE_RSA_WITH_DES_CBC_SHA,
?TLS_RSA_WITH_DES_CBC_SHA
- ].
+ ];
+
+suites(Minor) when Minor == 3 ->
+ [
+ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
+ ?TLS_RSA_WITH_AES_256_CBC_SHA256,
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
+ ?TLS_RSA_WITH_AES_128_CBC_SHA256
+ %% ?TLS_DH_anon_WITH_AES_128_CBC_SHA256,
+ %% ?TLS_DH_anon_WITH_AES_256_CBC_SHA256
+ ] ++ suites(2).
%%--------------------------------------------------------------------
%%% Internal functions
@@ -163,7 +221,19 @@ hmac_hash(?NULL, _, _) ->
hmac_hash(?MD5, Key, Value) ->
crypto:md5_mac(Key, Value);
hmac_hash(?SHA, Key, Value) ->
- crypto:sha_mac(Key, Value).
+ crypto:sha_mac(Key, Value);
+hmac_hash(?SHA256, Key, Value) ->
+ crypto:sha256_mac(Key, Value);
+hmac_hash(?SHA384, Key, Value) ->
+ crypto:sha384_mac(Key, Value);
+hmac_hash(?SHA512, Key, Value) ->
+ crypto:sha512_mac(Key, Value).
+
+mac_algo(?MD5) -> md5;
+mac_algo(?SHA) -> sha;
+mac_algo(?SHA256) -> sha256;
+mac_algo(?SHA384) -> sha384;
+mac_algo(?SHA512) -> sha512.
% First, we define a data expansion function, P_hash(secret, data) that
% uses a single hash function to expand a secret and seed into an
@@ -182,7 +252,7 @@ p_hash(_Secret, _Seed, WantedLength, _Method, _N, [Last | Acc])
when WantedLength =< 0 ->
Keep = byte_size(Last) + WantedLength,
<<B:Keep/binary, _/binary>> = Last,
- lists:reverse(Acc, [B]);
+ list_to_binary(lists:reverse(Acc, [B]));
p_hash(Secret, Seed, WantedLength, Method, N, Acc) ->
N1 = N+1,
Bin = hmac_hash(Method, Secret, [a(N1, Secret, Seed, Method), Seed]),
@@ -214,13 +284,18 @@ split_secret(BinSecret) ->
<<_:Div/binary, Secret2:EvenLength/binary>> = BinSecret,
{Secret1, Secret2}.
-prf(Secret, Label, Seed, WantedLength) ->
+prf(?MD5SHA, Secret, Label, Seed, WantedLength) ->
%% PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR
%% P_SHA-1(S2, label + seed);
{S1, S2} = split_secret(Secret),
LS = list_to_binary([Label, Seed]),
crypto:exor(p_hash(S1, LS, WantedLength, ?MD5),
- p_hash(S2, LS, WantedLength, ?SHA)).
+ p_hash(S2, LS, WantedLength, ?SHA));
+
+prf(MAC, Secret, Label, Seed, WantedLength) ->
+ %% PRF(secret, label, seed) = P_SHA256(secret, label + seed);
+ LS = list_to_binary([Label, Seed]),
+ p_hash(Secret, LS, WantedLength, MAC).
%%%% Misc help functions %%%%
@@ -228,8 +303,3 @@ finished_label(client) ->
<<"client finished">>;
finished_label(server) ->
<<"server finished">>.
-
-hash_final(?MD5, Conntext) ->
- crypto:md5_final(Conntext);
-hash_final(?SHA, Conntext) ->
- crypto:sha_final(Conntext).
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index a0f54c0359..d36dcb588b 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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
@@ -44,6 +44,8 @@ MODULES = \
ssl_to_openssl_SUITE \
ssl_session_cache_SUITE \
ssl_dist_SUITE \
+ ssl_npn_hello_SUITE \
+ ssl_npn_handshake_SUITE \
make_certs\
erl_make_certs
diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
index 693289990c..4603a9f846 100644
--- a/lib/ssl/test/make_certs.erl
+++ b/lib/ssl/test/make_certs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -121,7 +121,19 @@ create_self_signed_cert(Root, OpenSSLCmd, CAName, Cnf) ->
" -keyout ", KeyFile,
" -out ", CertFile],
Env = [{"ROOTDIR", Root}],
- cmd(Cmd, Env).
+ cmd(Cmd, Env),
+ fix_key_file(OpenSSLCmd, KeyFile).
+
+% openssl 1.0 generates key files in pkcs8 format by default and we don't handle this format
+fix_key_file(OpenSSLCmd, KeyFile) ->
+ KeyFileTmp = KeyFile ++ ".tmp",
+ Cmd = [OpenSSLCmd, " rsa",
+ " -in ",
+ KeyFile,
+ " -out ",
+ KeyFileTmp],
+ cmd(Cmd, []),
+ ok = file:rename(KeyFileTmp, KeyFile).
create_ca_dir(Root, CAName, Cnf) ->
CARoot = filename:join([Root, CAName]),
@@ -139,7 +151,8 @@ create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile) ->
" -keyout ", KeyFile,
" -out ", ReqFile],
Env = [{"ROOTDIR", Root}],
- cmd(Cmd, Env).
+ cmd(Cmd, Env),
+ fix_key_file(OpenSSLCmd, KeyFile).
sign_req(Root, OpenSSLCmd, CA, CertType, ReqFile, CertFile) ->
CACnfFile = filename:join([Root, CA, "ca.cnf"]),
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 5a52917d6c..6cf712fa6f 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -27,9 +27,11 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("public_key/include/public_key.hrl").
+-include("ssl_internal.hrl").
-include("ssl_alert.hrl").
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
+-include("ssl_handshake.hrl").
-define('24H_in_sec', 86400).
-define(TIMEOUT, 60000).
@@ -37,6 +39,7 @@
-define(EXPIRE, 10).
-define(SLEEP, 500).
-define(RENEGOTIATION_DISABLE_TIME, 12000).
+-define(CLEAN_SESSION_DB, 60000).
%% Test server callback functions
%%--------------------------------------------------------------------
@@ -54,7 +57,6 @@ init_per_suite(Config0) ->
try crypto:start() of
ok ->
application:start(public_key),
- ssl:start(),
%% make rsa certs using oppenssl
Result =
@@ -91,44 +93,26 @@ end_per_suite(_Config) ->
%% variable, but should NOT alter/remove any existing entries.
%% Description: Initialization before each test case
%%--------------------------------------------------------------------
-init_per_testcase(session_cache_process_list, Config) ->
- init_customized_session_cache(list, Config);
-
-init_per_testcase(session_cache_process_mnesia, Config) ->
- mnesia:start(),
- init_customized_session_cache(mnesia, Config);
-
-init_per_testcase(reuse_session_expired, Config0) ->
- Config = lists:keydelete(watchdog, 1, Config0),
- Dog = ssl_test_lib:timetrap(?EXPIRE * 1000 * 5),
- ssl:stop(),
- application:load(ssl),
- application:set_env(ssl, session_lifetime, ?EXPIRE),
- ssl:start(),
- [{watchdog, Dog} | Config];
-
init_per_testcase(no_authority_key_identifier, Config) ->
%% Clear cach so that root cert will not
%% be found.
- ssl:stop(),
- ssl:start(),
+ ssl:clear_pem_cache(),
Config;
-init_per_testcase(TestCase, Config) when TestCase == ciphers_rsa_signed_certs_ssl3;
- TestCase == ciphers_rsa_signed_certs_openssl_names_ssl3;
- TestCase == ciphers_dsa_signed_certs_ssl3;
- TestCase == ciphers_dsa_signed_certs_openssl_names_ssl3 ->
+init_per_testcase(protocol_versions, Config) ->
ssl:stop(),
application:load(ssl),
- application:set_env(ssl, protocol_version, sslv3),
+ %% For backwards compatibility sslv2 should be filtered out.
+ application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]),
ssl:start(),
Config;
-init_per_testcase(protocol_versions, Config) ->
+init_per_testcase(reuse_session_expired, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
ssl:stop(),
application:load(ssl),
- %% For backwards compatibility sslv2 should be filtered out.
- application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]),
+ application:set_env(ssl, session_lifetime, ?EXPIRE),
+ application:set_env(ssl, session_delay_cleanup_time, 500),
ssl:start(),
Config;
@@ -139,24 +123,15 @@ init_per_testcase(empty_protocol_versions, Config) ->
ssl:start(),
Config;
-init_per_testcase(different_ca_peer_sign, Config0) ->
- ssl_test_lib:make_mix_cert(Config0);
+%% init_per_testcase(different_ca_peer_sign, Config0) ->
+%% ssl_test_lib:make_mix_cert(Config0);
init_per_testcase(_TestCase, Config0) ->
+ test_server:format("TLS/SSL version ~p~n ", [ssl_record:supported_protocol_versions()]),
Config = lists:keydelete(watchdog, 1, Config0),
Dog = test_server:timetrap(?TIMEOUT),
[{watchdog, Dog} | Config].
-init_customized_session_cache(Type, Config0) ->
- Config = lists:keydelete(watchdog, 1, Config0),
- Dog = test_server:timetrap(?TIMEOUT),
- ssl:stop(),
- application:load(ssl),
- application:set_env(ssl, session_cb, ?MODULE),
- application:set_env(ssl, session_cb_init_args, [Type]),
- ssl:start(),
- [{watchdog, Dog} | Config].
-
%%--------------------------------------------------------------------
%% Function: end_per_testcase(TestCase, Config) -> _
%% Case - atom()
@@ -165,27 +140,11 @@ init_customized_session_cache(Type, Config0) ->
%% A list of key/value pairs, holding the test case configuration.
%% Description: Cleanup after each test case
%%--------------------------------------------------------------------
-end_per_testcase(session_cache_process_list, Config) ->
- application:unset_env(ssl, session_cb),
- end_per_testcase(default_action, Config);
-end_per_testcase(session_cache_process_mnesia, Config) ->
- application:unset_env(ssl, session_cb),
- application:unset_env(ssl, session_cb_init_args),
- mnesia:stop(),
- ssl:stop(),
- ssl:start(),
- end_per_testcase(default_action, Config);
end_per_testcase(reuse_session_expired, Config) ->
application:unset_env(ssl, session_lifetime),
+ application:unset_env(ssl, session_delay_cleanup_time),
end_per_testcase(default_action, Config);
-end_per_testcase(TestCase, Config) when TestCase == ciphers_rsa_signed_certs_ssl3;
- TestCase == ciphers_rsa_signed_certs_openssl_names_ssl3;
- TestCase == ciphers_dsa_signed_certs_ssl3;
- TestCase == ciphers_dsa_signed_certs_openssl_names_ssl3;
- TestCase == protocol_versions;
- TestCase == empty_protocol_versions->
- application:unset_env(ssl, protocol_version),
- end_per_testcase(default_action, Config);
+
end_per_testcase(_TestCase, Config) ->
Dog = ?config(watchdog, Config),
case Dog of
@@ -206,74 +165,170 @@ end_per_testcase(_TestCase, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app, alerts, connection_info, protocol_versions,
- empty_protocol_versions, controlling_process,
- controller_dies, client_closes_socket,
- connect_dist, peername, peercert, sockname, socket_options,
- invalid_inet_get_option, invalid_inet_get_option_not_list,
+ [
+ {group, basic},
+ {group, options},
+ {group, session},
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}
+ ].
+
+groups() ->
+ [{basic, [], basic_tests()},
+ {options, [], options_tests()},
+ {'tlsv1.2', [], all_versions_groups()},
+ {'tlsv1.1', [], all_versions_groups()},
+ {'tlsv1', [], all_versions_groups() ++ rizzo_tests()},
+ {'sslv3', [], all_versions_groups() ++ rizzo_tests()},
+ {api,[], api_tests()},
+ {certificate_verify, [], certificate_verify_tests()},
+ {session, [], session_tests()},
+ {renegotiate, [], renegotiate_tests()},
+ {ciphers, [], cipher_tests()},
+ {error_handling_tests, [], error_handling_tests()}
+ ].
+
+all_versions_groups ()->
+ [{group, api},
+ {group, certificate_verify},
+ {group, renegotiate},
+ {group, ciphers},
+ {group, error_handling_tests}].
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName),
+ Config;
+ false ->
+ {skip, "Missing crypto support"}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
+
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+basic_tests() ->
+ [app,
+ alerts,
+ send_close,
+ connect_twice,
+ connect_dist
+ ].
+
+options_tests() ->
+ [der_input,
+ misc_ssl_options,
+ socket_options,
+ invalid_inet_get_option,
+ invalid_inet_get_option_not_list,
invalid_inet_get_option_improper_list,
- invalid_inet_set_option, invalid_inet_set_option_not_list,
+ invalid_inet_set_option,
+ invalid_inet_set_option_not_list,
invalid_inet_set_option_improper_list,
- misc_ssl_options, versions, cipher_suites, upgrade,
- upgrade_with_timeout, tcp_connect, tcp_connect_big, ipv6, ekeyfile,
- ecertfile, ecacertfile, eoptions, shutdown,
- shutdown_write, shutdown_both, shutdown_error,
- ciphers_rsa_signed_certs, ciphers_rsa_signed_certs_ssl3,
- ciphers_rsa_signed_certs_openssl_names,
- ciphers_rsa_signed_certs_openssl_names_ssl3,
- ciphers_dsa_signed_certs, ciphers_dsa_signed_certs_ssl3,
- ciphers_dsa_signed_certs_openssl_names,
- ciphers_dsa_signed_certs_openssl_names_ssl3,
- anonymous_cipher_suites,
- default_reject_anonymous,
- send_close,
- close_transport_accept, dh_params,
- server_verify_peer_passive, server_verify_peer_active,
+ dh_params,
+ ecertfile,
+ ecacertfile,
+ ekeyfile,
+ eoptions,
+ protocol_versions,
+ empty_protocol_versions,
+ ipv6,
+ reuseaddr].
+
+api_tests() ->
+ [connection_info,
+ peername,
+ peercert,
+ sockname,
+ versions,
+ controlling_process,
+ upgrade,
+ upgrade_with_timeout,
+ shutdown,
+ shutdown_write,
+ shutdown_both,
+ shutdown_error,
+ hibernate
+ ].
+
+certificate_verify_tests() ->
+ [server_verify_peer_passive,
+ server_verify_peer_active,
server_verify_peer_active_once,
- server_verify_none_passive, server_verify_none_active,
+ server_verify_none_passive,
+ server_verify_none_active,
server_verify_none_active_once,
- server_verify_no_cacerts, server_require_peer_cert_ok,
+ server_verify_no_cacerts,
+ server_require_peer_cert_ok,
server_require_peer_cert_fail,
server_verify_client_once_passive,
server_verify_client_once_active,
server_verify_client_once_active_once,
- client_verify_none_passive, client_verify_none_active,
+ client_verify_none_passive,
+ client_verify_none_active,
client_verify_none_active_once,
- reuse_session,
- reuse_session_expired,
- server_does_not_want_to_reuse_session,
- client_renegotiate, server_renegotiate,
- client_renegotiate_reused_session,
- server_renegotiate_reused_session,
- client_no_wrap_sequence_number,
- server_no_wrap_sequence_number, extended_key_usage_verify_peer,
+ extended_key_usage_verify_peer,
extended_key_usage_verify_none,
- no_authority_key_identifier, invalid_signature_client,
- invalid_signature_server, cert_expired,
+ invalid_signature_client,
+ invalid_signature_server,
+ cert_expired,
client_with_cert_cipher_suites_handshake,
verify_fun_always_run_client,
verify_fun_always_run_server,
- unknown_server_ca_fail, der_input,
+ unknown_server_ca_fail,
unknown_server_ca_accept_verify_none,
unknown_server_ca_accept_verify_peer,
unknown_server_ca_accept_backwardscompatibility,
- %%different_ca_peer_sign,
- no_reuses_session_server_restart_new_cert,
- no_reuses_session_server_restart_new_cert_file, reuseaddr,
- hibernate, connect_twice, renegotiate_dos_mitigate_active,
- renegotiate_dos_mitigate_passive,
- tcp_error_propagation_in_active_mode, rizzo, no_rizzo_rc4,
- recv_error_handling
+ no_authority_key_identifier
].
-groups() ->
- [].
+session_tests() ->
+ [reuse_session,
+ reuse_session_expired,
+ server_does_not_want_to_reuse_session,
+ no_reuses_session_server_restart_new_cert,
+ no_reuses_session_server_restart_new_cert_file].
-init_per_group(_GroupName, Config) ->
- Config.
+renegotiate_tests() ->
+ [client_renegotiate,
+ server_renegotiate,
+ client_renegotiate_reused_session,
+ server_renegotiate_reused_session,
+ client_no_wrap_sequence_number,
+ server_no_wrap_sequence_number,
+ renegotiate_dos_mitigate_active,
+ renegotiate_dos_mitigate_passive].
-end_per_group(_GroupName, Config) ->
- Config.
+cipher_tests() ->
+ [cipher_suites,
+ ciphers_rsa_signed_certs,
+ ciphers_rsa_signed_certs_openssl_names,
+ ciphers_dsa_signed_certs,
+ ciphers_dsa_signed_certs_openssl_names,
+ anonymous_cipher_suites,
+ default_reject_anonymous].
+
+error_handling_tests()->
+ [controller_dies,
+ client_closes_socket,
+ tcp_error_propagation_in_active_mode,
+ tcp_connect,
+ tcp_connect_big,
+ close_transport_accept
+ ].
+
+rizzo_tests() ->
+ [rizzo,
+ no_rizzo_rc4].
%% Test cases starts here.
%%--------------------------------------------------------------------
@@ -1726,21 +1781,7 @@ ciphers_rsa_signed_certs(Config) when is_list(Config) ->
ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
Ciphers = ssl_test_lib:rsa_suites(),
- test_server:format("tls1 erlang cipher suites ~p~n", [Ciphers]),
- run_suites(Ciphers, Version, Config, rsa).
-
-ciphers_rsa_signed_certs_ssl3(doc) ->
- ["Test all rsa ssl cipher suites in ssl3"];
-
-ciphers_rsa_signed_certs_ssl3(suite) ->
- [];
-
-ciphers_rsa_signed_certs_ssl3(Config) when is_list(Config) ->
- Version =
- ssl_record:protocol_version({3,0}),
-
- Ciphers = ssl_test_lib:rsa_suites(),
- test_server:format("ssl3 erlang cipher suites ~p~n", [Ciphers]),
+ test_server:format("~p erlang cipher suites ~p~n", [Version, Ciphers]),
run_suites(Ciphers, Version, Config, rsa).
ciphers_rsa_signed_certs_openssl_names(doc) ->
@@ -1757,18 +1798,6 @@ ciphers_rsa_signed_certs_openssl_names(Config) when is_list(Config) ->
run_suites(Ciphers, Version, Config, rsa).
-ciphers_rsa_signed_certs_openssl_names_ssl3(doc) ->
- ["Test all dsa ssl cipher suites in ssl3"];
-
-ciphers_rsa_signed_certs_openssl_names_ssl3(suite) ->
- [];
-
-ciphers_rsa_signed_certs_openssl_names_ssl3(Config) when is_list(Config) ->
- Version = ssl_record:protocol_version({3,0}),
- Ciphers = ssl_test_lib:openssl_rsa_suites(),
- run_suites(Ciphers, Version, Config, rsa).
-
-
ciphers_dsa_signed_certs(doc) ->
["Test all dsa ssl cipher suites in highest support ssl/tls version"];
@@ -1780,24 +1809,9 @@ ciphers_dsa_signed_certs(Config) when is_list(Config) ->
ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
Ciphers = ssl_test_lib:dsa_suites(),
- test_server:format("tls1 erlang cipher suites ~p~n", [Ciphers]),
+ test_server:format("~p erlang cipher suites ~p~n", [Version, Ciphers]),
run_suites(Ciphers, Version, Config, dsa).
-ciphers_dsa_signed_certs_ssl3(doc) ->
- ["Test all dsa ssl cipher suites in ssl3"];
-
-ciphers_dsa_signed_certs_ssl3(suite) ->
- [];
-
-ciphers_dsa_signed_certs_ssl3(Config) when is_list(Config) ->
- Version =
- ssl_record:protocol_version({3,0}),
-
- Ciphers = ssl_test_lib:dsa_suites(),
- test_server:format("ssl3 erlang cipher suites ~p~n", [Ciphers]),
- run_suites(Ciphers, Version, Config, dsa).
-
-
ciphers_dsa_signed_certs_openssl_names(doc) ->
["Test all dsa ssl cipher suites in highest support ssl/tls version"];
@@ -1812,18 +1826,6 @@ ciphers_dsa_signed_certs_openssl_names(Config) when is_list(Config) ->
test_server:format("tls1 openssl cipher suites ~p~n", [Ciphers]),
run_suites(Ciphers, Version, Config, dsa).
-
-ciphers_dsa_signed_certs_openssl_names_ssl3(doc) ->
- ["Test all dsa ssl cipher suites in ssl3"];
-
-ciphers_dsa_signed_certs_openssl_names_ssl3(suite) ->
- [];
-
-ciphers_dsa_signed_certs_openssl_names_ssl3(Config) when is_list(Config) ->
- Version = ssl_record:protocol_version({3,0}),
- Ciphers = ssl_test_lib:openssl_dsa_suites(),
- run_suites(Ciphers, Version, Config, dsa).
-
anonymous_cipher_suites(doc)->
["Test the anonymous ciphersuites"];
anonymous_cipher_suites(suite) ->
@@ -1860,7 +1862,7 @@ run_suites(Ciphers, Version, Config, Type) ->
end.
erlang_cipher_suite(Suite) when is_list(Suite)->
- ssl_cipher:suite_definition(ssl_cipher:openssl_suite(Suite));
+ ssl:suite_definition(ssl_cipher:openssl_suite(Suite));
erlang_cipher_suite(Suite) ->
Suite.
@@ -2087,13 +2089,16 @@ reuse_session_expired(Config) when is_list(Config) ->
Server ! listen,
%% Make sure session is unregistered due to expiration
- test_server:sleep((?EXPIRE+1) * 1000),
+ test_server:sleep((?EXPIRE+1)),
+ [{session_id, Id} |_] = SessionInfo,
+
+ make_sure_expired(Hostname, Port, Id),
Client2 =
ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
+ {port, Port}, {host, Hostname},
{mfa, {ssl_test_lib, session_info_result, []}},
- {from, self()}, {options, ClientOpts}]),
+ {from, self()}, {options, ClientOpts}]),
receive
{Client2, SessionInfo} ->
test_server:fail(session_reused_when_session_expired);
@@ -2106,6 +2111,22 @@ reuse_session_expired(Config) when is_list(Config) ->
ssl_test_lib:close(Client1),
ssl_test_lib:close(Client2).
+make_sure_expired(Host, Port, Id) ->
+ {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
+ [_, _,_, _, Prop] = StatusInfo,
+ State = ssl_test_lib:state(Prop),
+ Cache = element(2, State),
+
+ case ssl_session_cache:lookup(Cache, {{Host, Port}, Id}) of
+ undefined ->
+ ok;
+ #session{is_resumable = false} ->
+ ok;
+ _ ->
+ test_server:sleep(?SLEEP),
+ make_sure_expired(Host, Port, Id)
+ end.
+
%%--------------------------------------------------------------------
server_does_not_want_to_reuse_session(doc) ->
["Test reuse of sessions (short handshake)"];
@@ -3912,7 +3933,7 @@ recv_error_handling(Config) when is_list(Config) ->
{mfa, {?MODULE, recv_close, []}},
{options, [{active, false} | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
- {Client, #sslsocket{pid=Pid} = SslSocket} = ssl_test_lib:start_client([return_socket,
+ {_Client, #sslsocket{} = SslSocket} = ssl_test_lib:start_client([return_socket,
{node, ClientNode}, {port, Port},
{host, Hostname},
{from, self()},
@@ -3929,9 +3950,9 @@ rizzo(doc) -> ["Test that there is a 1/n-1-split for non RC4 in 'TLS < 1.1' as i
rizzo(Config) when is_list(Config) ->
Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128],
- run_send_recv_rizzo(Ciphers, Config, sslv3,
- {?MODULE, send_recv_result_active_rizzo, []}),
- run_send_recv_rizzo(Ciphers, Config, tlsv1,
+ Prop = ?config(tc_group_properties, Config),
+ Version = proplists:get_value(name, Prop),
+ run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_rizzo, []}).
%%--------------------------------------------------------------------
no_rizzo_rc4(doc) ->
@@ -3939,9 +3960,9 @@ no_rizzo_rc4(doc) ->
no_rizzo_rc4(Config) when is_list(Config) ->
Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(),Y == rc4_128],
- run_send_recv_rizzo(Ciphers, Config, sslv3,
- {?MODULE, send_recv_result_active_no_rizzo, []}),
- run_send_recv_rizzo(Ciphers, Config, tlsv1,
+ Prop = ?config(tc_group_properties, Config),
+ Version = proplists:get_value(name, Prop),
+ run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_no_rizzo, []}).
%%--------------------------------------------------------------------
diff --git a/lib/ssl/test/ssl_cipher_SUITE.erl b/lib/ssl/test/ssl_cipher_SUITE.erl
index 99bc21e820..ea1d9dc90c 100644
--- a/lib/ssl/test/ssl_cipher_SUITE.erl
+++ b/lib/ssl/test/ssl_cipher_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. 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
@@ -27,6 +27,7 @@
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
-include("ssl_cipher.hrl").
+-include("ssl_alert.hrl").
-define(TIMEOUT, 600000).
@@ -103,7 +104,7 @@ end_per_testcase(_TestCase, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [aes_decipher_good, aes_decipher_fail].
+ [aes_decipher_good, aes_decipher_good_tls11, aes_decipher_fail, aes_decipher_fail_tls11].
groups() ->
[].
@@ -131,10 +132,39 @@ aes_decipher_good(Config) when is_list(Config) ->
190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160,
198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122,
108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>,
- Version = {3,3},
- Content = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56,72,69,76,76,79,10>>,
+ Content = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56, "HELLO\n">>,
Mac = <<71,136,212,107,223,200,70,232,127,116,148,205,232,35,158,113,237,174,15,217,192,168,35,8,6,107,107,233,25,174,90,111>>,
+ Version = {3,0},
{Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version),
+ Version1 = {3,1},
+ {Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1),
+ ok.
+
+%%--------------------------------------------------------------------
+
+aes_decipher_good_tls11(doc) ->
+ ["Decipher a known TLS 1.1 cryptotext."];
+
+aes_decipher_good_tls11(suite) ->
+ [];
+
+%% the fragment is actuall a TLS 1.1 record, with
+%% Version = TLS 1.1, we get the correct NextIV in #cipher_state
+aes_decipher_good_tls11(Config) when is_list(Config) ->
+ HashSz = 32,
+ CipherState = #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>,
+ key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,148>>},
+ Fragment = <<220,193,179,139,171,33,143,245,202,47,123,251,13,232,114,8,
+ 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160,
+ 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122,
+ 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>,
+ Content = <<"HELLO\n">>,
+ NextIV = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56>>,
+ Mac = <<71,136,212,107,223,200,70,232,127,116,148,205,232,35,158,113,237,174,15,217,192,168,35,8,6,107,107,233,25,174,90,111>>,
+ Version = {3,2},
+ {Content, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version),
+ Version1 = {3,2},
+ {Content, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1),
ok.
%%--------------------------------------------------------------------
@@ -154,10 +184,38 @@ aes_decipher_fail(Config) when is_list(Config) ->
190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160,
198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122,
108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>,
- Version = {3,3},
+ Version = {3,0},
{Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version),
32 = byte_size(Content),
32 = byte_size(Mac),
+ Version1 = {3,1},
+ {Content1, Mac1, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1),
+ 32 = byte_size(Content1),
+ 32 = byte_size(Mac1),
+ ok.
+
+%%--------------------------------------------------------------------
+
+aes_decipher_fail_tls11(doc) ->
+ ["Decipher a known TLS 1.1 cryptotext."];
+
+aes_decipher_fail_tls11(suite) ->
+ [];
+
+%% same as above, last byte of key replaced
+%% stricter padding checks in TLS 1.1 mean we get an alert instead
+aes_decipher_fail_tls11(Config) when is_list(Config) ->
+ HashSz = 32,
+ CipherState = #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>,
+ key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,254>>},
+ Fragment = <<220,193,179,139,171,33,143,245,202,47,123,251,13,232,114,8,
+ 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160,
+ 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122,
+ 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>,
+ Version = {3,2},
+ #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version),
+ Version1 = {3,3},
+ #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1),
ok.
%%--------------------------------------------------------------------
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index 08c23b2d47..363a0be594 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. 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
@@ -48,7 +48,8 @@ decode_hello_handshake(_Config) ->
16#00, 16#00, 16#33, 16#74, 16#00, 16#07, 16#06, 16#73,
16#70, 16#64, 16#79, 16#2f, 16#32>>,
- {Records, _Buffer} = ssl_handshake:get_tls_handshake(HelloPacket, <<>>),
+ Version = {3, 0},
+ {Records, _Buffer} = ssl_handshake:get_tls_handshake(Version, HelloPacket, <<>>),
{Hello, _Data} = hd(Records),
#renegotiation_info{renegotiated_connection = <<0>>} = Hello#server_hello.renegotiation_info.
diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
new file mode 100644
index 0000000000..8597aa6740
--- /dev/null
+++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
@@ -0,0 +1,310 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(ssl_npn_handshake_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+-include_lib("common_test/include/ct.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}].
+
+groups() ->
+ [
+ {'tlsv1.2', [], next_protocol_tests()},
+ {'tlsv1.1', [], next_protocol_tests()},
+ {'tlsv1', [], next_protocol_tests()},
+ {'sslv3', [], next_protocol_not_supported()}
+ ].
+
+next_protocol_tests() ->
+ [validate_empty_protocols_are_not_allowed,
+ validate_empty_advertisement_list_is_allowed,
+ validate_advertisement_must_be_a_binary_list,
+ validate_client_protocols_must_be_a_tuple,
+ normal_npn_handshake_server_preference,
+ normal_npn_handshake_client_preference,
+ fallback_npn_handshake,
+ fallback_npn_handshake_server_preference,
+ client_negotiate_server_does_not_support,
+ no_client_negotiate_but_server_supports_npn,
+ renegotiate_from_client_after_npn_handshake
+ ].
+
+next_protocol_not_supported() ->
+ [npn_not_supported_client,
+ npn_not_supported_server
+ ].
+
+init_per_suite(Config) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ application:start(public_key),
+ ssl:start(),
+ Result =
+ (catch make_certs:all(?config(data_dir, Config),
+ ?config(priv_dir, Config))),
+ test_server:format("Make certs ~p~n", [Result]),
+ ssl_test_lib:cert_options(Config)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto).
+
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName),
+ Config;
+ false ->
+ {skip, "Missing crypto support"}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
+
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%% Test cases starts here.
+%%--------------------------------------------------------------------
+
+validate_empty_protocols_are_not_allowed(Config) when is_list(Config) ->
+ {error, {eoptions, {next_protocols_advertised, {invalid_protocol, <<>>}}}}
+ = (catch ssl:listen(9443,
+ [{next_protocols_advertised, [<<"foo/1">>, <<"">>]}])),
+ {error, {eoptions, {client_preferred_next_protocols, {invalid_protocol, <<>>}}}}
+ = (catch ssl:connect({127,0,0,1}, 9443,
+ [{client_preferred_next_protocols,
+ {client, [<<"foo/1">>, <<"">>], <<"foox/1">>}}], infinity)),
+ Option = {client_preferred_next_protocols, {invalid_protocol, <<"">>}},
+ {error, {eoptions, Option}} = (catch ssl:connect({127,0,0,1}, 9443, [Option], infinity)).
+
+%--------------------------------------------------------------------------------
+
+validate_empty_advertisement_list_is_allowed(Config) when is_list(Config) ->
+ Option = {next_protocols_advertised, []},
+ {ok, Socket} = ssl:listen(0, [Option]),
+ ssl:close(Socket).
+%--------------------------------------------------------------------------------
+
+validate_advertisement_must_be_a_binary_list(Config) when is_list(Config) ->
+ Option = {next_protocols_advertised, blah},
+ {error, {eoptions, Option}} = (catch ssl:listen(9443, [Option])).
+%--------------------------------------------------------------------------------
+
+validate_client_protocols_must_be_a_tuple(Config) when is_list(Config) ->
+ Option = {client_preferred_next_protocols, [<<"foo/1">>]},
+ {error, {eoptions, Option}} = (catch ssl:connect({127,0,0,1}, 9443, [Option])).
+
+%--------------------------------------------------------------------------------
+
+normal_npn_handshake_server_preference(Config) when is_list(Config) ->
+ run_npn_handshake(Config,
+ [{client_preferred_next_protocols,
+ {server, [<<"http/1.0">>, <<"http/1.1">>], <<"http/1.1">>}}],
+ [{next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {ok, <<"http/1.1">>}).
+%--------------------------------------------------------------------------------
+
+normal_npn_handshake_client_preference(Config) when is_list(Config) ->
+ run_npn_handshake(Config,
+ [{client_preferred_next_protocols,
+ {client, [<<"http/1.0">>, <<"http/1.1">>], <<"http/1.1">>}}],
+ [{next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {ok, <<"http/1.0">>}).
+
+%--------------------------------------------------------------------------------
+
+fallback_npn_handshake(Config) when is_list(Config) ->
+ run_npn_handshake(Config,
+ [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}}],
+ [{next_protocols_advertised, [<<"spdy/1">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {ok, <<"http/1.1">>}).
+%--------------------------------------------------------------------------------
+
+fallback_npn_handshake_server_preference(Config) when is_list(Config) ->
+ run_npn_handshake(Config,
+ [{client_preferred_next_protocols, {server, [<<"spdy/2">>], <<"http/1.1">>}}],
+ [{next_protocols_advertised, [<<"spdy/1">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {ok, <<"http/1.1">>}).
+
+%--------------------------------------------------------------------------------
+
+no_client_negotiate_but_server_supports_npn(Config) when is_list(Config) ->
+ run_npn_handshake(Config,
+ [],
+ [{next_protocols_advertised, [<<"spdy/1">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {error, next_protocol_not_negotiated}).
+%--------------------------------------------------------------------------------
+
+
+client_negotiate_server_does_not_support(Config) when is_list(Config) ->
+ run_npn_handshake(Config,
+ [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}}],
+ [],
+ {error, next_protocol_not_negotiated}).
+
+%--------------------------------------------------------------------------------
+renegotiate_from_client_after_npn_handshake(Config) when is_list(Config) ->
+ Data = "hello world",
+
+ ClientOpts0 = ?config(client_opts, Config),
+ ClientOpts = [{client_preferred_next_protocols,
+ {client, [<<"http/1.0">>], <<"http/1.1">>}}] ++ ClientOpts0,
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = [{next_protocols_advertised,
+ [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ ServerOpts0,
+ ExpectedProtocol = {ok, <<"http/1.0">>},
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, ssl_receive_and_assert_npn, [ExpectedProtocol, Data]}},
+ {options, ServerOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, assert_npn_and_renegotiate_and_send_data, [ExpectedProtocol, Data]}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok).
+
+%--------------------------------------------------------------------------------
+npn_not_supported_client(Config) when is_list(Config) ->
+ ClientOpts0 = ?config(client_opts, Config),
+ PrefProtocols = {client_preferred_next_protocols,
+ {client, [<<"http/1.0">>], <<"http/1.1">>}},
+ ClientOpts = [PrefProtocols] ++ ClientOpts0,
+ {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Client = ssl_test_lib:start_client_error([{node, ClientNode},
+ {port, 8888}, {host, Hostname},
+ {from, self()}, {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Client, {error,
+ {eoptions,
+ {not_supported_in_sslv3, PrefProtocols}}}).
+
+%--------------------------------------------------------------------------------
+npn_not_supported_server(Config) when is_list(Config)->
+ ServerOpts0 = ?config(server_opts, Config),
+ AdvProtocols = {next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]},
+ ServerOpts = [AdvProtocols] ++ ServerOpts0,
+
+ {error, {eoptions, {not_supported_in_sslv3, AdvProtocols}}} = ssl:listen(0, ServerOpts).
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+run_npn_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedProtocol) ->
+ Data = "hello world",
+
+ ClientOpts0 = ?config(client_opts, Config),
+ ClientOpts = ClientExtraOpts ++ ClientOpts0,
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = ServerExtraOpts ++ ServerOpts0,
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, ssl_receive_and_assert_npn, [ExpectedProtocol, Data]}},
+ {options, ServerOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, ssl_send_and_assert_npn, [ExpectedProtocol, Data]}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok).
+
+
+assert_npn(Socket, Protocol) ->
+ test_server:format("Negotiated Protocol ~p, Expecting: ~p ~n",
+ [ssl:negotiated_next_protocol(Socket), Protocol]),
+ Protocol = ssl:negotiated_next_protocol(Socket).
+
+assert_npn_and_renegotiate_and_send_data(Socket, Protocol, Data) ->
+ assert_npn(Socket, Protocol),
+ test_server:format("Renegotiating ~n", []),
+ ok = ssl:renegotiate(Socket),
+ ssl:send(Socket, Data),
+ assert_npn(Socket, Protocol),
+ ok.
+
+ssl_send_and_assert_npn(Socket, Protocol, Data) ->
+ assert_npn(Socket, Protocol),
+ ssl_send(Socket, Data).
+
+ssl_receive_and_assert_npn(Socket, Protocol, Data) ->
+ assert_npn(Socket, Protocol),
+ ssl_receive(Socket, Data).
+
+ssl_send(Socket, Data) ->
+ test_server:format("Connection info: ~p~n",
+ [ssl:connection_info(Socket)]),
+ ssl:send(Socket, Data).
+
+ssl_receive(Socket, Data) ->
+ ssl_receive(Socket, Data, []).
+
+ssl_receive(Socket, Data, Buffer) ->
+ test_server:format("Connection info: ~p~n",
+ [ssl:connection_info(Socket)]),
+ receive
+ {ssl, Socket, MoreData} ->
+ test_server:format("Received ~p~n",[MoreData]),
+ NewBuffer = Buffer ++ MoreData,
+ case NewBuffer of
+ Data ->
+ ssl:send(Socket, "Got it"),
+ ok;
+ _ ->
+ ssl_receive(Socket, Data, NewBuffer)
+ end;
+ Other ->
+ test_server:fail({unexpected_message, Other})
+ after 4000 ->
+ test_server:fail({did_not_get, Data})
+ end.
+
+
+connection_info_result(Socket) ->
+ ssl:connection_info(Socket).
diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl
new file mode 100644
index 0000000000..5102c74e87
--- /dev/null
+++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl
@@ -0,0 +1,117 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(ssl_npn_hello_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+-include("ssl_handshake.hrl").
+-include("ssl_record.hrl").
+-include_lib("common_test/include/ct.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [encode_and_decode_npn_client_hello_test,
+ encode_and_decode_npn_server_hello_test,
+ encode_and_decode_client_hello_test,
+ encode_and_decode_server_hello_test,
+ create_server_hello_with_advertised_protocols_test,
+ create_server_hello_with_no_advertised_protocols_test].
+
+
+create_client_handshake(Npn) ->
+ ssl_handshake:encode_handshake(#client_hello{
+ client_version = {1, 2},
+ random = <<1:256>>,
+ session_id = <<>>,
+ cipher_suites = "",
+ compression_methods = "",
+ next_protocol_negotiation = Npn,
+ renegotiation_info = #renegotiation_info{}
+ }, vsn).
+
+
+encode_and_decode_client_hello_test(_Config) ->
+ HandShakeData = create_client_handshake(undefined),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ {[{DecodedHandshakeMessage, _Raw}], _} = ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
+ NextProtocolNegotiation = DecodedHandshakeMessage#client_hello.next_protocol_negotiation,
+ NextProtocolNegotiation = undefined.
+
+encode_and_decode_npn_client_hello_test(_Config) ->
+ HandShakeData = create_client_handshake(#next_protocol_negotiation{extension_data = <<>>}),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ {[{DecodedHandshakeMessage, _Raw}], _} = ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
+ NextProtocolNegotiation = DecodedHandshakeMessage#client_hello.next_protocol_negotiation,
+ NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<>>}.
+
+create_server_handshake(Npn) ->
+ ssl_handshake:encode_handshake(#server_hello{
+ server_version = {1, 2},
+ random = <<1:256>>,
+ session_id = <<>>,
+ cipher_suite = <<1,2>>,
+ compression_method = 1,
+ next_protocol_negotiation = Npn,
+ renegotiation_info = #renegotiation_info{}
+ }, vsn).
+
+encode_and_decode_server_hello_test(_Config) ->
+ HandShakeData = create_server_handshake(undefined),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ {[{DecodedHandshakeMessage, _Raw}], _} =
+ ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
+ NextProtocolNegotiation = DecodedHandshakeMessage#server_hello.next_protocol_negotiation,
+ NextProtocolNegotiation = undefined.
+
+encode_and_decode_npn_server_hello_test(_Config) ->
+ HandShakeData = create_server_handshake(#next_protocol_negotiation{extension_data = <<6, "spdy/2">>}),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ {[{DecodedHandshakeMessage, _Raw}], _} = ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
+ NextProtocolNegotiation = DecodedHandshakeMessage#server_hello.next_protocol_negotiation,
+ ct:print("~p ~n", [NextProtocolNegotiation]),
+ NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<6, "spdy/2">>}.
+
+create_connection_states() ->
+ #connection_states{
+ pending_read = #connection_state{
+ security_parameters = #security_parameters{
+ server_random = <<1:256>>,
+ compression_algorithm = 1,
+ cipher_suite = <<1, 2>>
+ }
+ },
+
+ current_read = #connection_state {
+ secure_renegotiation = false
+ }
+ }.
+
+create_server_hello_with_no_advertised_protocols_test(_Config) ->
+ Hello = ssl_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), false, undefined),
+ undefined = Hello#server_hello.next_protocol_negotiation.
+
+create_server_hello_with_advertised_protocols_test(_Config) ->
+ Hello = ssl_handshake:server_hello(<<>>, {3, 0}, create_connection_states(),
+ false, [<<"spdy/1">>, <<"http/1.0">>, <<"http/1.1">>]),
+ #next_protocol_negotiation{extension_data = <<6, "spdy/1", 8, "http/1.0", 8, "http/1.1">>} =
+ Hello#server_hello.next_protocol_negotiation.
diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl
index 593b1fda5e..8ce80cb725 100644
--- a/lib/ssl/test/ssl_packet_SUITE.erl
+++ b/lib/ssl/test/ssl_packet_SUITE.erl
@@ -122,15 +122,56 @@ end_per_testcase(_TestCase, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
+ [
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}
+ ].
+
+groups() ->
+ [{'tlsv1.2', [], packet_tests()},
+ {'tlsv1.1', [], packet_tests()},
+ {'tlsv1', [], packet_tests()},
+ {'sslv3', [], packet_tests()}].
+
+packet_tests() ->
+ active_packet_tests() ++ active_once_packet_tests() ++ passive_packet_tests() ++
+ [packet_send_to_large,
+ packet_cdr_decode, packet_cdr_decode_list,
+ packet_http_decode, packet_http_decode_list,
+ packet_http_bin_decode_multi,
+ packet_line_decode, packet_line_decode_list,
+ packet_asn1_decode, packet_asn1_decode_list,
+ packet_tpkt_decode, packet_tpkt_decode_list,
+ packet_sunrm_decode, packet_sunrm_decode_list].
+
+passive_packet_tests() ->
[packet_raw_passive_many_small,
packet_0_passive_many_small,
packet_1_passive_many_small,
packet_2_passive_many_small,
packet_4_passive_many_small,
- packet_raw_passive_some_big, packet_0_passive_some_big,
- packet_1_passive_some_big, packet_2_passive_some_big,
+ packet_raw_passive_some_big,
+ packet_0_passive_some_big,
+ packet_1_passive_some_big,
+ packet_2_passive_some_big,
packet_4_passive_some_big,
- packet_raw_active_once_many_small,
+ packet_httph_passive,
+ packet_httph_bin_passive,
+ packet_http_error_passive,
+ packet_wait_passive,
+ packet_size_passive,
+ packet_baddata_passive,
+ %% inet header option should be deprecated!
+ header_decode_one_byte_passive,
+ header_decode_two_bytes_passive,
+ header_decode_two_bytes_two_sent_passive,
+ header_decode_two_bytes_one_sent_passive
+ ].
+
+active_once_packet_tests() ->
+ [packet_raw_active_once_many_small,
packet_0_active_once_many_small,
packet_1_active_once_many_small,
packet_2_active_once_many_small,
@@ -140,44 +181,49 @@ all() ->
packet_1_active_once_some_big,
packet_2_active_once_some_big,
packet_4_active_once_some_big,
- packet_raw_active_many_small,
- packet_0_active_many_small, packet_1_active_many_small,
- packet_2_active_many_small, packet_4_active_many_small,
- packet_raw_active_some_big, packet_0_active_some_big,
- packet_1_active_some_big, packet_2_active_some_big,
- packet_4_active_some_big, packet_send_to_large,
- packet_wait_passive, packet_wait_active,
- packet_baddata_passive, packet_baddata_active,
- packet_size_passive, packet_size_active,
- packet_cdr_decode, packet_cdr_decode_list,
- packet_http_decode, packet_http_decode_list,
- packet_http_bin_decode_multi, packet_http_error_passive,
- packet_httph_active, packet_httph_bin_active,
- packet_httph_active_once, packet_httph_bin_active_once,
- packet_httph_passive, packet_httph_bin_passive,
- packet_line_decode, packet_line_decode_list,
- packet_asn1_decode, packet_asn1_decode_list,
- packet_tpkt_decode, packet_tpkt_decode_list,
- packet_sunrm_decode, packet_sunrm_decode_list,
- {group, header}
+ packet_httph_active_once,
+ packet_httph_bin_active_once
].
-groups() ->
- [{header, [], [ header_decode_one_byte,
- header_decode_two_bytes,
- header_decode_two_bytes_one_sent,
- header_decode_two_bytes_two_sent]}].
+active_packet_tests() ->
+ [packet_raw_active_many_small,
+ packet_0_active_many_small,
+ packet_1_active_many_small,
+ packet_2_active_many_small,
+ packet_4_active_many_small,
+ packet_raw_active_some_big,
+ packet_0_active_some_big,
+ packet_1_active_some_big,
+ packet_2_active_some_big,
+ packet_4_active_some_big,
+ packet_httph_active,
+ packet_httph_bin_active,
+ packet_wait_active,
+ packet_baddata_active,
+ packet_size_active,
+ %% inet header option should be deprecated!
+ header_decode_one_byte_active,
+ header_decode_two_bytes_active,
+ header_decode_two_bytes_two_sent_active,
+ header_decode_two_bytes_one_sent_active
+ ].
-init_per_group(header, Config) ->
- case ssl_record:highest_protocol_version(ssl_record:supported_protocol_versions()) of
- {3, N} when N < 2 ->
- {skip, ""};
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName),
+ Config;
+ false ->
+ {skip, "Missing crypto support"}
+ end;
_ ->
+ ssl:start(),
Config
- end;
+ end.
-init_per_group(_, Config) ->
- Config.
end_per_group(_GroupName, Config) ->
Config.
@@ -2436,11 +2482,11 @@ packet_sunrm_decode_list(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-header_decode_one_byte(doc) ->
+header_decode_one_byte_active(doc) ->
["Test setting the packet option {header, 1}"];
-header_decode_one_byte(suite) ->
+header_decode_one_byte_active(suite) ->
[];
-header_decode_one_byte(Config) when is_list(Config) ->
+header_decode_one_byte_active(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -2449,7 +2495,7 @@ header_decode_one_byte(Config) when is_list(Config) ->
Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, server_header_decode,
+ {mfa, {?MODULE, server_header_decode_active,
[Data, [11 | <<"Hello world">>]]}},
{options, [{active, true}, binary,
{header,1}|ServerOpts]}]),
@@ -2458,7 +2504,7 @@ header_decode_one_byte(Config) when is_list(Config) ->
Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
{host, Hostname},
{from, self()},
- {mfa, {?MODULE, client_header_decode,
+ {mfa, {?MODULE, client_header_decode_active,
[Data, [11 | <<"Hello world">> ]]}},
{options, [{active, true}, {header, 1},
binary | ClientOpts]}]),
@@ -2470,11 +2516,11 @@ header_decode_one_byte(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-header_decode_two_bytes(doc) ->
+header_decode_two_bytes_active(doc) ->
["Test setting the packet option {header, 2}"];
-header_decode_two_bytes(suite) ->
+header_decode_two_bytes_active(suite) ->
[];
-header_decode_two_bytes(Config) when is_list(Config) ->
+header_decode_two_bytes_active(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -2483,7 +2529,7 @@ header_decode_two_bytes(Config) when is_list(Config) ->
Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, server_header_decode,
+ {mfa, {?MODULE, server_header_decode_active,
[Data, [11, $H | <<"ello world">> ]]}},
{options, [{active, true}, binary,
{header,2}|ServerOpts]}]),
@@ -2492,7 +2538,7 @@ header_decode_two_bytes(Config) when is_list(Config) ->
Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
{host, Hostname},
{from, self()},
- {mfa, {?MODULE, client_header_decode,
+ {mfa, {?MODULE, client_header_decode_active,
[Data, [11, $H | <<"ello world">> ]]}},
{options, [{active, true}, {header, 2},
binary | ClientOpts]}]),
@@ -2505,11 +2551,11 @@ header_decode_two_bytes(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-header_decode_two_bytes_two_sent(doc) ->
- ["Test setting the packet option {header, 2} and sending on byte"];
-header_decode_two_bytes_two_sent(suite) ->
+header_decode_two_bytes_two_sent_active(doc) ->
+ ["Test setting the packet option {header, 2} and sending two byte"];
+header_decode_two_bytes_two_sent_active(suite) ->
[];
-header_decode_two_bytes_two_sent(Config) when is_list(Config) ->
+header_decode_two_bytes_two_sent_active(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -2518,8 +2564,8 @@ header_decode_two_bytes_two_sent(Config) when is_list(Config) ->
Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, server_header_decode,
- [Data, [$H, $e | <<>> ]]}},
+ {mfa, {?MODULE, server_header_decode_active,
+ [Data, [$H, $e]]}},
{options, [{active, true}, binary,
{header,2}|ServerOpts]}]),
@@ -2527,8 +2573,8 @@ header_decode_two_bytes_two_sent(Config) when is_list(Config) ->
Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
{host, Hostname},
{from, self()},
- {mfa, {?MODULE, client_header_decode,
- [Data, [$H, $e | <<>> ]]}},
+ {mfa, {?MODULE, client_header_decode_active,
+ [Data, [$H, $e]]}},
{options, [{active, true}, {header, 2},
binary | ClientOpts]}]),
@@ -2540,11 +2586,11 @@ header_decode_two_bytes_two_sent(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-header_decode_two_bytes_one_sent(doc) ->
- ["Test setting the packet option {header, 2} and sending on byte"];
-header_decode_two_bytes_one_sent(suite) ->
+header_decode_two_bytes_one_sent_active(doc) ->
+ ["Test setting the packet option {header, 2} and sending one byte"];
+header_decode_two_bytes_one_sent_active(suite) ->
[];
-header_decode_two_bytes_one_sent(Config) when is_list(Config) ->
+header_decode_two_bytes_one_sent_active(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -2553,7 +2599,7 @@ header_decode_two_bytes_one_sent(Config) when is_list(Config) ->
Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, server_header_decode,
+ {mfa, {?MODULE, server_header_decode_active,
[Data, "H"]}},
{options, [{active, true}, binary,
{header,2}|ServerOpts]}]),
@@ -2562,7 +2608,7 @@ header_decode_two_bytes_one_sent(Config) when is_list(Config) ->
Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
{host, Hostname},
{from, self()},
- {mfa, {?MODULE, client_header_decode,
+ {mfa, {?MODULE, client_header_decode_active,
[Data, "H"]}},
{options, [{active, true}, {header, 2},
binary | ClientOpts]}]),
@@ -2572,6 +2618,143 @@ header_decode_two_bytes_one_sent(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+
+header_decode_one_byte_passive(doc) ->
+ ["Test setting the packet option {header, 1}"];
+header_decode_one_byte_passive(suite) ->
+ [];
+header_decode_one_byte_passive(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = <<11:8, "Hello world">>,
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_header_decode_passive,
+ [Data, [11 | <<"Hello world">>]]}},
+ {options, [{active, false}, binary,
+ {header,1}|ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, client_header_decode_passive,
+ [Data, [11 | <<"Hello world">> ]]}},
+ {options, [{active, false}, {header, 1},
+ binary | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+
+header_decode_two_bytes_passive(doc) ->
+ ["Test setting the packet option {header, 2}"];
+header_decode_two_bytes_passive(suite) ->
+ [];
+header_decode_two_bytes_passive(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = <<11:8, "Hello world">>,
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_header_decode_passive,
+ [Data, [11, $H | <<"ello world">> ]]}},
+ {options, [{active, false}, binary,
+ {header,2}|ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, client_header_decode_passive,
+ [Data, [11, $H | <<"ello world">> ]]}},
+ {options, [{active, false}, {header, 2},
+ binary | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+
+header_decode_two_bytes_two_sent_passive(doc) ->
+ ["Test setting the packet option {header, 2} and sending two byte"];
+header_decode_two_bytes_two_sent_passive(suite) ->
+ [];
+header_decode_two_bytes_two_sent_passive(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = <<"He">>,
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_header_decode_passive,
+ [Data, [$H, $e]]}},
+ {options, [{active, false}, binary,
+ {header,2}|ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, client_header_decode_passive,
+ [Data, [$H, $e]]}},
+ {options, [{active, false}, {header, 2},
+ binary | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+
+header_decode_two_bytes_one_sent_passive(doc) ->
+ ["Test setting the packet option {header, 2} and sending one byte"];
+header_decode_two_bytes_one_sent_passive(suite) ->
+ [];
+header_decode_two_bytes_one_sent_passive(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = <<"H">>,
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_header_decode_passive,
+ [Data, "H"]}},
+ {options, [{active, false}, binary,
+ {header,2}|ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, client_header_decode_passive,
+ [Data, "H"]}},
+ {options, [{active, false}, {header, 2},
+ binary | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
%% Internal functions
@@ -2758,29 +2941,52 @@ client_packet_decode(Socket, P1, P2, Packet) ->
Other2 -> exit({?LINE, Other2})
end.
-server_header_decode(Socket, Packet, Result) ->
+server_header_decode_active(Socket, Packet, Result) ->
receive
- {ssl, Socket, Result} -> ok;
- Other1 -> exit({?LINE, Other1})
- end,
- ok = ssl:send(Socket, Packet),
- receive
- {ssl, Socket, Result} -> ok;
- Other2 -> exit({?LINE, Other2})
+ {ssl, Socket, Result} ->
+ ok;
+ {ssl, Socket, Other1} ->
+ check_header_result(Result, Other1)
end,
ok = ssl:send(Socket, Packet).
-client_header_decode(Socket, Packet, Result) ->
+client_header_decode_active(Socket, Packet, Result) ->
ok = ssl:send(Socket, Packet),
receive
- {ssl, Socket, Result} -> ok;
- Other1 -> exit({?LINE, Other1})
+ {ssl, Socket, Result} ->
+ ok;
+ {ssl, Socket, Other1} ->
+ check_header_result(Result, Other1)
+ end.
+
+server_header_decode_passive(Socket, Packet, Result) ->
+ case ssl:recv(Socket, 0) of
+ {ok, Result} ->
+ ok;
+ {ok, Other} ->
+ check_header_result(Result, Other)
end,
+ ok = ssl:send(Socket, Packet).
+
+client_header_decode_passive(Socket, Packet, Result) ->
ok = ssl:send(Socket, Packet),
- receive
- {ssl, Socket, Result} -> ok;
- Other2 -> exit({?LINE, Other2})
+
+ case ssl:recv(Socket, 0) of
+ {ok, Result} ->
+ ok;
+ {ok, Other} ->
+ check_header_result(Result, Other)
end.
+
+%% The inet header option is a broken option as it does not buffer until it gets enough data.
+%% This check only checks that it has the same behavior as inet, but it is a quite useless
+%% option and the bitsynax makes it obsolete!
+check_header_result([Byte1 | _], [Byte1]) ->
+ ok;
+check_header_result([Byte1, Byte2 | _], [Byte1, Byte2]) ->
+ ok;
+check_header_result(_,Got) ->
+ exit({?LINE, Got}).
server_line_packet_decode(Socket, Packet) when is_binary(Packet) ->
[L1, L2] = string:tokens(binary_to_list(Packet), "\n"),
diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl
index 02b5516e35..c97f97e70b 100644
--- a/lib/ssl/test/ssl_payload_SUITE.erl
+++ b/lib/ssl/test/ssl_payload_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. 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
@@ -103,23 +103,56 @@ end_per_testcase(_TestCase, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
+ [
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}
+ ].
+
+groups() ->
+ [
+ {'tlsv1.2', [], payload_tests()},
+ {'tlsv1.1', [], payload_tests()},
+ {'tlsv1', [], payload_tests()},
+ {'sslv3', [], payload_tests()}
+ ].
+
+payload_tests() ->
[server_echos_passive_small,
server_echos_active_once_small,
- server_echos_active_small, client_echos_passive_small,
+ server_echos_active_small,
+ client_echos_passive_small,
client_echos_active_once_small,
- client_echos_active_small, server_echos_passive_big,
- server_echos_active_once_big, server_echos_active_big,
- client_echos_passive_big, client_echos_active_once_big,
- client_echos_active_big, server_echos_passive_huge,
- server_echos_active_once_huge, server_echos_active_huge,
+ client_echos_active_small,
+ server_echos_passive_big,
+ server_echos_active_once_big,
+ server_echos_active_big,
+ client_echos_passive_big,
+ client_echos_active_once_big,
+ client_echos_active_big,
+ server_echos_passive_huge,
+ server_echos_active_once_huge,
+ server_echos_active_huge,
client_echos_passive_huge,
- client_echos_active_once_huge, client_echos_active_huge].
+ client_echos_active_once_huge,
+ client_echos_active_huge].
-groups() ->
- [].
-init_per_group(_GroupName, Config) ->
- Config.
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName),
+ Config;
+ false ->
+ {skip, "Missing crypto support"}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
end_per_group(_GroupName, Config) ->
Config.
diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl
index 6d758ecb01..1d71efd40c 100644
--- a/lib/ssl/test/ssl_session_cache_SUITE.erl
+++ b/lib/ssl/test/ssl_session_cache_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index fa8a1826f2..63731ee25c 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. 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
@@ -150,7 +150,7 @@ run_client(Opts) ->
{ok, Socket} ->
Pid ! { connected, Socket },
test_server:format("Client: connected~n", []),
- %% In specail cases we want to know the client port, it will
+ %% In special cases we want to know the client port, it will
%% be indicated by sending {port, 0} in options list!
send_selected_port(Pid, proplists:get_value(port, Options), Socket),
{Module, Function, Args} = proplists:get_value(mfa, Opts),
@@ -708,3 +708,33 @@ state([{data,[{"StateData", State}]} | _]) ->
State;
state([_ | Rest]) ->
state(Rest).
+
+is_tls_version('tlsv1.2') ->
+ true;
+is_tls_version('tlsv1.1') ->
+ true;
+is_tls_version('tlsv1') ->
+ true;
+is_tls_version('sslv3') ->
+ true;
+is_tls_version(_) ->
+ false.
+
+init_tls_version(Version) ->
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, protocol_version, Version),
+ ssl:start().
+
+sufficient_crypto_support('tlsv1.2') ->
+ Data = "Sampl",
+ Data2 = "e #1",
+ Key = <<0,1,2,3,16,17,18,19,32,33,34,35,48,49,50,51,4,5,6,7,20,21,22,23,36,37,38,39,
+ 52,53,54,55,8,9,10,11,24,25,26,27,40,41,42,43,56,57,58,59>>,
+ try
+ crypto:sha256_mac(Key, lists:flatten([Data, Data2])),
+ true
+ catch _:_ -> false
+ end;
+sufficient_crypto_support(_) ->
+ true.
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index f593c1c552..21797bee08 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -29,7 +29,7 @@
-define(TIMEOUT, 120000).
-define(LONG_TIMEOUT, 600000).
-define(SLEEP, 1000).
--define(OPENSSL_RENEGOTIATE, "r\n").
+-define(OPENSSL_RENEGOTIATE, "R\n").
-define(OPENSSL_QUIT, "Q\n").
-define(OPENSSL_GARBAGE, "P\n").
-define(EXPIRE, 10).
@@ -107,14 +107,23 @@ init_per_testcase(TestCase, Config0) ->
special_init(TestCase, Config)
when TestCase == erlang_client_openssl_server_renegotiate;
TestCase == erlang_client_openssl_server_no_wrap_sequence_number;
- TestCase == erlang_server_openssl_client_no_wrap_sequence_number ->
+ TestCase == erlang_server_openssl_client_no_wrap_sequence_number
+ ->
check_sane_openssl_renegotaite(Config);
special_init(ssl2_erlang_server_openssl_client, Config) ->
check_sane_openssl_sslv2(Config);
-special_init(ciphers_dsa_signed_certs, Config) ->
- check_sane_openssl_dsa(Config);
+special_init(TestCase, Config)
+ when TestCase == erlang_client_openssl_server_npn;
+ TestCase == erlang_server_openssl_client_npn;
+ TestCase == erlang_server_openssl_client_npn_renegotiate;
+ TestCase == erlang_client_openssl_server_npn_renegotiate;
+ TestCase == erlang_server_openssl_client_npn_only_server;
+ TestCase == erlang_server_openssl_client_npn_only_client;
+ TestCase == erlang_client_openssl_server_npn_only_client;
+ TestCase == erlang_client_openssl_server_npn_only_server ->
+ check_openssl_npn_support(Config);
special_init(_, Config) ->
Config.
@@ -153,37 +162,69 @@ end_per_testcase(_, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [erlang_client_openssl_server,
+ [
+ {group, basic},
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}
+ ].
+
+groups() ->
+ [{basic, [], basic_tests()},
+ {'tlsv1.2', [], all_versions_tests() ++ npn_tests()},
+ {'tlsv1.1', [], all_versions_tests() ++ npn_tests()},
+ {'tlsv1', [], all_versions_tests()++ npn_tests()},
+ {'sslv3', [], all_versions_tests()}].
+
+basic_tests() ->
+ [basic_erlang_client_openssl_server,
+ basic_erlang_server_openssl_client,
+ expired_session].
+
+all_versions_tests() ->
+ [
+ erlang_client_openssl_server,
erlang_server_openssl_client,
- tls1_erlang_client_openssl_server_dsa_cert,
- tls1_erlang_server_openssl_client_dsa_cert,
- ssl3_erlang_client_openssl_server_dsa_cert,
- ssl3_erlang_server_openssl_client_dsa_cert,
+ erlang_client_openssl_server_dsa_cert,
+ erlang_server_openssl_client_dsa_cert,
erlang_server_openssl_client_reuse_session,
erlang_client_openssl_server_renegotiate,
- erlang_client_openssl_server_no_wrap_sequence_number,
- erlang_server_openssl_client_no_wrap_sequence_number,
+ erlang_client_openssl_server_nowrap_seqnum,
+ erlang_server_openssl_client_nowrap_seqnum,
erlang_client_openssl_server_no_server_ca_cert,
- ssl3_erlang_client_openssl_server,
- ssl3_erlang_server_openssl_client,
- ssl3_erlang_client_openssl_server_client_cert,
- ssl3_erlang_server_openssl_client_client_cert,
- ssl3_erlang_server_erlang_client_client_cert,
- tls1_erlang_client_openssl_server,
- tls1_erlang_server_openssl_client,
- tls1_erlang_client_openssl_server_client_cert,
- tls1_erlang_server_openssl_client_client_cert,
- tls1_erlang_server_erlang_client_client_cert,
- ciphers_rsa_signed_certs, ciphers_dsa_signed_certs,
+ erlang_client_openssl_server_client_cert,
+ erlang_server_openssl_client_client_cert,
+ ciphers_rsa_signed_certs,
+ ciphers_dsa_signed_certs,
erlang_client_bad_openssl_server,
expired_session,
ssl2_erlang_server_openssl_client].
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
+npn_tests() ->
+ [erlang_client_openssl_server_npn,
+ erlang_server_openssl_client_npn,
+ erlang_server_openssl_client_npn_renegotiate,
+ erlang_client_openssl_server_npn_renegotiate,
+ erlang_server_openssl_client_npn_only_client,
+ erlang_server_openssl_client_npn_only_server,
+ erlang_client_openssl_server_npn_only_client,
+ erlang_client_openssl_server_npn_only_server].
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case check_sane_openssl_version(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName),
+ Config;
+ false ->
+ {skip, openssl_does_not_support_version}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
end_per_group(_GroupName, Config) ->
Config.
@@ -191,12 +232,11 @@ end_per_group(_GroupName, Config) ->
%% Test cases starts here.
%%--------------------------------------------------------------------
-
-erlang_client_openssl_server(doc) ->
+basic_erlang_client_openssl_server(doc) ->
["Test erlang client with openssl server"];
-erlang_client_openssl_server(suite) ->
+basic_erlang_client_openssl_server(suite) ->
[];
-erlang_client_openssl_server(Config) when is_list(Config) ->
+basic_erlang_client_openssl_server(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
ClientOpts = ?config(client_opts, Config),
@@ -208,8 +248,8 @@ erlang_client_openssl_server(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(node()),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile,
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -234,13 +274,12 @@ erlang_client_openssl_server(Config) when is_list(Config) ->
process_flag(trap_exit, false),
ok.
-
%%--------------------------------------------------------------------
-erlang_server_openssl_client(doc) ->
+basic_erlang_server_openssl_client(doc) ->
["Test erlang server with openssl client"];
-erlang_server_openssl_client(suite) ->
+basic_erlang_server_openssl_client(suite) ->
[];
-erlang_server_openssl_client(Config) when is_list(Config) ->
+basic_erlang_server_openssl_client(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
@@ -253,8 +292,8 @@ erlang_server_openssl_client(Config) when is_list(Config) ->
{mfa, {?MODULE, erlang_ssl_receive, [Data]}},
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
" -host localhost",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -269,30 +308,26 @@ erlang_server_openssl_client(Config) when is_list(Config) ->
close_port(OpenSslPort),
process_flag(trap_exit, false),
ok.
-
-%%--------------------------------------------------------------------
-
-tls1_erlang_client_openssl_server_dsa_cert(doc) ->
- ["Test erlang server with openssl client"];
-tls1_erlang_client_openssl_server_dsa_cert(suite) ->
+%%--------------------------------------------------------------------
+erlang_client_openssl_server(doc) ->
+ ["Test erlang client with openssl server"];
+erlang_client_openssl_server(suite) ->
[];
-tls1_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
+erlang_client_openssl_server(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- ClientOpts = ?config(client_dsa_opts, Config),
- ServerOpts = ?config(server_dsa_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
{ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
+
Data = "From openssl to erlang",
Port = ssl_test_lib:inet_port(node()),
- CaCertFile = proplists:get_value(cacertfile, ServerOpts),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
- " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -Verify 2 -tls1 -msg",
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile,
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -306,44 +341,39 @@ tls1_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
{mfa, {?MODULE,
erlang_ssl_receive, [Data]}},
{options, ClientOpts}]),
-
port_command(OpensslPort, Data),
- ssl_test_lib:check_result(Client, ok),
-
+ ssl_test_lib:check_result(Client, ok),
+
%% Clean close down! Server needs to be closed first !!
close_port(OpensslPort),
ssl_test_lib:close(Client),
process_flag(trap_exit, false),
ok.
-%%--------------------------------------------------------------------
-tls1_erlang_server_openssl_client_dsa_cert(doc) ->
+%%--------------------------------------------------------------------
+erlang_server_openssl_client(doc) ->
["Test erlang server with openssl client"];
-tls1_erlang_server_openssl_client_dsa_cert(suite) ->
+erlang_server_openssl_client(suite) ->
[];
-tls1_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
+erlang_server_openssl_client(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- ClientOpts = ?config(client_dsa_opts, Config),
- ServerOpts = ?config(server_dsa_verify_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
{_, ServerNode, _} = ssl_test_lib:run_where(Config),
Data = "From openssl to erlang",
- CaCertFile = proplists:get_value(cacertfile, ClientOpts),
- CertFile = proplists:get_value(certfile, ClientOpts),
- KeyFile = proplists:get_value(keyfile, ClientOpts),
-
+
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
{mfa, {?MODULE, erlang_ssl_receive, [Data]}},
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
- " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -tls1 -msg",
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
+ " -host localhost",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -351,7 +381,7 @@ tls1_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
port_command(OpenSslPort, Data),
ssl_test_lib:check_result(Server, ok),
-
+
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close(Server),
close_port(OpenSslPort),
@@ -360,11 +390,11 @@ tls1_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-ssl3_erlang_client_openssl_server_dsa_cert(doc) ->
+erlang_client_openssl_server_dsa_cert(doc) ->
["Test erlang server with openssl client"];
-ssl3_erlang_client_openssl_server_dsa_cert(suite) ->
+erlang_client_openssl_server_dsa_cert(suite) ->
[];
-ssl3_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
+erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ClientOpts = ?config(client_dsa_opts, Config),
ServerOpts = ?config(server_dsa_opts, Config),
@@ -377,10 +407,11 @@ ssl3_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
CaCertFile = proplists:get_value(cacertfile, ServerOpts),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -Verify 2 -ssl3 -msg",
+ ++ " -key " ++ KeyFile ++ " -Verify 2 -msg",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -404,49 +435,46 @@ ssl3_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
ssl_test_lib:close(Client),
process_flag(trap_exit, false),
ok.
-
-%%--------------------------------------------------------------------
-
-ssl3_erlang_server_openssl_client_dsa_cert(doc) ->
+%%--------------------------------------------------------------------
+erlang_server_openssl_client_dsa_cert(doc) ->
["Test erlang server with openssl client"];
-ssl3_erlang_server_openssl_client_dsa_cert(suite) ->
+erlang_server_openssl_client_dsa_cert(suite) ->
[];
-ssl3_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
+erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ClientOpts = ?config(client_dsa_opts, Config),
- ServerOpts = ?config(server_dsa_verify_opts, Config),
+ ServerOpts = ?config(server_dsa_verify_opts, Config),
{_, ServerNode, _} = ssl_test_lib:run_where(Config),
-
+
Data = "From openssl to erlang",
CaCertFile = proplists:get_value(cacertfile, ClientOpts),
CertFile = proplists:get_value(certfile, ClientOpts),
KeyFile = proplists:get_value(keyfile, ClientOpts),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {options, ServerOpts}]),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
- " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -ssl3 -msg",
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
+ " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
+ ++ " -key " ++ KeyFile ++ " -msg",
test_server:format("openssl cmd: ~p~n", [Cmd]),
-
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
port_command(OpenSslPort, Data),
-
+
ssl_test_lib:check_result(Server, ok),
-
+
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close(Server),
close_port(OpenSslPort),
process_flag(trap_exit, false),
ok.
-
%%--------------------------------------------------------------------
erlang_server_openssl_client_reuse_session(doc) ->
@@ -468,8 +496,8 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) ->
{reconnect_times, 5},
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -host localhost -reconnect",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -505,8 +533,9 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(node()),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -536,14 +565,14 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-erlang_client_openssl_server_no_wrap_sequence_number(doc) ->
+erlang_client_openssl_server_nowrap_seqnum(doc) ->
["Test that erlang client will renegotiate session when",
"max sequence number celing is about to be reached. Although"
"in the testcase we use the test option renegotiate_at"
" to lower treashold substantially."];
-erlang_client_openssl_server_no_wrap_sequence_number(suite) ->
+erlang_client_openssl_server_nowrap_seqnum(suite) ->
[];
-erlang_client_openssl_server_no_wrap_sequence_number(Config) when is_list(Config) ->
+erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
ClientOpts = ?config(client_opts, Config),
@@ -556,8 +585,8 @@ erlang_client_openssl_server_no_wrap_sequence_number(Config) when is_list(Config
Port = ssl_test_lib:inet_port(node()),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -582,15 +611,15 @@ erlang_client_openssl_server_no_wrap_sequence_number(Config) when is_list(Config
process_flag(trap_exit, false),
ok.
%%--------------------------------------------------------------------
-erlang_server_openssl_client_no_wrap_sequence_number(doc) ->
+erlang_server_openssl_client_nowrap_seqnum(doc) ->
["Test that erlang client will renegotiate session when",
"max sequence number celing is about to be reached. Although"
"in the testcase we use the test option renegotiate_at"
" to lower treashold substantially."];
-erlang_server_openssl_client_no_wrap_sequence_number(suite) ->
+erlang_server_openssl_client_nowrap_seqnum(suite) ->
[];
-erlang_server_openssl_client_no_wrap_sequence_number(Config) when is_list(Config) ->
+erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
@@ -606,8 +635,8 @@ erlang_server_openssl_client_no_wrap_sequence_number(Config) when is_list(Config
trigger_renegotiate, [[Data, N+2]]}},
{options, [{renegotiate_at, N}, {reuse_sessions, false} | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -host localhost -msg",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -643,8 +672,8 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(node()),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -671,85 +700,11 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) ->
ok.
%%--------------------------------------------------------------------
-ssl3_erlang_client_openssl_server(doc) ->
- ["Test erlang client with openssl server"];
-ssl3_erlang_client_openssl_server(suite) ->
- [];
-ssl3_erlang_client_openssl_server(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_opts, Config),
- ClientOpts = ?config(client_opts, Config),
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Port = ssl_test_lib:inet_port(node()),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -ssl3",
-
- test_server:format("openssl cmd: ~p~n", [Cmd]),
-
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
-
- wait_for_openssl_server(),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- connection_info, [sslv3]}},
- {options,
- [{versions, [sslv3]} | ClientOpts]}]),
- ssl_test_lib:check_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- close_port(OpensslPort),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-
-ssl3_erlang_server_openssl_client(doc) ->
- ["Test erlang server with openssl client"];
-ssl3_erlang_server_openssl_client(suite) ->
- [];
-ssl3_erlang_server_openssl_client(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_opts, Config),
-
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa,
- {?MODULE, connection_info, [sslv3]}},
- {options,
- [{versions, [sslv3]} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
- " -host localhost -ssl3",
-
- test_server:format("openssl cmd: ~p~n", [Cmd]),
-
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
-
- ssl_test_lib:check_result(Server, ok),
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close(Server),
- close_port(OpenSslPort),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-ssl3_erlang_client_openssl_server_client_cert(doc) ->
+erlang_client_openssl_server_client_cert(doc) ->
["Test erlang client with openssl server when client sends cert"];
-ssl3_erlang_client_openssl_server_client_cert(suite) ->
+erlang_client_openssl_server_client_cert(suite) ->
[];
-ssl3_erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
+erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_verification_opts, Config),
ClientOpts = ?config(client_verification_opts, Config),
@@ -762,10 +717,10 @@ ssl3_erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
CertFile = proplists:get_value(certfile, ServerOpts),
CaCertFile = proplists:get_value(cacertfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -Verify 2 -ssl3",
+ ++ " -key " ++ KeyFile ++ " -Verify 2",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -791,11 +746,11 @@ ssl3_erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-ssl3_erlang_server_openssl_client_client_cert(doc) ->
+erlang_server_openssl_client_client_cert(doc) ->
["Test erlang server with openssl client when client sends cert"];
-ssl3_erlang_server_openssl_client_client_cert(suite) ->
+erlang_server_openssl_client_client_cert(suite) ->
[];
-ssl3_erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
+erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_verification_opts, Config),
ClientOpts = ?config(client_verification_opts, Config),
@@ -816,10 +771,10 @@ ssl3_erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
CaCertFile = proplists:get_value(cacertfile, ClientOpts),
CertFile = proplists:get_value(certfile, ClientOpts),
KeyFile = proplists:get_value(keyfile, ClientOpts),
-
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
Cmd = "openssl s_client -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++
- " -host localhost -ssl3",
+ ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
+ " -host localhost",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -837,15 +792,15 @@ ssl3_erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-ssl3_erlang_server_erlang_client_client_cert(doc) ->
+erlang_server_erlang_client_client_cert(doc) ->
["Test erlang server with erlang client when client sends cert"];
-ssl3_erlang_server_erlang_client_client_cert(suite) ->
+erlang_server_erlang_client_client_cert(suite) ->
[];
-ssl3_erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
+erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_verification_opts, Config),
ClientOpts = ?config(client_verification_opts, Config),
-
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Data = "From erlang to erlang",
@@ -867,7 +822,7 @@ ssl3_erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
%% Due to 1/n-1 splitting countermeasure Rizzo/Duong-Beast
{mfa, {ssl, send, [Data]}},
{options,
- [{versions, [sslv3]} | ClientOpts]}]),
+ [{versions, [Version]} | ClientOpts]}]),
ssl_test_lib:check_result(Server, ok, Client, ok),
@@ -875,215 +830,8 @@ ssl3_erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
ssl_test_lib:close(Client),
process_flag(trap_exit, false),
ok.
-
-
-%%--------------------------------------------------------------------
-
-tls1_erlang_client_openssl_server(doc) ->
- ["Test erlang client with openssl server"];
-tls1_erlang_client_openssl_server(suite) ->
- [];
-tls1_erlang_client_openssl_server(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_opts, Config),
- ClientOpts = ?config(client_opts, Config),
-
-
- test_server:format("Server Opts", [ServerOpts]),
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Port = ssl_test_lib:inet_port(node()),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -tls1",
-
- test_server:format("openssl cmd: ~p~n", [Cmd]),
-
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
-
- wait_for_openssl_server(),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- connection_info, [tlsv1]}},
- {options,
- [{versions, [tlsv1]} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- close_port(OpensslPort),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-
-tls1_erlang_server_openssl_client(doc) ->
- ["Test erlang server with openssl client"];
-tls1_erlang_server_openssl_client(suite) ->
- [];
-tls1_erlang_server_openssl_client(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_opts, Config),
-
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa,
- {?MODULE, connection_info, [tlsv1]}},
- {options,
- [{versions, [tlsv1]} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
- " -host localhost -tls1",
-
- test_server:format("openssl cmd: ~p~n", [Cmd]),
-
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
-
- ssl_test_lib:check_result(Server, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close(Server),
- close_port(OpenSslPort),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-
-tls1_erlang_client_openssl_server_client_cert(doc) ->
- ["Test erlang client with openssl server when client sends cert"];
-tls1_erlang_client_openssl_server_client_cert(suite) ->
- [];
-tls1_erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_verification_opts, Config),
- ClientOpts = ?config(client_verification_opts, Config),
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Port = ssl_test_lib:inet_port(node()),
- CaCertFile = proplists:get_value(cacertfile, ServerOpts),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
- " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -Verify 2 -tls1",
-
- test_server:format("openssl cmd: ~p~n", [Cmd]),
-
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
-
- wait_for_openssl_server(),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options, ClientOpts}]),
- port_command(OpensslPort, Data),
-
- ssl_test_lib:check_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- close_port(OpensslPort),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
- ok.
-
%%--------------------------------------------------------------------
-tls1_erlang_server_openssl_client_client_cert(doc) ->
- ["Test erlang server with openssl client when client sends cert"];
-tls1_erlang_server_openssl_client_client_cert(suite) ->
- [];
-tls1_erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_verification_opts, Config),
- ClientOpts = ?config(client_verification_opts, Config),
-
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options,
- [{verify , verify_peer}
- | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- CaCertFile = proplists:get_value(cacertfile, ClientOpts),
- CertFile = proplists:get_value(certfile, ClientOpts),
- KeyFile = proplists:get_value(keyfile, ClientOpts),
-
- Cmd = "openssl s_client -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++
- " -host localhost -tls1",
-
- test_server:format("openssl cmd: ~p~n", [Cmd]),
-
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
- port_command(OpenSslPort, Data),
-
- ssl_test_lib:check_result(Server, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close(Server),
- close_port(OpenSslPort),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-tls1_erlang_server_erlang_client_client_cert(doc) ->
- ["Test erlang server with erlang client when client sends cert"];
-tls1_erlang_server_erlang_client_client_cert(suite) ->
- [];
-tls1_erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ?config(server_verification_opts, Config),
- ClientOpts = ?config(client_verification_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From erlang to erlang",
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options,
- [{verify , verify_peer}
- | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl, send, [Data]}},
- {options,
- [{versions, [tlsv1]} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- process_flag(trap_exit, false),
- ok.
-%%--------------------------------------------------------------------
-
ciphers_rsa_signed_certs(doc) ->
["Test cipher suites that uses rsa certs"];
@@ -1190,12 +938,6 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
process_flag(trap_exit, false),
Return.
-
-version_flag(tlsv1) ->
- " -tls1 ";
-version_flag(sslv3) ->
- " -ssl3 ".
-
%%--------------------------------------------------------------------
erlang_client_bad_openssl_server(doc) ->
[""];
@@ -1211,8 +953,8 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(node()),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -1226,7 +968,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) ->
{from, self()},
{mfa, {?MODULE, server_sent_garbage, []}},
{options,
- [{versions, [tlsv1]} | ClientOpts]}]),
+ [{versions, [Version]} | ClientOpts]}]),
%% Send garbage
port_command(OpensslPort, ?OPENSSL_GARBAGE),
@@ -1245,7 +987,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) ->
{from, self()},
{mfa, {ssl_test_lib, no_result_msg, []}},
{options,
- [{versions, [tlsv1]} | ClientOpts]}]),
+ [{versions, [Version]} | ClientOpts]}]),
%% Clean close down! Server needs to be closed first !!
close_port(OpensslPort),
@@ -1271,8 +1013,8 @@ expired_session(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(node()),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "",
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -1348,6 +1090,248 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
ok.
%%--------------------------------------------------------------------
+erlang_client_openssl_server_npn(doc) ->
+ ["Test erlang client with openssl server doing npn negotiation"];
+erlang_client_openssl_server_npn(suite) ->
+ [];
+erlang_client_openssl_server_npn(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, fun(Client, OpensslPort) ->
+ port_command(OpensslPort, Data),
+
+ ssl_test_lib:check_result(Client, ok)
+ end),
+
+ ok.
+
+
+%%--------------------------------------------------------------------
+erlang_client_openssl_server_npn_renegotiate(doc) ->
+ ["Test erlang client with openssl server doing npn negotiation and renegotiate"];
+erlang_client_openssl_server_npn_renegotiate(suite) ->
+ [];
+erlang_client_openssl_server_npn_renegotiate(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, fun(Client, OpensslPort) ->
+ port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
+ test_server:sleep(?SLEEP),
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Client, ok)
+ end),
+ ok.
+
+
+%%--------------------------------------------------------------------------
+
+
+erlang_server_openssl_client_npn(doc) ->
+ ["Test erlang server with openssl client and npn negotiation"];
+erlang_server_openssl_client_npn(suite) ->
+ [];
+erlang_server_openssl_client_npn(Config) when is_list(Config) ->
+
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------------
+
+erlang_server_openssl_client_npn_renegotiate(doc) ->
+ ["Test erlang server with openssl client and npn negotiation with renegotiation"];
+erlang_server_openssl_client_npn_renegotiate(suite) ->
+ [];
+erlang_server_openssl_client_npn_renegotiate(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
+ test_server:sleep(?SLEEP),
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+%%--------------------------------------------------------------------------
+
+erlang_client_openssl_server_npn_only_server(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_with_opts(Config, [], "-nextprotoneg spdy/2", Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------------
+
+erlang_client_openssl_server_npn_only_client(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_with_opts(Config, [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}}], "", Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------------
+erlang_server_openssl_client_npn_only_server(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_with_opts(Config, [{next_protocols_advertised, [<<"spdy/2">>]}], "", Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+
+erlang_server_openssl_client_npn_only_client(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_with_opts(Config, [], "-nextprotoneg spdy/2", Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------------
+
+start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, OpensslServerOpts, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts0 = ?config(client_opts, Config),
+ ClientOpts = ErlangClientOpts ++ ClientOpts0,
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Cmd = "openssl s_server " ++ OpensslServerOpts ++ " -accept " ++
+ integer_to_list(Port) ++ version_flag(Version) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile,
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options, ClientOpts}]),
+
+ Callback(Client, OpensslPort),
+
+ %% Clean close down! Server needs to be closed first !!
+ close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false).
+
+start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts0 = ?config(client_opts, Config),
+ ClientOpts = [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}} | ClientOpts0],
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Cmd = "openssl s_server -msg -nextprotoneg http/1.1,spdy/2 -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile,
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive_and_assert_npn, [<<"spdy/2">>, Data]}},
+ {options, ClientOpts}]),
+
+ Callback(Client, OpensslPort),
+
+ %% Clean close down! Server needs to be closed first !!
+ close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false).
+
+start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = [{next_protocols_advertised, [<<"spdy/2">>]}, ServerOpts0],
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive_and_assert_npn, [<<"spdy/2">>, Data]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_client -nextprotoneg http/1.0,spdy/2 -msg -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
+ " -host localhost",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ Callback(Server, OpenSslPort),
+
+ ssl_test_lib:close(Server),
+
+ close_port(OpenSslPort),
+ process_flag(trap_exit, false).
+
+start_erlang_server_and_openssl_client_with_opts(Config, ErlangServerOpts, OpenSSLClientOpts, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = ErlangServerOpts ++ ServerOpts0,
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Cmd = "openssl s_client " ++ OpenSSLClientOpts ++ " -msg -port " ++ integer_to_list(Port) ++
+ " -host localhost",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ Callback(Server, OpenSslPort),
+
+ ssl_test_lib:close(Server),
+
+ close_port(OpenSslPort),
+ process_flag(trap_exit, false).
+
+
+erlang_ssl_receive_and_assert_npn(Socket, Protocol, Data) ->
+ {ok, Protocol} = ssl:negotiated_next_protocol(Socket),
+ erlang_ssl_receive(Socket, Data),
+ {ok, Protocol} = ssl:negotiated_next_protocol(Socket),
+ ok.
erlang_ssl_receive(Socket, Data) ->
test_server:format("Connection info: ~p~n",
@@ -1437,15 +1421,31 @@ wait_for_openssl_server() ->
%% more so than sleep!)
test_server:sleep(?SLEEP)
end.
-
+
+version_flag(tlsv1) ->
+ " -tls1 ";
+version_flag('tlsv1.1') ->
+ " -tls1_1 ";
+version_flag('tlsv1.2') ->
+ " -tls1_2 ";
+version_flag(sslv3) ->
+ " -ssl3 ".
+
+check_openssl_npn_support(Config) ->
+ HelpText = os:cmd("openssl s_client --help"),
+ case string:str(HelpText, "nextprotoneg") of
+ 0 ->
+ {skip, "Openssl not compiled with nextprotoneg support"};
+ _ ->
+ Config
+ end.
+
check_sane_openssl_renegotaite(Config) ->
case os:cmd("openssl version") of
"OpenSSL 0.9.8" ++ _ ->
- {skip, "Known renegotiation bug in OppenSSL"};
+ {skip, "Known renegotiation bug in OpenSSL"};
"OpenSSL 0.9.7" ++ _ ->
- {skip, "Known renegotiation bug in OppenSSL"};
- "OpenSSL 1.0.1c" ++ _ ->
- {skip, "Known renegotiation bug in OppenSSL"};
+ {skip, "Known renegotiation bug in OpenSSL"};
_ ->
Config
end.
@@ -1458,10 +1458,18 @@ check_sane_openssl_sslv2(Config) ->
Config
end.
-check_sane_openssl_dsa(Config) ->
- case os:cmd("openssl version") of
- "OpenSSL 1.0.1" ++ _ ->
- {skip, "known dsa bug in openssl"};
- _ ->
- Config
+check_sane_openssl_version(Version) ->
+ case {Version, os:cmd("openssl version")} of
+ {_, "OpenSSL 1.0.1" ++ _} ->
+ true;
+ {'tlsv1.2', "OpenSSL 1.0" ++ _} ->
+ false;
+ {'tlsv1.1', "OpenSSL 1.0" ++ _} ->
+ false;
+ {'tlsv1.2', "OpenSSL 0" ++ _} ->
+ false;
+ {'tlsv1.1', "OpenSSL 0" ++ _} ->
+ false;
+ {_, _} ->
+ true
end.
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index 0fccbfe908..e381b73c27 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 5.0.1
+SSL_VSN = 5.1
diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile
index c3138c4d9f..50f6427eaa 100644
--- a/lib/stdlib/doc/src/Makefile
+++ b/lib/stdlib/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/stdlib/doc/src/binary.xml b/lib/stdlib/doc/src/binary.xml
index 7b8e279788..06cfad0b0b 100644
--- a/lib/stdlib/doc/src/binary.xml
+++ b/lib/stdlib/doc/src/binary.xml
@@ -77,41 +77,30 @@
</datatypes>
<funcs>
<func>
- <name>at(Subject, Pos) -> byte()</name>
+ <name name="at" arity="2"/>
<fsummary>Returns the byte at a specific position in a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Pos = integer() >= 0</v>
- </type>
<desc>
- <p>Returns the byte at position <c>Pos</c> (zero-based) in the binary
- <c>Subject</c> as an integer. If <c>Pos</c> &gt;= <c>byte_size(Subject)</c>,
+ <p>Returns the byte at position <c><anno>Pos</anno></c> (zero-based) in the binary
+ <c><anno>Subject</anno></c> as an integer. If <c><anno>Pos</anno></c> &gt;= <c>byte_size(<anno>Subject</anno>)</c>,
a <c>badarg</c>
exception is raised.</p>
</desc>
</func>
<func>
- <name>bin_to_list(Subject) -> [byte()]</name>
+ <name name="bin_to_list" arity="1"/>
<fsummary>Convert a binary to a list of integers</fsummary>
- <type>
- <v>Subject = binary()</v>
- </type>
<desc>
- <p>The same as <c>bin_to_list(Subject,{0,byte_size(Subject)})</c>.</p>
+ <p>The same as <c>bin_to_list(<anno>Subject</anno>,{0,byte_size(<anno>Subject</anno>)})</c>.</p>
</desc>
</func>
<func>
- <name>bin_to_list(Subject, PosLen) -> [byte()]</name>
+ <name name="bin_to_list" arity="2"/>
<fsummary>Convert a binary to a list of integers</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>PosLen = part()</v>
- </type>
<desc>
- <p>Converts <c>Subject</c> to a list of <c>byte()</c>s, each representing
+ <p>Converts <c><anno>Subject</anno></c> to a list of <c>byte()</c>s, each representing
the value of one byte. The <c>part()</c> denotes which part of the
<c>binary()</c> to convert. Example:</p>
@@ -120,27 +109,19 @@
"rla"
%% or [114,108,97] in list notation.
</code>
- <p>If <c>PosLen</c> in any way references outside the binary, a <c>badarg</c> exception is raised.</p>
+ <p>If <c><anno>PosLen</anno></c> in any way references outside the binary, a <c>badarg</c> exception is raised.</p>
</desc>
</func>
<func>
- <name>bin_to_list(Subject, Pos, Len) -> [byte()]</name>
+ <name name="bin_to_list" arity="3"/>
<fsummary>Convert a binary to a list of integers</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Pos = integer() >= 0</v>
- <v>Len = integer() >= 0</v>
- </type>
<desc>
- <p>The same as<c> bin_to_list(Subject,{Pos,Len})</c>.</p>
+ <p>The same as<c> bin_to_list(<anno>Subject</anno>,{<anno>Pos</anno>,<anno>Len</anno>})</c>.</p>
</desc>
</func>
<func>
- <name>compile_pattern(Pattern) -> cp()</name>
+ <name name="compile_pattern" arity="1"/>
<fsummary>Pre-compiles a binary search pattern</fsummary>
- <type>
- <v>Pattern = binary() | [ binary() ]</v>
- </type>
<desc>
<p>Builds an internal structure representing a compilation of a
@@ -155,7 +136,7 @@
<p>When a list of binaries is given, it denotes a set of
alternative binaries to search for. I.e if
<c>[&lt;&lt;"functional"&gt;&gt;,&lt;&lt;"programming"&gt;&gt;]</c>
- is given as <c>Pattern</c>, this
+ is given as <c><anno>Pattern</anno></c>, this
means "either <c>&lt;&lt;"functional"&gt;&gt;</c> or
<c>&lt;&lt;"programming"&gt;&gt;</c>". The pattern is a set of
alternatives; when only a single binary is given, the set has
@@ -163,32 +144,25 @@
<p>The list of binaries used for search alternatives shall be flat and proper.</p>
- <p>If <c>Pattern</c> is not a binary or a flat proper list of binaries with length &gt; 0,
+ <p>If <c><anno>Pattern</anno></c> is not a binary or a flat proper list of binaries with length &gt; 0,
a <c>badarg</c> exception will be raised.</p>
</desc>
</func>
<func>
- <name>copy(Subject) -> binary()</name>
+ <name name="copy" arity="1"/>
<fsummary>Creates a duplicate of a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- </type>
<desc>
- <p>The same as <c>copy(Subject, 1)</c>.</p>
+ <p>The same as <c>copy(<anno>Subject</anno>, 1)</c>.</p>
</desc>
</func>
<func>
- <name>copy(Subject,N) -> binary()</name>
+ <name name="copy" arity="2"/>
<fsummary>Duplicates a binary N times and creates a new</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>N = integer() >= 0</v>
- </type>
<desc>
- <p>Creates a binary with the content of <c>Subject</c> duplicated <c>N</c> times.</p>
+ <p>Creates a binary with the content of <c><anno>Subject</anno></c> duplicated <c><anno>N</anno></c> times.</p>
- <p>This function will always create a new binary, even if <c>N =
+ <p>This function will always create a new binary, even if <c><anno>N</anno> =
1</c>. By using <c>copy/1</c> on a binary referencing a larger binary, one
might free up the larger binary for garbage collection.</p>
@@ -201,32 +175,23 @@
large binaries are no longer used in any process, deliberate
copying might be a good idea.</p> </note>
- <p>If <c>N</c> &lt; <c>0</c>, a <c>badarg</c> exception is raised.</p>
+ <p>If <c><anno>N</anno></c> &lt; <c>0</c>, a <c>badarg</c> exception is raised.</p>
</desc>
</func>
<func>
- <name>decode_unsigned(Subject) -> Unsigned</name>
+ <name name="decode_unsigned" arity="1"/>
<fsummary>Decode a whole binary into an integer of arbitrary size</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Unsigned = integer() >= 0</v>
- </type>
<desc>
- <p>The same as <c>decode_unsigned(Subject,big)</c>.</p>
+ <p>The same as <c>decode_unsigned(<anno>Subject</anno>, big)</c>.</p>
</desc>
</func>
<func>
- <name>decode_unsigned(Subject, Endianess) -> Unsigned</name>
+ <name name="decode_unsigned" arity="2"/>
<fsummary>Decode a whole binary into an integer of arbitrary size</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Endianess = big | little</v>
- <v>Unsigned = integer() >= 0</v>
- </type>
<desc>
<p>Converts the binary digit representation, in big or little
- endian, of a positive integer in <c>Subject</c> to an Erlang <c>integer()</c>.</p>
+ endian, of a positive integer in <c><anno>Subject</anno></c> to an Erlang <c>integer()</c>.</p>
<p>Example:</p>
@@ -237,22 +202,15 @@
</desc>
</func>
<func>
- <name>encode_unsigned(Unsigned) -> binary()</name>
+ <name name="encode_unsigned" arity="1"/>
<fsummary>Encodes an unsigned integer into the minimal binary</fsummary>
- <type>
- <v>Unsigned = integer() >= 0</v>
- </type>
<desc>
- <p>The same as <c>encode_unsigned(Unsigned,big)</c>.</p>
+ <p>The same as <c>encode_unsigned(<anno>Unsigned</anno>, big)</c>.</p>
</desc>
</func>
<func>
- <name>encode_unsigned(Unsigned,Endianess) -> binary()</name>
+ <name name="encode_unsigned" arity="2"/>
<fsummary>Encodes an unsigned integer into the minimal binary</fsummary>
- <type>
- <v>Unsigned = integer() >= 0</v>
- <v>Endianess = big | little</v>
- </type>
<desc>
<p>Converts a positive integer to the smallest possible
@@ -268,51 +226,39 @@
</desc>
</func>
<func>
- <name>first(Subject) -> byte()</name>
+ <name name="first" arity="1"/>
<fsummary>Returns the first byte of a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- </type>
<desc>
- <p>Returns the first byte of the binary <c>Subject</c> as an integer. If the
- size of <c>Subject</c> is zero, a <c>badarg</c> exception is raised.</p>
+ <p>Returns the first byte of the binary <c><anno>Subject</anno></c> as an integer. If the
+ size of <c><anno>Subject</anno></c> is zero, a <c>badarg</c> exception is raised.</p>
</desc>
</func>
<func>
- <name>last(Subject) -> byte()</name>
+ <name name="last" arity="1"/>
<fsummary>Returns the last byte of a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- </type>
<desc>
- <p>Returns the last byte of the binary <c>Subject</c> as an integer. If the
- size of <c>Subject</c> is zero, a <c>badarg</c> exception is raised.</p>
+ <p>Returns the last byte of the binary <c><anno>Subject</anno></c> as an integer. If the
+ size of <c><anno>Subject</anno></c> is zero, a <c>badarg</c> exception is raised.</p>
</desc>
</func>
<func>
- <name>list_to_bin(ByteList) -> binary()</name>
+ <name name="list_to_bin" arity="1"/>
<fsummary>Convert a list of integers and binaries to a binary</fsummary>
- <type>
- <v>ByteList = iodata() (see module erlang)</v>
- </type>
<desc>
<p>Works exactly as <c>erlang:list_to_binary/1</c>, added for completeness.</p>
</desc>
</func>
<func>
- <name>longest_common_prefix(Binaries) -> integer() >= 0</name>
+ <name name="longest_common_prefix" arity="1"/>
<fsummary>Returns length of longest common prefix for a set of binaries</fsummary>
- <type>
- <v>Binaries = [ binary() ]</v>
- </type>
<desc>
<p>Returns the length of the longest common prefix of the
- binaries in the list <c>Binaries</c>. Example:</p>
+ binaries in the list <c><anno>Binaries</anno></c>. Example:</p>
<code>
1> binary:longest_common_prefix([&lt;&lt;"erlang"&gt;&gt;,&lt;&lt;"ergonomy"&gt;&gt;]).
@@ -321,19 +267,16 @@
0
</code>
- <p>If <c>Binaries</c> is not a flat list of binaries, a <c>badarg</c> exception is raised.</p>
+ <p>If <c><anno>Binaries</anno></c> is not a flat list of binaries, a <c>badarg</c> exception is raised.</p>
</desc>
</func>
<func>
- <name>longest_common_suffix(Binaries) -> integer() >= 0</name>
+ <name name="longest_common_suffix" arity="1"/>
<fsummary>Returns length of longest common suffix for a set of binaries</fsummary>
- <type>
- <v>Binaries = [ binary() ]</v>
- </type>
<desc>
<p>Returns the length of the longest common suffix of the
- binaries in the list <c>Binaries</c>. Example:</p>
+ binaries in the list <c><anno>Binaries</anno></c>. Example:</p>
<code>
1> binary:longest_common_suffix([&lt;&lt;"erlang"&gt;&gt;,&lt;&lt;"fang"&gt;&gt;]).
@@ -347,35 +290,24 @@
</desc>
</func>
<func>
- <name>match(Subject, Pattern) -> Found | <c>nomatch</c></name>
+ <name name="match" arity="2"/>
<fsummary>Searches for the first match of a pattern in a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Pattern = binary() | [ binary() ] | cp()</v>
- <v>Found = part()</v>
- </type>
<desc>
- <p>The same as <c>match(Subject, Pattern, [])</c>.</p>
+ <p>The same as <c>match(<anno>Subject</anno>, <anno>Pattern</anno>, [])</c>.</p>
</desc>
</func>
<func>
- <name>match(Subject,Pattern,Options) -> Found | <c>nomatch</c></name>
+ <name name="match" arity="3"/>
+ <type name="part"/>
<fsummary>Searches for the first match of a pattern in a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Pattern = binary() | [ binary() ] | cp()</v>
- <v>Found = part()</v>
- <v>Options = [ Option ]</v>
- <v>Option = {scope, part()}</v>
- </type>
<desc>
- <p>Searches for the first occurrence of <c>Pattern</c> in <c>Subject</c> and
+ <p>Searches for the first occurrence of <c><anno>Pattern</anno></c> in <c><anno>Subject</anno></c> and
returns the position and length.</p>
- <p>The function will return <c>{Pos,Length}</c> for the binary
- in <c>Pattern</c> starting at the lowest position in
- <c>Subject</c>, Example:</p>
+ <p>The function will return <c>{Pos, Length}</c> for the binary
+ in <c><anno>Pattern</anno></c> starting at the lowest position in
+ <c><anno>Subject</anno></c>, Example:</p>
<code>
1> binary:match(&lt;&lt;"abcde"&gt;&gt;, [&lt;&lt;"bcde"&gt;&gt;,&lt;&lt;"cd"&gt;&gt;],[]).
@@ -391,16 +323,16 @@
<p>Summary of the options:</p>
<taglist>
- <tag>{scope, {Start, Length}}</tag>
+ <tag>{scope, {<anno>Start</anno>, <anno>Length</anno>}}</tag>
<item><p>Only the given part is searched. Return values still have
- offsets from the beginning of <c>Subject</c>. A negative <c>Length</c> is
- allowed as described in the <c>TYPES</c> section of this manual.</p></item>
+ offsets from the beginning of <c><anno>Subject</anno></c>. A negative <c>Length</c> is
+ allowed as described in the <c>DATA TYPES</c> section of this manual.</p></item>
</taglist>
<p>If none of the strings in
- <c>Pattern</c> is found, the atom <c>nomatch</c> is returned.</p>
+ <c><anno>Pattern</anno></c> is found, the atom <c>nomatch</c> is returned.</p>
- <p>For a description of <c>Pattern</c>, see
+ <p>For a description of <c><anno>Pattern</anno></c>, see
<seealso marker="#compile_pattern-1">compile_pattern/1</seealso>.</p>
<p>If <c>{scope, {Start,Length}}</c> is given in the options
@@ -412,32 +344,21 @@
</desc>
</func>
<func>
- <name>matches(Subject, Pattern) -> Found</name>
+ <name name="matches" arity="2"/>
<fsummary>Searches for all matches of a pattern in a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Pattern = binary() | [ binary() ] | cp()</v>
- <v>Found = [ part() ] | []</v>
- </type>
<desc>
- <p>The same as <c>matches(Subject, Pattern, [])</c>.</p>
+ <p>The same as <c>matches(<anno>Subject</anno>, <anno>Pattern</anno>, [])</c>.</p>
</desc>
</func>
<func>
- <name>matches(Subject,Pattern,Options) -> Found</name>
+ <name name="matches" arity="3"/>
+ <type name="part"/>
<fsummary>Searches for all matches of a pattern in a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Pattern = binary() | [ binary() ] | cp()</v>
- <v>Found = [ part() ] | []</v>
- <v>Options = [ Option ]</v>
- <v>Option = {scope, part()}</v>
- </type>
<desc>
- <p>Works like match, but the <c>Subject</c> is searched until
+ <p>Works like <c>match/2</c>, but the <c><anno>Subject</anno></c> is searched until
exhausted and a list of all non-overlapping parts matching
- <c>Pattern</c> is returned (in order). </p>
+ <c><anno>Pattern</anno></c> is returned (in order). </p>
<p>The first and longest match is preferred to a shorter,
which is illustrated by the following example:</p>
@@ -458,26 +379,22 @@
<p>If none of the strings in pattern is found, an empty list is returned.</p>
- <p>For a description of <c>Pattern</c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso> and for a
+ <p>For a description of <c><anno>Pattern</anno></c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso> and for a
description of available options, see <seealso marker="#match-3">match/3</seealso>.</p>
- <p>If <c>{scope, {Start,Length}}</c> is given in the options such that
- <c>Start</c> is larger than the size of <c>Subject</c>, <c>Start + Length</c> is
- less than zero or <c>Start + Length</c> is larger than the size of
- <c>Subject</c>, a <c>badarg</c> exception is raised.</p>
+ <p>If <c>{scope, {<anno>Start</anno>,<anno>Length</anno>}}</c> is given in the options such that
+ <c><anno>Start</anno></c> is larger than the size of <c><anno>Subject</anno></c>, <c><anno>Start</anno> + <anno>Length</anno></c> is
+ less than zero or <c><anno>Start</anno> + <anno>Length</anno></c> is larger than the size of
+ <c><anno>Subject</anno></c>, a <c>badarg</c> exception is raised.</p>
</desc>
</func>
<func>
- <name>part(Subject, PosLen) -> binary()</name>
+ <name name="part" arity="2"/>
<fsummary>Extracts a part of a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>PosLen = part()</v>
- </type>
<desc>
- <p>Extracts the part of the binary <c>Subject</c> described by <c>PosLen</c>.</p>
+ <p>Extracts the part of the binary <c><anno>Subject</anno></c> described by <c><anno>PosLen</anno></c>.</p>
<p>Negative length can be used to extract bytes at the end of a binary:</p>
@@ -494,25 +411,20 @@
<c>binary_part/3</c>. Those BIFs are allowed in guard tests.</p>
</note>
- <p>If <c>PosLen</c> in any way references outside the binary, a <c>badarg</c> exception
+ <p>If <c><anno>PosLen</anno></c> in any way references outside the binary, a <c>badarg</c> exception
is raised.</p>
</desc>
</func>
<func>
- <name>part(Subject, Pos, Len) -> binary()</name>
+ <name name="part" arity="3"/>
<fsummary>Extracts a part of a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Pos = integer() >= 0</v>
- <v>Len = integer()</v>
- </type>
<desc>
- <p>The same as <c>part(Subject, {Pos, Len})</c>.</p>
+ <p>The same as <c>part(<anno>Subject</anno>, {<anno>Pos</anno>, <anno>Len</anno>})</c>.</p>
</desc>
</func>
<func>
- <name>referenced_byte_size(binary()) -> integer() >= 0</name>
+ <name name="referenced_byte_size" arity="1"/>
<fsummary>Determines the size of the actual binary pointed out by a sub-binary</fsummary>
<desc>
diff --git a/lib/stdlib/doc/src/epp.xml b/lib/stdlib/doc/src/epp.xml
index 488499581f..386ed89fe1 100644
--- a/lib/stdlib/doc/src/epp.xml
+++ b/lib/stdlib/doc/src/epp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index 487b06473f..abaf64fb91 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -128,11 +128,17 @@
<datatypes>
<datatype>
+ <name name="access"/>
+ </datatype>
+ <datatype>
<name><marker id="type-continuation">continuation()</marker></name>
<desc>
<p>Opaque continuation used by <seealso marker="#select/1">
- <c>select/1</c></seealso> and <seealso marker="#select/3">
- <c>select/3</c></seealso>.</p>
+ <c>select/1,3</c></seealso>, <seealso marker="#select_reverse/1">
+ <c>select_reverse/1,3</c></seealso>, <seealso
+ marker="#match/1">
+ <c>match/1,3</c></seealso>, and <seealso marker="#match_object/1">
+ <c>match_object/1,3</c></seealso>.</p>
</desc>
</datatype>
<datatype>
@@ -140,6 +146,10 @@
<desc><p>A match specification, see above.</p></desc>
</datatype>
<datatype>
+ <name name="comp_match_spec"/>
+ <desc><p>A compiled match specification.</p></desc>
+ </datatype>
+ <datatype>
<name name="match_pattern"/>
</datatype>
<datatype>
@@ -149,14 +159,14 @@
<name name="tid"/>
<desc><p>A table identifier, as returned by new/2.</p></desc>
</datatype>
+ <datatype>
+ <name name="type"/>
+ </datatype>
</datatypes>
<funcs>
<func>
- <name>all() -> [Tab]</name>
+ <name name="all" arity="0"/>
<fsummary>Return a list of all ETS tables.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- </type>
<desc>
<p>Returns a list of all tables at the node. Named tables are
given by their names, unnamed tables are given by their
@@ -164,48 +174,34 @@
</desc>
</func>
<func>
- <name>delete(Tab) -> true</name>
+ <name name="delete" arity="1"/>
<fsummary>Delete an entire ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- </type>
<desc>
- <p>Deletes the entire table <c>Tab</c>.</p>
+ <p>Deletes the entire table <c><anno>Tab</anno></c>.</p>
</desc>
</func>
<func>
- <name>delete(Tab, Key) -> true</name>
+ <name name="delete" arity="2"/>
<fsummary>Delete all objects with a given key from an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key = term()</v>
- </type>
<desc>
- <p>Deletes all objects with the key <c>Key</c> from the table
- <c>Tab</c>.</p>
+ <p>Deletes all objects with the key <c><anno>Key</anno></c> from the table
+ <c><anno>Tab</anno></c>.</p>
</desc>
</func>
<func>
- <name>delete_all_objects(Tab) -> true</name>
+ <name name="delete_all_objects" arity="1"/>
<fsummary>Delete all objects in an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- </type>
<desc>
- <p>Delete all objects in the ETS table <c>Tab</c>.
+ <p>Delete all objects in the ETS table <c><anno>Tab</anno></c>.
The operation is guaranteed to be
<seealso marker="#concurrency">atomic and isolated</seealso>.</p>
</desc>
</func>
<func>
- <name>delete_object(Tab,Object) -> true</name>
+ <name name="delete_object" arity="2"/>
<fsummary>Deletes a specific from an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Object = tuple()</v>
- </type>
<desc>
- <p>Delete the exact object <c>Object</c> from the ETS table,
+ <p>Delete the exact object <c><anno>Object</anno></c> from the ETS table,
leaving objects with the same key but other differences
(useful for type <c>bag</c>). In a <c>duplicate_bag</c>, all
instances of the object will be deleted.</p>
@@ -257,14 +253,10 @@
</desc>
</func>
<func>
- <name>first(Tab) -> Key | '$end_of_table'</name>
+ <name name="first" arity="1"/>
<fsummary>Return the first key in an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key = term()</v>
- </type>
<desc>
- <p>Returns the first key <c>Key</c> in the table <c>Tab</c>.
+ <p>Returns the first key <c><anno>Key</anno></c> in the table <c><anno>Tab</anno></c>.
If the table is of the <c>ordered_set</c> type, the first key
in Erlang term order will be returned. If the table is of any
other type, the first key according to the table's internal
@@ -336,7 +328,7 @@
the source file.</p>
<p>The fun is very restricted, it can take only a single
parameter (the object to match): a sole variable or a
- tuple. It needs to use the <c>is_</c>XXX guard tests.
+ tuple. It needs to use the <c>is_</c> guard tests.
Language constructs that have no representation
in a match_spec (like <c>if</c>, <c>case</c>, <c>receive</c>
etc) are not allowed.</p>
@@ -386,19 +378,14 @@ Error: fun containing local Erlang function calls
</desc>
</func>
<func>
- <name>give_away(Tab, Pid, GiftData) -> true</name>
+ <name name="give_away" arity="3"/>
<fsummary>Change owner of a table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Pid = pid()</v>
- <v>GiftData = term()</v>
- </type>
<desc>
- <p>Make process <c>Pid</c> the new owner of table <c>Tab</c>.
+ <p>Make process <c><anno>Pid</anno></c> the new owner of table <c><anno>Tab</anno></c>.
If successful, the message
- <c>{'ETS-TRANSFER',Tab,FromPid,GiftData}</c> will be sent
+ <c>{'ETS-TRANSFER',<anno>Tab</anno>,FromPid,<anno>GiftData</anno>}</c> will be sent
to the new owner.</p>
- <p>The process <c>Pid</c> must be alive, local and not already the
+ <p>The process <c><anno>Pid</anno></c> must be alive, local and not already the
owner of the table. The calling process must be the table owner.</p>
<p>Note that <c>give_away</c> does not at all affect the
<seealso marker="#heir">heir</seealso> option of the table. A table
@@ -421,81 +408,72 @@ Error: fun containing local Erlang function calls
</desc>
</func>
<func>
- <name>info(Tab) -> [{Item, Value}] | undefined</name>
+ <name name="info" arity="1"/>
<fsummary>Return information about an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Item = atom(), see below</v>
- <v>Value = term(), see below</v>
- </type>
<desc>
- <p>Returns information about the table <c>Tab</c> as a list of
- <c>{Item, Value}</c> tuples. If <c>Tab</c> has the correct type
+ <p>Returns information about the table <c><anno>Tab</anno></c> as a list of
+ tuples. If <c><anno>Tab</anno></c> has the correct type
for a table identifier, but does not refer to an existing ETS
- table, <c>undefined</c> is returned. If <c>Tab</c> is not of the
+ table, <c>undefined</c> is returned. If <c><anno>Tab</anno></c> is not of the
correct type, this function fails with reason <c>badarg</c>.</p>
<list type="bulleted">
- <item><c>Item=memory, Value=integer()</c> <br></br>
+ <item><c>{compressed, boolean()}</c> <br></br>
- The number of words allocated to the table.</item>
- <item><c>Item=owner, Value=pid()</c> <br></br>
-
- The pid of the owner of the table.</item>
- <item><c>Item=heir, Value=pid()|none</c> <br></br>
+ Indicates if the table is compressed or not.</item>
+ <item><c>{heir, pid() | none}</c> <br></br>
The pid of the heir of the table, or <c>none</c> if no heir is set.</item>
- <item><c>Item=name, Value=atom()</c> <br></br>
+ <item><c>{keypos, integer() >= 1}</c> <br></br>
- The name of the table.</item>
- <item><c>Item=size, Value=integer()</c> <br></br>
+ The key position.</item>
+ <item><c>{memory, integer() >= 0</c> <br></br>
- The number of objects inserted in the table.</item>
- <item><c>Item=node, Value=atom()</c> <br></br>
+ The number of words allocated to the table.</item>
+ <item><c>{name, atom()}</c> <br></br>
- The node where the table is stored. This field is no longer
- meaningful as tables cannot be accessed from other nodes.</item>
- <item><c>Item=named_table, Value=true|false</c> <br></br>
+ The name of the table.</item>
+ <item><c>{named_table, boolean()}</c> <br></br>
Indicates if the table is named or not.</item>
- <item><c>Item=type, Value=set|ordered_set|bag|duplicate_bag</c> <br></br>
+ <item><c>{node, node()}</c> <br></br>
- The table type.</item>
- <item><c>Item=keypos, Value=integer()</c> <br></br>
+ The node where the table is stored. This field is no longer
+ meaningful as tables cannot be accessed from other nodes.</item>
+ <item><c>{owner, pid()}</c> <br></br>
- The key position.</item>
- <item><c>Item=protection, Value=public|protected|private</c> <br></br>
+ The pid of the owner of the table.</item>
+ <item><c>{protection, <seealso marker="#type-access">access()</seealso>}</c> <br></br>
The table access rights.</item>
- <item><c>Item=compressed, Value=true|false</c> <br></br>
+ <item><c>{size, integer() >= 0</c> <br></br>
- Indicates if the table is compressed or not.</item>
+ The number of objects inserted in the table.</item>
+ <item><c>{type, <seealso marker="#type-type">type()</seealso>}</c> <br></br>
+
+ The table type.</item>
</list>
</desc>
</func>
<func>
- <name>info(Tab, Item) -> Value | undefined</name>
+ <name name="info" arity="2"/>
<fsummary>Return the information associated with given item for an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Item, Value - see below</v>
- </type>
<desc>
<p>Returns the information associated with <c>Item</c> for
- the table <c>Tab</c>, or returns <c>undefined</c> if <c>Tab</c>
+ the table <c><anno>Tab</anno></c>, or returns <c>undefined</c> if <c>Tab</c>
does not refer an existing ETS table.
- If <c>Tab</c> is not of the correct type, or if <c>Item</c> is not
+ If <c><anno>Tab</anno></c> is not of the correct type, or if <c><anno>Item</anno></c> is not
one of the allowed values, this function fails with reason <c>badarg</c>.</p>
<warning><p>In R11B and earlier, this function would not fail but return
<c>undefined</c> for invalid values for <c>Item</c>.</p>
</warning>
- <p>In addition to the <c>{Item,Value}</c>
+ <p>In addition to the <c>{<anno>Item</anno>,<anno>Value</anno>}</c>
pairs defined for <c>info/1</c>, the following items are
allowed:</p>
<list type="bulleted">
- <item><c>Item=fixed, Value=true|false</c> <br></br>
+ <item><c>Item=fixed, Value=boolean()</c> <br></br>
Indicates if the table is fixed by any process or not.</item>
<item>
@@ -547,15 +525,11 @@ Error: fun containing local Erlang function calls
</desc>
</func>
<func>
- <name>insert(Tab, ObjectOrObjects) -> true</name>
+ <name name="insert" arity="2"/>
<fsummary>Insert an object into an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>ObjectOrObjects = tuple() | [tuple()]</v>
- </type>
<desc>
<p>Inserts the object or all of the objects in the list
- <c>ObjectOrObjects</c> into the table <c>Tab</c>.
+ <c><anno>ObjectOrObjects</anno></c> into the table <c><anno>Tab</anno></c>.
If the table is a <c>set</c> and the key of the inserted
objects <em>matches</em> the key of any object in the table,
the old object will be replaced. If the table is an
@@ -572,19 +546,15 @@ Error: fun containing local Erlang function calls
</desc>
</func>
<func>
- <name>insert_new(Tab, ObjectOrObjects) -> boolean()</name>
+ <name name="insert_new" arity="2"/>
<fsummary>Insert an object into an ETS table if the key is not already present.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>ObjectOrObjects = tuple() | [tuple()]</v>
- </type>
<desc>
<p>This function works exactly like <c>insert/2</c>, with the
exception that instead of overwriting objects with the same
key (in the case of <c>set</c> or <c>ordered_set</c>) or
adding more objects with keys already existing in the table
(in the case of <c>bag</c> and <c>duplicate_bag</c>), it
- simply returns <c>false</c>. If <c>ObjectOrObjects</c> is a
+ simply returns <c>false</c>. If <c><anno>ObjectOrObjects</anno></c> is a
list, the function checks <em>every</em> key prior to
inserting anything. Nothing will be inserted if not
<em>all</em> keys present in the list are absent from the
@@ -593,11 +563,8 @@ Error: fun containing local Erlang function calls
</desc>
</func>
<func>
- <name>is_compiled_ms(Term) -> boolean()</name>
+ <name name="is_compiled_ms" arity="1"/>
<fsummary>Checks if an Erlang term is the result of ets:match_spec_compile</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
<p>This function is used to check if a term is a valid
compiled <seealso marker="#match_spec">match_spec</seealso>.
@@ -626,14 +593,10 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>last(Tab) -> Key | '$end_of_table'</name>
+ <name name="last" arity="1"/>
<fsummary>Return the last key in an ETS table of type<c>ordered_set</c>.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key = term()</v>
- </type>
<desc>
- <p>Returns the last key <c>Key</c> according to Erlang term
+ <p>Returns the last key <c><anno>Key</anno></c> according to Erlang term
order in the table <c>Tab</c> of the <c>ordered_set</c> type.
If the table is of any other type, the function is synonymous
to <c>first/2</c>. If the table is empty,
@@ -642,16 +605,11 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>lookup(Tab, Key) -> [Object]</name>
+ <name name="lookup" arity="2"/>
<fsummary>Return all objects with a given key in an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key = term()</v>
- <v>Object = tuple()</v>
- </type>
<desc>
- <p>Returns a list of all objects with the key <c>Key</c> in
- the table <c>Tab</c>.</p>
+ <p>Returns a list of all objects with the key <c><anno>Key</anno></c> in
+ the table <c><anno>Tab</anno></c>.</p>
<p>In the case of <c>set, bag and duplicate_bag</c>, an object
is returned only if the given key <em>matches</em> the key
of the object in the table. If the table is an
@@ -681,22 +639,16 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>lookup_element(Tab, Key, Pos) -> Elem</name>
+ <name name="lookup_element" arity="3"/>
<fsummary>Return the <c>Pos</c>:th element of all objects with a given key in an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key = term()</v>
- <v>Pos = integer()</v>
- <v>Elem = term() | [term()]</v>
- </type>
<desc>
- <p>If the table <c>Tab</c> is of type <c>set</c> or
- <c>ordered_set</c>, the function returns the <c>Pos</c>:th
- element of the object with the key <c>Key</c>.</p>
+ <p>If the table <c><anno>Tab</anno></c> is of type <c>set</c> or
+ <c>ordered_set</c>, the function returns the <c><anno>Pos</anno></c>:th
+ element of the object with the key <c><anno>Key</anno></c>.</p>
<p>If the table is of type <c>bag</c> or <c>duplicate_bag</c>,
- the functions returns a list with the <c>Pos</c>:th element of
- every object with the key <c>Key</c>.</p>
- <p>If no object with the key <c>Key</c> exists, the function
+ the functions returns a list with the <c><anno>Pos</anno></c>:th element of
+ every object with the key <c><anno>Key</anno></c>.</p>
+ <p>If no object with the key <c><anno>Key</anno></c> exists, the function
will exit with reason <c>badarg</c>.</p>
<p>The difference between <c>set</c>, <c>bag</c> and
<c>duplicate_bag</c> on one hand, and <c>ordered_set</c> on
@@ -708,16 +660,11 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>match(Tab, Pattern) -> [Match]</name>
+ <name name="match" arity="2"/>
<fsummary>Match the objects in an ETS table against a pattern.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Pattern = tuple()</v>
- <v>Match = [term()]</v>
- </type>
<desc>
- <p>Matches the objects in the table <c>Tab</c> against the
- pattern <c>Pattern</c>.</p>
+ <p>Matches the objects in the table <c><anno>Tab</anno></c> against the
+ pattern <c><anno>Pattern</anno></c>.</p>
<p>A pattern is a term that may contain:</p>
<list type="bulleted">
<item>bound parts (Erlang terms),</item>
@@ -744,18 +691,12 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>match(Tab, Pattern, Limit) -> {[Match],Continuation} | '$end_of_table'</name>
+ <name name="match" arity="3"/>
<fsummary>Match the objects in an ETS table against a pattern and returns part of the answers.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Pattern = tuple()</v>
- <v>Match = [term()]</v>
- <v>Continuation = term()</v>
- </type>
<desc>
<p>Works like <c>ets:match/2</c> but only returns a limited
- (<c>Limit</c>) number of matching objects. The
- <c>Continuation</c> term can then be used in subsequent calls
+ (<c><anno>Limit</anno></c>) number of matching objects. The
+ <c><anno>Continuation</anno></c> term can then be used in subsequent calls
to <c>ets:match/1</c> to get the next chunk of matching
objects. This is a space efficient way to work on objects in a
table which is still faster than traversing the table object
@@ -764,16 +705,12 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>match(Continuation) -> {[Match],Continuation} | '$end_of_table'</name>
+ <name name="match" arity="1"/>
<fsummary>Continues matching objects in an ETS table.</fsummary>
- <type>
- <v>Match = [term()]</v>
- <v>Continuation = term()</v>
- </type>
<desc>
<p>Continues a match started with <c>ets:match/3</c>. The next
chunk of the size given in the initial <c>ets:match/3</c>
- call is returned together with a new <c>Continuation</c>
+ call is returned together with a new <c><anno>Continuation</anno></c>
that can be used in subsequent calls to this function.</p>
<p><c>'$end_of_table'</c> is returned when there are no more
objects in the table.</p>
@@ -789,15 +726,11 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>match_object(Tab, Pattern) -> [Object]</name>
+ <name name="match_object" arity="2"/>
<fsummary>Match the objects in an ETS table against a pattern.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Pattern = Object = tuple()</v>
- </type>
<desc>
- <p>Matches the objects in the table <c>Tab</c> against the
- pattern <c>Pattern</c>. See <c>match/2</c> for a description
+ <p>Matches the objects in the table <c><anno>Tab</anno></c> against the
+ pattern <c><anno>Pattern</anno></c>. See <c>match/2</c> for a description
of patterns. The function returns a list of all objects which
match the pattern.</p>
<p>If the key is specified in the pattern, the match is very
@@ -809,18 +742,12 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>match_object(Tab, Pattern, Limit) -> {[Match],Continuation} | '$end_of_table'</name>
+ <name name="match_object" arity="3"/>
<fsummary>Match the objects in an ETS table against a pattern and returns part of the answers.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Pattern = tuple()</v>
- <v>Match = [term()]</v>
- <v>Continuation = term()</v>
- </type>
<desc>
<p>Works like <c>ets:match_object/2</c> but only returns a
- limited (<c>Limit</c>) number of matching objects. The
- <c>Continuation</c> term can then be used in subsequent calls
+ limited (<c><anno>Limit</anno></c>) number of matching objects. The
+ <c><anno>Continuation</anno></c> term can then be used in subsequent calls
to <c>ets:match_object/1</c> to get the next chunk of matching
objects. This is a space efficient way to work on objects in a
table which is still faster than traversing the table object
@@ -829,29 +756,21 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>match_object(Continuation) -> {[Match],Continuation} | '$end_of_table'</name>
+ <name name="match_object" arity="1"/>
<fsummary>Continues matching objects in an ETS table.</fsummary>
- <type>
- <v>Match = [term()]</v>
- <v>Continuation = term()</v>
- </type>
<desc>
<p>Continues a match started with <c>ets:match_object/3</c>.
The next chunk of the size given in the initial
<c>ets:match_object/3</c> call is returned together with a
- new <c>Continuation</c> that can be used in subsequent calls
+ new <c><anno>Continuation</anno></c> that can be used in subsequent calls
to this function.</p>
<p><c>'$end_of_table'</c> is returned when there are no more
objects in the table.</p>
</desc>
</func>
<func>
- <name>match_spec_compile(MatchSpec) -> CompiledMatchSpec</name>
+ <name name="match_spec_compile" arity="1"/>
<fsummary>Compiles a match specification into its internal representation</fsummary>
- <type>
- <v>MatchSpec = match_spec()</v>
- <v>CompiledMatchSpec = comp_match_spec()</v>
- </type>
<desc>
<p>This function transforms a
<seealso marker="#match_spec">match_spec</seealso> into an
@@ -863,7 +782,7 @@ ets:is_compiled_ms(Broken).</code>
valid compiled match_spec, nor can it be stored on disk).
The validity of a compiled match_spec can be checked using
<c>ets:is_compiled_ms/1</c>.</p>
- <p>If the term <c>MatchSpec</c> can not be compiled (does not
+ <p>If the term <c><anno>MatchSpec</anno></c> can not be compiled (does not
represent a valid match_spec), a <c>badarg</c> fault is
thrown.</p>
<note>
@@ -873,25 +792,21 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>match_spec_run(List,CompiledMatchSpec) -> list()</name>
+ <name name="match_spec_run" arity="2"/>
<fsummary>Performs matching, using a compiled match_spec, on a list of tuples</fsummary>
- <type>
- <v>List = [ tuple() ]</v>
- <v>CompiledMatchSpec = comp_match_spec()</v>
- </type>
<desc>
<p>This function executes the matching specified in a
compiled <seealso marker="#match_spec">match_spec</seealso> on
- a list of tuples. The <c>CompiledMatchSpec</c> term should be
+ a list of tuples. The <c><anno>CompiledMatchSpec</anno></c> term should be
the result of a call to <c>ets:match_spec_compile/1</c> and
is hence the internal representation of the match_spec one
wants to use.</p>
- <p>The matching will be executed on each element in <c>List</c>
+ <p>The matching will be executed on each element in <c><anno>List</anno></c>
and the function returns a list containing all results. If an
- element in <c>List</c> does not match, nothing is returned
+ element in <c><anno>List</anno></c> does not match, nothing is returned
for that element. The length of the result list is therefore
equal or less than the the length of the parameter
- <c>List</c>. The two calls in the following example will give
+ <c><anno>List</anno></c>. The two calls in the following example will give
the same result (but certainly not the same execution
time...):</p>
<code type="none">
@@ -910,37 +825,23 @@ ets:select(Table,MatchSpec),</code>
</desc>
</func>
<func>
- <name>member(Tab, Key) -> true | false</name>
+ <name name="member" arity="2"/>
<fsummary>Tests for occurrence of a key in an ETS table</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key = term()</v>
- </type>
<desc>
<p>Works like <c>lookup/2</c>, but does not return the objects.
The function returns <c>true</c> if one or more elements in
- the table has the key <c>Key</c>, <c>false</c> otherwise.</p>
+ the table has the key <c><anno>Key</anno></c>, <c>false</c> otherwise.</p>
</desc>
</func>
<func>
- <name>new(Name, Options) -> tid() | atom()</name>
+ <name name="new" arity="2"/>
<fsummary>Create a new ETS table.</fsummary>
- <type>
- <v>Name = atom()</v>
- <v>Options = [Option]</v>
- <v>&nbsp;Option = Type | Access | named_table | {keypos,Pos} | {heir,pid(),HeirData} | {heir,none} | Tweaks</v>
- <v>&nbsp;&nbsp;Type = set | ordered_set | bag | duplicate_bag</v>
- <v>&nbsp;&nbsp;Access = public | protected | private</v>
- <v>&nbsp;&nbsp;Tweaks = {write_concurrency,boolean()} | {read_concurrency,boolean()} | compressed</v>
- <v>&nbsp;&nbsp;Pos = integer()</v>
- <v>&nbsp;&nbsp;HeirData = term()</v>
- </type>
<desc>
<p>Creates a new table and returns a table identifier which can
be used in subsequent operations. The table identifier can be
sent to other processes so that a table can be shared between
different processes within a node.</p>
- <p>The parameter <c>Options</c> is a list of atoms which
+ <p>The parameter <c><anno>Options</anno></c> is a list of atoms which
specifies table type, access rights, key position and if the
table is named or not. If one or more options are left out,
the default values are used. This means that not specifying
@@ -997,27 +898,27 @@ ets:select(Table,MatchSpec),</code>
</item>
<item>
<p><c>named_table</c>
- If this option is present, the name <c>Name</c> is
+ If this option is present, the name <c><anno>Name</anno></c> is
associated with the table identifier. The name can then
be used instead of the table identifier in subsequent
operations.</p>
</item>
<item>
- <p><c>{keypos,Pos}</c>
+ <p><c>{keypos,<anno>Pos</anno>}</c>
Specfies which element in the stored tuples should be
used as key. By default, it is the first element, i.e.
- <c>Pos=1</c>. However, this is not always appropriate. In
+ <c><anno>Pos</anno>=1</c>. However, this is not always appropriate. In
particular, we do not want the first element to be the
key if we want to store Erlang records in a table.</p>
<p>Note that any tuple stored in the table must have at
- least <c>Pos</c> number of elements.</p>
+ least <c><anno>Pos</anno></c> number of elements.</p>
</item>
<item>
<marker id="heir"></marker>
- <p><c>{heir,Pid,HeirData} | {heir,none}</c><br></br>
+ <p><c>{heir,<anno>Pid</anno>,<anno>HeirData</anno>} | {heir,none}</c><br></br>
Set a process as heir. The heir will inherit the table if
the owner terminates. The message
- <c>{'ETS-TRANSFER',tid(),FromPid,HeirData}</c> will be sent to
+ <c>{'ETS-TRANSFER',tid(),FromPid,<anno>HeirData</anno>}</c> will be sent to
the heir when that happens. The heir must be a local process.
Default heir is <c>none</c>, which will destroy the table when
the owner terminates.</p>
@@ -1082,15 +983,11 @@ ets:select(Table,MatchSpec),</code>
</desc>
</func>
<func>
- <name>next(Tab, Key1) -> Key2 | '$end_of_table'</name>
+ <name name="next" arity="2"/>
<fsummary>Return the next key in an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key1 = Key2 = term()</v>
- </type>
<desc>
- <p>Returns the next key <c>Key2</c>, following the key
- <c>Key1</c> in the table <c>Tab</c>. If the table is of the
+ <p>Returns the next key <c><anno>Key2</anno></c>, following the key
+ <c><anno>Key1</anno></c> in the table <c><anno>Tab</anno></c>. If the table is of the
<c>ordered_set</c> type, the next key in Erlang term order is
returned. If the table is of any other type, the next key
according to the table's internal order is returned. If there
@@ -1105,16 +1002,12 @@ ets:select(Table,MatchSpec),</code>
</desc>
</func>
<func>
- <name>prev(Tab, Key1) -> Key2 | '$end_of_table'</name>
+ <name name="prev" arity="2"/>
<fsummary>Return the previous key in an ETS table of type<c>ordered_set</c>.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key1 = Key2 = term()</v>
- </type>
<desc>
- <p>Returns the previous key <c>Key2</c>, preceding the key
- <c>Key1</c> according the Erlang term order in the table
- <c>Tab</c> of the <c>ordered_set</c> type. If the table is of
+ <p>Returns the previous key <c><anno>Key2</anno></c>, preceding the key
+ <c><anno>Key1</anno></c> according the Erlang term order in the table
+ <c><anno>Tab</anno></c> of the <c>ordered_set</c> type. If the table is of
any other type, the function is synonymous to <c>next/2</c>.
If there is no previous key, <c>'$end_of_table'</c> is
returned.</p>
@@ -1122,14 +1015,11 @@ ets:select(Table,MatchSpec),</code>
</desc>
</func>
<func>
- <name>rename(Tab, Name) -> Name</name>
+ <name name="rename" arity="2"/>
<fsummary>Rename a named ETS table.</fsummary>
- <type>
- <v>Tab = Name = atom()</v>
- </type>
<desc>
- <p>Renames the named table <c>Tab</c> to the new name
- <c>Name</c>. Afterwards, the old name can not be used to
+ <p>Renames the named table <c><anno>Tab</anno></c> to the new name
+ <c><anno>Name</anno></c>. Afterwards, the old name can not be used to
access the table. Renaming an unnamed table has no effect.</p>
</desc>
</func>
@@ -1186,18 +1076,15 @@ ets:select(ets:repair_continuation(Broken,MS)).</code>
</desc>
</func>
<func>
- <name>safe_fixtable(Tab, true|false) -> true</name>
+ <name name="safe_fixtable" arity="2"/>
<fsummary>Fix an ETS table for safe traversal.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- </type>
<desc>
<p>Fixes a table of the <c>set</c>, <c>bag</c> or
<c>duplicate_bag</c> table type for safe traversal.</p>
<p>A process fixes a table by calling
- <c>safe_fixtable(Tab,true)</c>. The table remains fixed until
+ <c>safe_fixtable(<anno>Tab</anno>, true)</c>. The table remains fixed until
the process releases it by calling
- <c>safe_fixtable(Tab,false)</c>, or until the process
+ <c>safe_fixtable(<anno>Tab</anno>, false)</c>, or until the process
terminates.</p>
<p>If several processes fix a table, the table will remain fixed
until all processes have released it (or terminated).
@@ -1242,15 +1129,10 @@ clean_all_with_value(Tab,X,Key) ->
</desc>
</func>
<func>
- <name>select(Tab, MatchSpec) -> [Match]</name>
+ <name name="select" arity="2"/>
<fsummary>Match the objects in an ETS table against a match_spec.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Match = term()</v>
- <v>MatchSpec = match_spec()</v>
- </type>
<desc>
- <p>Matches the objects in the table <c>Tab</c> using a
+ <p>Matches the objects in the table <c><anno>Tab</anno></c> using a
<seealso marker="#match_spec">match_spec</seealso>. This is a
more general call than the <c>ets:match/2</c> and
<c>ets:match_object/2</c> calls. In its simplest forms the
@@ -1337,18 +1219,12 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
- <name>select(Tab, MatchSpec, Limit) -> {[Match],Continuation} | '$end_of_table'</name>
+ <name name="select" arity="3"/>
<fsummary>Match the objects in an ETS table against a match_spec and returns part of the answers.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Match = term()</v>
- <v>MatchSpec = match_spec()</v>
- <v>Continuation = term()</v>
- </type>
<desc>
<p>Works like <c>ets:select/2</c> but only returns a limited
- (<c>Limit</c>) number of matching objects. The
- <c>Continuation</c> term can then be used in subsequent calls
+ (<c><anno>Limit</anno></c>) number of matching objects. The
+ <c><anno>Continuation</anno></c> term can then be used in subsequent calls
to <c>ets:select/1</c> to get the next chunk of matching
objects. This is a space efficient way to work on objects in a
table which is still faster than traversing the table object
@@ -1357,33 +1233,23 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
- <name>select(Continuation) -> {[Match],Continuation} | '$end_of_table'</name>
+ <name name="select" arity="1"/>
<fsummary>Continue matching objects in an ETS table.</fsummary>
- <type>
- <v>Match = term()</v>
- <v>Continuation = term()</v>
- </type>
<desc>
<p>Continues a match started with
<c>ets:select/3</c>. The next
chunk of the size given in the initial <c>ets:select/3</c>
- call is returned together with a new <c>Continuation</c>
+ call is returned together with a new <c><anno>Continuation</anno></c>
that can be used in subsequent calls to this function.</p>
<p><c>'$end_of_table'</c> is returned when there are no more
objects in the table.</p>
</desc>
</func>
<func>
- <name>select_count(Tab, MatchSpec) -> NumMatched</name>
+ <name name="select_count" arity="2"/>
<fsummary>Match the objects in an ETS table against a match_spec and returns the number of objects for which the match_spec returned 'true'</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Object = tuple()</v>
- <v>MatchSpec = match_spec()</v>
- <v>NumMatched = integer()</v>
- </type>
<desc>
- <p>Matches the objects in the table <c>Tab</c> using a
+ <p>Matches the objects in the table <c><anno>Tab</anno></c> using a
<seealso marker="#match_spec">match_spec</seealso>. If the
match_spec returns <c>true</c> for an object, that object
considered a match and is counted. For any other result from
@@ -1396,16 +1262,10 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
- <name>select_delete(Tab, MatchSpec) -> NumDeleted</name>
+ <name name="select_delete" arity="2"/>
<fsummary>Match the objects in an ETS table against a match_spec and deletes objects where the match_spec returns 'true'</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Object = tuple()</v>
- <v>MatchSpec = match_spec()</v>
- <v>NumDeleted = integer()</v>
- </type>
<desc>
- <p>Matches the objects in the table <c>Tab</c> using a
+ <p>Matches the objects in the table <c><anno>Tab</anno></c> using a
<seealso marker="#match_spec">match_spec</seealso>. If the
match_spec returns <c>true</c> for an object, that object is
removed from the table. For any other result from the
@@ -1422,13 +1282,8 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
- <name>select_reverse(Tab, MatchSpec) -> [Match]</name>
+ <name name="select_reverse" arity="2"/>
<fsummary>Match the objects in an ETS table against a match_spec.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Match = term()</v>
- <v>MatchSpec = match_spec()</v>
- </type>
<desc>
<p>Works like <c>select/2</c>, but returns the list in reverse
@@ -1438,14 +1293,8 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
- <name>select_reverse(Tab, MatchSpec, Limit) -> {[Match],Continuation} | '$end_of_table'</name>
+ <name name="select_reverse" arity="3"/>
<fsummary>Match the objects in an ETS table against a match_spec and returns part of the answers.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Match = term()</v>
- <v>MatchSpec = match_spec()</v>
- <v>Continuation = term()</v>
- </type>
<desc>
<p>Works like <c>select/3</c>, but for the <c>ordered_set</c>
@@ -1456,18 +1305,14 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
<p>Note that this is <em>not</em> equivalent to
reversing the result list of a <c>select/3</c> call, as the result list
- is not only reversed, but also contains the last <c>Limit</c>
+ is not only reversed, but also contains the last <c><anno>Limit</anno></c>
matching objects in the table, not the first.</p>
</desc>
</func>
<func>
- <name>select_reverse(Continuation) -> {[Match],Continuation} | '$end_of_table'</name>
+ <name name="select_reverse" arity="1"/>
<fsummary>Continue matching objects in an ETS table.</fsummary>
- <type>
- <v>Match = term()</v>
- <v>Continuation = term()</v>
- </type>
<desc>
<p>Continues a match started with
@@ -1477,7 +1322,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
returned list will also contain objects with keys in reverse
order.</p>
- <p>For all other table types, the behaviour is exatly that of <c>select/1</c>.</p>
+ <p>For all other table types, the behaviour is exactly that of <c>select/1</c>.</p>
<p>Example:</p>
<code>
1> T = ets:new(x,[ordered_set]).
@@ -1501,14 +1346,8 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
- <name>setopts(Tab, Opts) -> true</name>
+ <name name="setopts" arity="2"/>
<fsummary>Set table options.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Opts = Opt | [Opt]</v>
- <v>Opt = {heir,pid(),HeirData} | {heir,none}</v>
- <v>HeirData = term()</v>
- </type>
<desc>
<p>Set table options. The only option that currently is allowed to be
set after the table has been created is
@@ -1517,28 +1356,23 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
- <name>slot(Tab, I) -> [Object] | '$end_of_table'</name>
+ <name name="slot" arity="2"/>
<fsummary>Return all objects in a given slot of an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>I = integer()</v>
- <v>Object = tuple()</v>
- </type>
<desc>
<p>This function is mostly for debugging purposes, Normally
one should use <c>first/next</c> or <c>last/prev</c> instead.</p>
- <p>Returns all objects in the <c>I</c>:th slot of the table
- <c>Tab</c>. A table can be traversed by repeatedly calling
- the function, starting with the first slot <c>I=0</c> and
+ <p>Returns all objects in the <c><anno>I</anno></c>:th slot of the table
+ <c><anno>Tab</anno></c>. A table can be traversed by repeatedly calling
+ the function, starting with the first slot <c><anno>I</anno>=0</c> and
ending when <c>'$end_of_table'</c> is returned.
The function will fail with reason <c>badarg</c> if the
- <c>I</c> argument is out of range.</p>
+ <c><anno>I</anno></c> argument is out of range.</p>
<p>Unless a table of type <c>set</c>, <c>bag</c> or
<c>duplicate_bag</c> is protected using
<c>safe_fixtable/2</c>, see above, a traversal may fail if
concurrent updates are made to the table. If the table is of
type <c>ordered_set</c>, the function returns a list
- containing the <c>I</c>:th object in Erlang term order.</p>
+ containing the <c><anno>I</anno></c>:th object in Erlang term order.</p>
</desc>
</func>
<func>
@@ -1754,16 +1588,16 @@ true</pre>
</desc>
</func>
<func>
- <name>update_counter(Tab, Key, UpdateOp) -> Result</name>
- <name>update_counter(Tab, Key, [UpdateOp]) -> [Result]</name>
- <name>update_counter(Tab, Key, Incr) -> Result</name>
+ <name name="update_counter" arity="3" clause_i="1"/>
+ <name name="update_counter" arity="3" clause_i="2"/>
+ <name name="update_counter" arity="3" clause_i="3"/>
+ <type variable="Tab"/>
+ <type variable="Key"/>
+ <type variable="UpdateOp" name_i="1"/>
+ <type variable="Pos" name_i="1"/>
+ <type variable="Threshold" name_i="1"/>
+ <type variable="SetValue" name_i="1"/>
<fsummary>Update a counter object in an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key = term()</v>
- <v>UpdateOp = {Pos,Incr} | {Pos,Incr,Threshold,SetValue}</v>
- <v>Pos = Incr = Threshold = SetValue = Result = integer()</v>
- </type>
<desc>
<p>This function provides an efficient way to update one or more
counters, without the hassle of having to look up an object, update
@@ -1771,22 +1605,22 @@ true</pre>
into the table again. (The update is done atomically; i.e. no process
can access the ets table in the middle of the operation.)
</p>
- <p>It will destructively update the object with key <c>Key</c>
- in the table <c>Tab</c> by adding <c>Incr</c> to the element
- at the <c>Pos</c>:th position. The new counter value is
+ <p>It will destructively update the object with key <c><anno>Key</anno></c>
+ in the table <c><anno>Tab</anno></c> by adding <c><anno>Incr</anno></c> to the element
+ at the <c><anno>Pos</anno></c>:th position. The new counter value is
returned. If no position is specified, the element directly
following the key (<c><![CDATA[<keypos>+1]]></c>) is updated.</p>
- <p>If a <c>Threshold</c> is specified, the counter will be
- reset to the value <c>SetValue</c> if the following
+ <p>If a <c><anno>Threshold</anno></c> is specified, the counter will be
+ reset to the value <c><anno>SetValue</anno></c> if the following
conditions occur:</p>
<list type="bulleted">
- <item>The <c>Incr</c> is not negative (<c>>= 0</c>) and the
- result would be greater than (<c>></c>) <c>Threshold</c></item>
- <item>The <c>Incr</c> is negative (<c><![CDATA[< 0]]></c>) and the
+ <item>The <c><anno>Incr</anno></c> is not negative (<c>>= 0</c>) and the
+ result would be greater than (<c>></c>) <c><anno>Threshold</anno></c></item>
+ <item>The <c><anno>Incr</anno></c> is negative (<c><![CDATA[< 0]]></c>) and the
result would be less than (<c><![CDATA[<]]></c>)
- <c>Threshold</c></item>
+ <c><anno>Threshold</anno></c></item>
</list>
- <p>A list of <c>UpdateOp</c> can be supplied to do several update
+ <p>A list of <c><anno>UpdateOp</anno></c> can be supplied to do several update
operations within the object. The operations are carried out in the
order specified in the list. If the same counter position occurs
more than one time in the list, the corresponding counter will thus
@@ -1797,7 +1631,7 @@ true</pre>
returned. If the function should fail, no updates will be done at
all.
</p>
- <p>The given Key is used to identify the object by either
+ <p>The given <c><anno>Key</anno></c> is used to identify the object by either
<em>matching</em> the key of an object in a <c>set</c> table,
or <em>compare equal</em> to the key of an object in an
<c>ordered_set</c> table (see
@@ -1812,29 +1646,28 @@ true</pre>
<item>the object has the wrong arity,</item>
<item>the element to update is not an integer,</item>
<item>the element to update is also the key, or,</item>
- <item>any of <c>Pos</c>, <c>Incr</c>, <c>Threshold</c> or
- <c>SetValue</c> is not an integer</item>
+ <item>any of <c><anno>Pos</anno></c>, <c><anno>Incr</anno></c>, <c><anno>Threshold</anno></c> or
+ <c><anno>SetValue</anno></c> is not an integer</item>
</list>
</desc>
</func>
<func>
- <name>update_element(Tab, Key, {Pos,Value}) -> true | false</name>
- <name>update_element(Tab, Key, [{Pos,Value}]) -> true | false</name>
+ <name name="update_element" arity="3" clause_i="1"/>
+ <name name="update_element" arity="3" clause_i="2"/>
+ <type variable="Tab"/>
+ <type variable="Key"/>
+ <type variable="Value"/>
+ <type variable="Pos"/>
<fsummary>Updates the <c>Pos</c>:th element of the object with a given key in an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key = Value = term()</v>
- <v>Pos = integer()</v>
- </type>
<desc>
<p>This function provides an efficient way to update one or more
elements within an object, without the hassle of having to look up,
update and write back the entire object.
</p>
- <p>It will destructively update the object with key <c>Key</c>
- in the table <c>Tab</c>. The element at the <c>Pos</c>:th position
- will be given the value <c>Value</c>. </p>
- <p>A list of <c>{Pos,Value}</c> can be supplied to update several
+ <p>It will destructively update the object with key <c><anno>Key</anno></c>
+ in the table <c><anno>Tab</anno></c>. The element at the <c><anno>Pos</anno></c>:th position
+ will be given the value <c><anno>Value</anno></c>. </p>
+ <p>A list of <c>{<anno>Pos</anno>,<anno>Value</anno>}</c> can be supplied to update several
elements within the same object. If the same position occurs more
than one in the list, the last value in the list will be written. If
the list is empty or the function fails, no updates will be done at
@@ -1842,9 +1675,9 @@ true</pre>
can never see any intermediate results.
</p>
<p>The function returns <c>true</c> if an object with the key
- <c>Key</c> was found, <c>false</c> otherwise.
+ <c><anno>Key</anno></c> was found, <c>false</c> otherwise.
</p>
- <p>The given Key is used to identify the object by either
+ <p>The given <c><anno>Key</anno></c> is used to identify the object by either
<em>matching</em> the key of an object in a <c>set</c> table,
or <em>compare equal</em> to the key of an object in an
<c>ordered_set</c> table (see
@@ -1855,7 +1688,7 @@ true</pre>
<list type="bulleted">
<item>the table is not of type <c>set</c> or
<c>ordered_set</c>,</item>
- <item><c>Pos</c> is less than 1 or greater than the object
+ <item><c><anno>Pos</anno></c> is less than 1 or greater than the object
arity, or,</item>
<item>the element to update is also the key</item>
</list>
diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml
index f3079c7337..cec20aee8e 100644
--- a/lib/stdlib/doc/src/filelib.xml
+++ b/lib/stdlib/doc/src/filelib.xml
@@ -150,6 +150,11 @@
<p>Matches any number of characters up to the end of
the filename, the next dot, or the next slash.</p>
</item>
+ <tag>**</tag>
+ <item>
+ <p>Two adjacent <c>*</c>'s used as a single pattern will
+ match all files and zero or more directories and subdirectories.</p>
+ </item>
<tag>[Character1,Character2,...]</tag>
<item>
<p>Matches any of the characters listed. Two characters
@@ -192,6 +197,10 @@
<c>src</c> or <c>include</c> directories, use:</p>
<code type="none">
filelib:wildcard("lib/*/{src,include}/*.{erl,hrl}") </code>
+ <p>To find all <c>.erl</c> or <c>.hrl</c> files in any
+ subdirectory, use:</p>
+ <code type="none">
+ filelib:wildcard("lib/**/*.{erl,hrl}") </code>
</desc>
</func>
<func>
diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml
index 8b31f3ac3d..b6c0fa4e05 100644
--- a/lib/stdlib/doc/src/lists.xml
+++ b/lib/stdlib/doc/src/lists.xml
@@ -248,18 +248,13 @@ flatmap(Fun, List1) ->
</desc>
</func>
<func>
- <name>keyfind(Key, N, TupleList) -> Tuple | false</name>
+ <name name="keyfind" arity="3"/>
+ <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc>
<fsummary>Search for an element in a list of tuples</fsummary>
- <type>
- <v>Key = term()</v>
- <v>N = 1..tuple_size(Tuple)</v>
- <v>TupleList = [Tuple]</v>
- <v>Tuple = tuple()</v>
- </type>
- <desc>
- <p>Searches the list of tuples <c>TupleList</c> for a
- tuple whose <c>N</c>th element compares equal to <c>Key</c>.
- Returns <c>Tuple</c> if such a tuple is found,
+ <desc>
+ <p>Searches the list of tuples <c><anno>TupleList</anno></c> for a
+ tuple whose <c><anno>N</anno></c>th element compares equal to <c><anno>Key</anno></c>.
+ Returns <c><anno>Tuple</anno></c> if such a tuple is found,
otherwise <c>false</c>.</p>
</desc>
</func>
@@ -281,17 +276,12 @@ flatmap(Fun, List1) ->
</desc>
</func>
<func>
- <name>keymember(Key, N, TupleList) -> boolean()</name>
+ <name name="keymember" arity="3"/>
+ <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc>
<fsummary>Test for membership of a list of tuples</fsummary>
- <type>
- <v>Key = term()</v>
- <v>N = 1..tuple_size(Tuple)</v>
- <v>TupleList = [Tuple]</v>
- <v>&nbsp;Tuple = tuple()</v>
- </type>
- <desc>
- <p>Returns <c>true</c> if there is a tuple in <c>TupleList</c>
- whose <c>N</c>th element compares equal to <c>Key</c>, otherwise
+ <desc>
+ <p>Returns <c>true</c> if there is a tuple in <c><anno>TupleList</anno></c>
+ whose <c><anno>N</anno></c>th element compares equal to <c><anno>Key</anno></c>, otherwise
<c>false</c>.</p>
</desc>
</func>
@@ -321,18 +311,13 @@ flatmap(Fun, List1) ->
</desc>
</func>
<func>
- <name>keysearch(Key, N, TupleList) -> {value, Tuple} | false</name>
+ <name name="keysearch" arity="3"/>
+ <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc>
<fsummary>Search for an element in a list of tuples</fsummary>
- <type>
- <v>Key = term()</v>
- <v>N = 1..tuple_size(Tuple)</v>
- <v>TupleList = [Tuple]</v>
- <v>Tuple = tuple()</v>
- </type>
- <desc>
- <p>Searches the list of tuples <c>TupleList</c> for a
- tuple whose <c>N</c>th element compares equal to <c>Key</c>.
- Returns <c>{value, Tuple}</c> if such a tuple is found,
+ <desc>
+ <p>Searches the list of tuples <c><anno>TupleList</anno></c> for a
+ tuple whose <c><anno>N</anno></c>th element compares equal to <c><anno>Key</anno></c>.
+ Returns <c>{value, <anno>Tuple</anno>}</c> if such a tuple is found,
otherwise <c>false</c>.</p>
<note><p>This function is retained for backward compatibility.
The function <c>lists:keyfind/3</c> (introduced in R13A)
@@ -425,15 +410,11 @@ flatmap(Fun, List1) ->
</desc>
</func>
<func>
- <name>member(Elem, List) -> boolean()</name>
+ <name name="member" arity="2"/>
<fsummary>Test for membership of a list</fsummary>
- <type>
- <v>Elem = term()</v>
- <v>List = [term()]</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Elem</c> matches some element of
- <c>List</c>, otherwise <c>false</c>.</p>
+ <p>Returns <c>true</c> if <c><anno>Elem</anno></c> matches some element of
+ <c><anno>List</anno></c>, otherwise <c>false</c>.</p>
</desc>
</func>
<func>
@@ -562,14 +543,11 @@ c</pre>
</desc>
</func>
<func>
- <name>reverse(List1, Tail) -> List2</name>
+ <name name="reverse" arity="2"/>
<fsummary>Reverse a list appending a tail</fsummary>
- <type>
- <v>List1 = Tail = List2 = [term()]</v>
- </type>
<desc>
- <p>Returns a list with the elements in <c>List1</c>
- in reverse order, with the tail <c>Tail</c> appended. For
+ <p>Returns a list with the elements in <c><anno>List1</anno></c>
+ in reverse order, with the tail <c><anno>Tail</anno></c> appended. For
example:</p>
<pre>
> <input>lists:reverse([1, 2, 3, 4], [a, b, c]).</input>
diff --git a/lib/stdlib/doc/src/math.xml b/lib/stdlib/doc/src/math.xml
index 518457d5d8..0219dcce10 100644
--- a/lib/stdlib/doc/src/math.xml
+++ b/lib/stdlib/doc/src/math.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -52,54 +52,47 @@
</desc>
</func>
<func>
- <name>sin(X)</name>
- <name>cos(X)</name>
- <name>tan(X)</name>
- <name>asin(X)</name>
- <name>acos(X)</name>
- <name>atan(X)</name>
- <name>atan2(Y, X)</name>
- <name>sinh(X)</name>
- <name>cosh(X)</name>
- <name>tanh(X)</name>
- <name>asinh(X)</name>
- <name>acosh(X)</name>
- <name>atanh(X)</name>
- <name>exp(X)</name>
- <name>log(X)</name>
- <name>log10(X)</name>
- <name>pow(X, Y)</name>
- <name>sqrt(X)</name>
+ <name name="sin" arity="1"/>
+ <name name="cos" arity="1"/>
+ <name name="tan" arity="1"/>
+ <name name="asin" arity="1"/>
+ <name name="acos" arity="1"/>
+ <name name="atan" arity="1"/>
+ <name name="atan2" arity="2"/>
+ <name name="sinh" arity="1"/>
+ <name name="cosh" arity="1"/>
+ <name name="tanh" arity="1"/>
+ <name name="asinh" arity="1"/>
+ <name name="acosh" arity="1"/>
+ <name name="atanh" arity="1"/>
+ <name name="exp" arity="1"/>
+ <name name="log" arity="1"/>
+ <name name="log10" arity="1"/>
+ <name name="pow" arity="2"/>
+ <name name="sqrt" arity="1"/>
+ <type variable="X" name_i="7"/>
+ <type variable="Y" name_i="7"/>
<fsummary>Diverse math functions</fsummary>
- <type>
- <v>X = Y = number()</v>
- </type>
<desc>
<p>A collection of math functions which return floats. Arguments
are numbers. </p>
</desc>
</func>
<func>
- <name>erf(X) -> float()</name>
+ <name name="erf" arity="1"/>
<fsummary>Error function.</fsummary>
- <type>
- <v>X = number()</v>
- </type>
<desc>
- <p>Returns the error function of <c>X</c>, where</p>
+ <p>Returns the error function of <c><anno>X</anno></c>, where</p>
<pre>
erf(X) = 2/sqrt(pi)*integral from 0 to X of exp(-t*t) dt. </pre>
</desc>
</func>
<func>
- <name>erfc(X) -> float()</name>
+ <name name="erfc" arity="1"/>
<fsummary>Another error function</fsummary>
- <type>
- <v>X = number()</v>
- </type>
<desc>
<p><c>erfc(X)</c> returns <c>1.0 - erf(X)</c>, computed by
- methods that avoid cancellation for large <c>X</c>. </p>
+ methods that avoid cancellation for large <c><anno>X</anno></c>. </p>
</desc>
</func>
</funcs>
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index 4834426d5c..b4d9f9a444 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,84 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 1.18.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed bug where if given an invalid drive letter on
+ windows ensure dir would go into an infinite loop.</p>
+ <p>
+ Own Id: OTP-10104</p>
+ </item>
+ <item>
+ <p>
+ Calls to gen_server:enter_loop/4 where ServerName has a
+ global scope and no timeout is given now works correctly.</p>
+ <p>
+ Thanks to Sam Bobroff for reporting the issue.</p>
+ <p>
+ Own Id: OTP-10130</p>
+ </item>
+ <item>
+ <p>
+ fix escript/primary archive reloading</p>
+ <p>
+ If the mtime of an escript/primary archive file changes
+ after being added to the code path, correctly reload the
+ archive and update the cache. (Thanks to Tuncer Ayaz)</p>
+ <p>
+ Own Id: OTP-10151</p>
+ </item>
+ <item>
+ <p>
+ Fix bug that in some cases could cause corrupted binaries
+ in ETS tables with <c>compressed</c> option.</p>
+ <p>
+ Own Id: OTP-10182</p>
+ </item>
+ <item>
+ <p>
+ Fix filename:nativename/1 on Win32</p>
+ <p>
+ Don't choke on paths given as binary argument on Win32.
+ Thanks to Jan Kl�tzke</p>
+ <p>
+ Own Id: OTP-10188</p>
+ </item>
+ <item>
+ <p>
+ Fix bug in <c>ets:test_ms/2</c> that could cause emulator
+ crash when using <c>'$_'</c> in match spec.</p>
+ <p>
+ Own Id: OTP-10190</p>
+ </item>
+ <item>
+ <p>
+ Fix bug where zip archives wrongly have a first disk
+ number set to 1</p>
+ <p>
+ Own Id: OTP-10223</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The message printed by the Erlang shell as an
+ explanation of the <c>badarith</c> error has been
+ corrected. (Thanks to Matthias Lang.) </p>
+ <p>
+ Own Id: OTP-10054</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 1.18.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/stdlib/doc/src/re.xml b/lib/stdlib/doc/src/re.xml
index 224b4b2771..2211bfb925 100644
--- a/lib/stdlib/doc/src/re.xml
+++ b/lib/stdlib/doc/src/re.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -78,28 +78,15 @@
</datatypes>
<funcs>
<func>
- <name>compile(Regexp) -> {ok, MP} | {error, ErrSpec}</name>
+ <name name="compile" arity="1"/>
<fsummary>Compile a regular expression into a match program</fsummary>
- <type>
- <v>Regexp = iodata()</v>
- </type>
<desc>
- <p>The same as <c>compile(Regexp,[])</c></p>
+ <p>The same as <c>compile(<anno>Regexp</anno>,[])</c></p>
</desc>
</func>
<func>
- <name>compile(Regexp,Options) -> {ok, MP} | {error, ErrSpec}</name>
+ <name name="compile" arity="2"/>
<fsummary>Compile a regular expression into a match program</fsummary>
- <type>
- <v>Regexp = iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v>
- <v>Options = [ Option ]</v>
- <v>Option = <seealso marker="#type-compile_option">compile_option()</seealso></v>
- <v>NLSpec = <seealso marker="#type-nl_spec">nl_spec()</seealso></v>
- <v>MP = <seealso marker="#type-mp">mp()</seealso></v>
- <v>ErrSpec = {ErrString, Position}</v>
- <v>ErrString = string()</v>
- <v>Position = non_neg_integer()</v>
- </type>
<desc>
<p>This function compiles a regular expression with the syntax
described below into an internal format to be used later as a
@@ -165,44 +152,23 @@ This option makes it possible to include comments inside complicated patterns. N
</func>
<func>
- <name>run(Subject,RE) -> {match, Captured} | nomatch</name>
+ <name name="run" arity="2"/>
<fsummary>Match a subject against regular expression and capture subpatterns</fsummary>
- <type>
- <v>Subject = iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v>
- <v>RE = <seealso marker="#type-mp">mp()</seealso> | iodata()</v>
- <v>Captured = [ CaptureData ]</v>
- <v>CaptureData = {integer(),integer()}</v>
- </type>
<desc>
- <p>The same as <c>run(Subject,RE,[])</c>.</p>
+ <p>The same as <c>run(<anno>Subject</anno>,<anno>RE</anno>,[])</c>.</p>
</desc>
</func>
<func>
- <name>run(Subject,RE,Options) -> {match, Captured} | match | nomatch</name>
+ <name name="run" arity="3"/>
+ <type_desc variable="CompileOpt">See <seealso marker="#compile_options">compile/2</seealso> above.</type_desc>
<fsummary>Match a subject against regular expression and capture subpatterns</fsummary>
- <type>
- <v>Subject = iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v>
- <v>RE = <seealso marker="#type-mp">mp()</seealso> | iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v>
- <v>Options = [ Option ]</v>
- <v>Option = anchored | global | notbol | noteol | notempty | {offset, integer() >= 0} | {newline, NLSpec} | bsr_anycrlf | bsr_unicode | {capture, ValueSpec} | {capture, ValueSpec, Type} | CompileOpt</v>
- <v>Type = index | list | binary</v>
- <v>ValueSpec = all | all_but_first | first | none | ValueList</v>
- <v>ValueList = [ ValueID ]</v>
- <v>ValueID = integer() | string() | atom()</v>
- <v>CompileOpt = <seealso marker="#type-compile_option">compile_option()</seealso></v>
- <d>See <seealso marker="#compile_options">compile/2</seealso> above.</d>
- <v>NLSpec = <seealso marker="#type-nl_spec">nl_spec()</seealso></v>
- <v>Captured = [ CaptureData ] | [ [ CaptureData ] ... ]</v>
- <v>CaptureData = {integer(),integer()} | ListConversionData | binary()</v>
- <v>ListConversionData = string() | {error, string(), binary()} | {incomplete, string(), binary()}</v>
- </type>
<desc>
<p>Executes a regexp matching, returning <c>match/{match,
- Captured}</c> or <c>nomatch</c>. The regular expression can be
+ <anno>Captured</anno>}</c> or <c>nomatch</c>. The regular expression can be
given either as <c>iodata()</c> in which case it is
automatically compiled (as by <c>re:compile/2</c>) and executed,
- or as a pre compiled <c>mp()</c> in which case it is executed
+ or as a pre-compiled <c>mp()</c> in which case it is executed
against the subject directly.</p>
<p>When compilation is involved, the exception <c>badarg</c> is
@@ -214,23 +180,23 @@ This option makes it possible to include comments inside complicated patterns. N
list can only contain the options <c>anchored</c>,
<c>global</c>, <c>notbol</c>, <c>noteol</c>,
<c>notempty</c>, <c>{offset, integer() >= 0}</c>, <c>{newline,
- NLSpec}</c> and <c>{capture, ValueSpec}/{capture, ValueSpec,
- Type}</c>. Otherwise all options valid for the
+ <anno>NLSpec</anno>}</c> and <c>{capture, <anno>ValueSpec</anno>}/{capture, <anno>ValueSpec</anno>,
+ <anno>Type</anno>}</c>. Otherwise all options valid for the
<c>re:compile/2</c> function are allowed as well. Options
allowed both for compilation and execution of a match, namely
- <c>anchored</c> and <c>{newline, NLSpec}</c>, will affect both
+ <c>anchored</c> and <c>{newline, <anno>NLSpec</anno>}</c>, will affect both
the compilation and execution if present together with a non
pre-compiled regular expression.</p>
<p>If the regular expression was previously compiled with the
- option <c>unicode</c>, the <c>Subject</c> should be provided as
+ option <c>unicode</c>, the <c><anno>Subject</anno></c> should be provided as
a valid Unicode <c>charlist()</c>, otherwise any <c>iodata()</c>
will do. If compilation is involved and the option
- <c>unicode</c> is given, both the <c>Subject</c> and the regular
+ <c>unicode</c> is given, both the <c><anno>Subject</anno></c> and the regular
expression should be given as valid Unicode
<c>charlists()</c>.</p>
- <p>The <c>{capture, ValueSpec}/{capture, ValueSpec, Type}</c>
+ <p>The <c>{capture, <anno>ValueSpec</anno>}/{capture, <anno>ValueSpec</anno>, <anno>Type</anno>}</c>
defines what to return from the function upon successful
matching. The <c>capture</c> tuple may contain both a
value specification telling which of the captured
@@ -244,9 +210,9 @@ This option makes it possible to include comments inside complicated patterns. N
at all is to be done (<c>{capture, none}</c>), the function will
return the single atom <c>match</c> upon successful matching,
otherwise the tuple
- <c>{match, ValueList}</c> is returned. Disabling capturing can
+ <c>{match, <anno>ValueList</anno>}</c> is returned. Disabling capturing can
be done either by specifying <c>none</c> or an empty list as
- <c>ValueSpec</c>.</p>
+ <c><anno>ValueSpec</anno></c>.</p>
<p>The options relevant for execution are:</p>
@@ -266,7 +232,7 @@ This option makes it possible to include comments inside complicated patterns. N
Perl). Each match is returned as a separate
<c>list()</c> containing the specific match as well as any
matching subexpressions (or as specified by the <c>capture
- option</c>). The <c>Captured</c> part of the return value will
+ option</c>). The <c><anno>Captured</anno></c> part of the return value will
hence be a <c>list()</c> of <c>list()</c>s when this
option is given.</p>
@@ -362,7 +328,7 @@ This option makes it possible to include comments inside complicated patterns. N
subject string. The offset is zero-based, so that the default is
<c>{offset,0}</c> (all of the subject string).</item>
- <tag><c>{newline, NLSpec}</c></tag>
+ <tag><c>{newline, <anno>NLSpec</anno>}</c></tag>
<item>
<p>Override the default definition of a newline in the subject string, which is LF (ASCII 10) in Erlang.</p>
<taglist>
@@ -383,7 +349,7 @@ This option makes it possible to include comments inside complicated patterns. N
<tag><c>bsr_unicode</c></tag>
<item>Specifies specifically that \R is to match all the Unicode newline characters (including crlf etc, the default).(overrides compilation option)</item>
- <tag><c>{capture, ValueSpec}</c>/<c>{capture, ValueSpec, Type}</c></tag>
+ <tag><c>{capture, <anno>ValueSpec</anno>}</c>/<c>{capture, <anno>ValueSpec</anno>, <anno>Type</anno>}</c></tag>
<item>
<p>Specifies which captured substrings are returned and in what
@@ -392,7 +358,7 @@ This option makes it possible to include comments inside complicated patterns. N
substring as well as all capturing subpatterns (all of the
pattern is automatically captured). The default return type is
(zero-based) indexes of the captured parts of the string, given as
- <c>{Offset,Length}</c> pairs (the <c>index</c> <c>Type</c> of
+ <c>{Offset,Length}</c> pairs (the <c>index</c> <c><anno>Type</anno></c> of
capturing).</p>
<p>As an example of the default behavior, the following call:</p>
@@ -422,8 +388,8 @@ This option makes it possible to include comments inside complicated patterns. N
<p>The capture tuple is built up as follows:</p>
<taglist>
- <tag><c>ValueSpec</c></tag>
- <item><p>Specifies which captured (sub)patterns are to be returned. The ValueSpec can either be an atom describing a predefined set of return values, or a list containing either the indexes or the names of specific subpatterns to return.</p>
+ <tag><c><anno>ValueSpec</anno></c></tag>
+ <item><p>Specifies which captured (sub)patterns are to be returned. The <c><anno>ValueSpec</anno></c> can either be an atom describing a predefined set of return values, or a list containing either the indexes or the names of specific subpatterns to return.</p>
<p>The predefined sets of subpatterns are:</p>
<taglist>
<tag><c>all</c></tag>
@@ -437,7 +403,7 @@ This option makes it possible to include comments inside complicated patterns. N
</taglist>
<p>The value list is a list of indexes for the subpatterns to return, where index 0 is for all of the pattern, and 1 is for the first explicit capturing subpattern in the regular expression, and so forth. When using named captured subpatterns (see below) in the regular expression, one can use <c>atom()</c>s or <c>string()</c>s to specify the subpatterns to be returned. For example, consider the regular expression:</p>
<code> ".*(abcd).*"</code>
- <p>matched against the string ""ABCabcdABC", capturing only the "abcd" part (the first explicit subpattern):</p>
+ <p>matched against the string "ABCabcdABC", capturing only the "abcd" part (the first explicit subpattern):</p>
<code> re:run("ABCabcdABC",".*(abcd).*",[{capture,[1]}]).</code>
<p>The call will yield the following result:</p>
<code> {match,[{3,4}]}</code>
@@ -460,8 +426,8 @@ This option makes it possible to include comments inside complicated patterns. N
or list respectively.</p>
</item>
- <tag><c>Type</c></tag>
- <item><p>Optionally specifies how captured substrings are to be returned. If omitted, the default of <c>index</c> is used. The <c>Type</c> can be one of the following:</p>
+ <tag><c><anno>Type</anno></c></tag>
+ <item><p>Optionally specifies how captured substrings are to be returned. If omitted, the default of <c>index</c> is used. The <c><anno>Type</anno></c> can be one of the following:</p>
<taglist>
<tag><c>index</c></tag>
<item>Return captured substrings as pairs of byte indexes into the subject string and length of the matching string in the subject (as if the subject string was flattened with <c>iolist_to_binary/1</c> or <c>unicode:characters_to_binary/2</c> prior to matching). Note that the <c>unicode</c> option results in <em>byte-oriented</em> indexes in a (possibly virtual) <em>UTF-8 encoded</em> binary. A byte index tuple <c>{0,2}</c> might therefore represent one or two characters when <c>unicode</c> is in effect. This might seem counter-intuitive, but has been deemed the most effective and useful way to way to do it. To return lists instead might result in simpler code if that is desired. This return type is the default.</item>
@@ -478,7 +444,7 @@ This option makes it possible to include comments inside complicated patterns. N
<code> "ABCabcdABC"</code>
<p>the subpattern at index 2 won't match, as "abdd" is not present in the string, but the complete pattern matches (due to the alternative <c>a(..d)</c>. The subpattern at index 2 is therefore unassigned and the default return value will be:</p>
<code> {match,[{0,10},{3,4},{-1,0},{4,3}]}</code>
- <p>Setting the capture <c>Type</c> to <c>binary</c> would give the following:</p>
+ <p>Setting the capture <c><anno>Type</anno></c> to <c>binary</c> would give the following:</p>
<code> {match,[&lt;&lt;"ABCabcdABC"&gt;&gt;,&lt;&lt;"abcd"&gt;&gt;,&lt;&lt;&gt;&gt;,&lt;&lt;"bcd"&gt;&gt;]}</code>
<p>where the empty binary (<c>&lt;&lt;&gt;&gt;</c>) represents the unassigned subpattern. In the <c>binary</c> case, some information about the matching is therefore lost, the <c>&lt;&lt;&gt;&gt;</c> might just as well be an empty string captured.</p>
<p>If differentiation between empty matches and non existing subpatterns is necessary, use the <c>type</c> <c>index</c>
diff --git a/lib/stdlib/doc/src/string.xml b/lib/stdlib/doc/src/string.xml
index 48867ffe72..549c871aed 100644
--- a/lib/stdlib/doc/src/string.xml
+++ b/lib/stdlib/doc/src/string.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -255,18 +255,12 @@ sub_string("Hello World", 4, 8).
</desc>
</func>
<func>
- <name>to_float(String) -> {Float,Rest} | {error,Reason} </name>
+ <name name="to_float" arity="1"/>
<fsummary>Returns a float whose text representation is the integers (ASCII values) in String.</fsummary>
- <type>
- <v>String = string()</v>
- <v>Float = float()</v>
- <v>Rest = string()</v>
- <v>Reason = no_float | not_a_list</v>
- </type>
<desc>
- <p>Argument <c>String</c> is expected to start with a valid text
+ <p>Argument <c><anno>String</anno></c> is expected to start with a valid text
represented float (the digits being ASCII values). Remaining characters
- in the string after the float are returned in <c>Rest</c>.</p>
+ in the string after the float are returned in <c><anno>Rest</anno></c>.</p>
<p>Example:</p>
<code type="none">
> {F1,Fs} = string:to_float("1.0-1.0e-1"),
@@ -280,18 +274,12 @@ sub_string("Hello World", 4, 8).
</desc>
</func>
<func>
- <name>to_integer(String) -> {Int,Rest} | {error,Reason} </name>
+ <name name="to_integer" arity="1"/>
<fsummary>Returns an integer whose text representation is the integers (ASCII values) in String.</fsummary>
- <type>
- <v>String = string()</v>
- <v>Int = integer()</v>
- <v>Rest = string()</v>
- <v>Reason = no_integer | not_a_list</v>
- </type>
<desc>
- <p>Argument <c>String</c> is expected to start with a valid text
+ <p>Argument <c><anno>String</anno></c> is expected to start with a valid text
represented integer (the digits being ASCII values). Remaining characters
- in the string after the integer are returned in <c>Rest</c>.</p>
+ in the string after the integer are returned in <c><anno>Rest</anno></c>.</p>
<p>Example:</p>
<code type="none">
> {I1,Is} = string:to_integer("33+22"),
diff --git a/lib/stdlib/doc/src/unicode.xml b/lib/stdlib/doc/src/unicode.xml
index 1001ebbae4..1f6cbaccd7 100644
--- a/lib/stdlib/doc/src/unicode.xml
+++ b/lib/stdlib/doc/src/unicode.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -130,34 +130,24 @@
</desc>
</func>
<func>
- <name>characters_to_list(Data, InEncoding) -> Result</name>
+ <name name="characters_to_list" arity="2"/>
<fsummary>Convert a collection of characters to list of Unicode characters</fsummary>
- <type>
- <v>Data = <seealso marker="#type-latin1_chardata">latin1_chardata()</seealso>
- | <seealso marker="#type-chardata">chardata()</seealso>
- | <seealso marker="#type-external_chardata">external_chardata()</seealso></v>
- <v>Result = list() | {error, list(), RestData} | {incomplete, list(), binary()}</v>
- <v>RestData = <seealso marker="#type-latin1_chardata">latin1_chardata()</seealso>
- | <seealso marker="#type-chardata">chardata()</seealso>
- | <seealso marker="#type-external_chardata">external_chardata()</seealso></v>
- <v>InEncoding = <seealso marker="#type-encoding">encoding()</seealso></v>
- </type>
<desc>
<p>This function converts a possibly deep list of integers and
binaries into a list of integers representing unicode
characters. The binaries in the input may have characters
encoded as latin1 (0 - 255, one character per byte), in which
- case the <c>InEncoding</c> parameter should be given as
+ case the <c><anno>InEncoding</anno></c> parameter should be given as
<c>latin1</c>, or have characters encoded as one of the
- UTF-encodings, which is given as the <c>InEncoding</c>
- parameter. Only when the <c>InEncoding</c> is one of the UTF
+ UTF-encodings, which is given as the <c><anno>InEncoding</anno></c>
+ parameter. Only when the <c><anno>InEncoding</anno></c> is one of the UTF
encodings, integers in the list are allowed to be grater than
255.</p>
- <p>If <c>InEncoding</c> is <c>latin1</c>, the <c>Data</c> parameter
+ <p>If <c><anno>InEncoding</anno></c> is <c>latin1</c>, the <c><anno>Data</anno></c> parameter
corresponds to the <c>iodata()</c> type, but for <c>unicode</c>,
- the <c>Data</c> parameter can contain integers greater than 255
+ the <c><anno>Data</anno></c> parameter can contain integers greater than 255
(unicode characters beyond the iso-latin-1 range), which would
make it invalid as <c>iodata()</c>.</p>
@@ -188,16 +178,16 @@
depth as the original data. The error occurs when traversing the
list and whatever's left to decode is simply returned as is.</p>
- <p>However, if the input <c>Data</c> is a pure binary, the third
+ <p>However, if the input <c><anno>Data</anno></c> is a pure binary, the third
part of the error tuple is guaranteed to be a binary as
well.</p>
<p>Errors occur for the following reasons:</p>
<list type="bulleted">
- <item>Integers out of range - If <c>InEncoding</c> is
+ <item>Integers out of range - If <c><anno>InEncoding</anno></c> is
<c>latin1</c>, an error occurs whenever an integer greater
- than 255 is found in the lists. If <c>InEncoding</c> is
+ than 255 is found in the lists. If <c><anno>InEncoding</anno></c> is
of a Unicode type, an error occurs whenever an integer
<list type="bulleted">
<item>greater than <c>16#10FFFF</c>
@@ -208,7 +198,7 @@
is found.
</item>
- <item>UTF encoding incorrect - If <c>InEncoding</c> is
+ <item>UTF encoding incorrect - If <c><anno>InEncoding</anno></c> is
one of the UTF types, the bytes in any binaries have to be valid
in that encoding. Errors can occur for various
reasons, including &quot;pure&quot; decoding errors
@@ -220,7 +210,7 @@
number should have been encoded in fewer bytes. The
case of a truncated UTF is handled specially, see the
paragraph about incomplete binaries below. If
- <c>InEncoding</c> is <c>latin1</c>, binaries are always valid
+ <c><anno>InEncoding</anno></c> is <c>latin1</c>, binaries are always valid
as long as they contain whole bytes,
as each byte falls into the valid iso-latin-1 range.</item>
@@ -238,7 +228,7 @@
the first part of a (so far) valid UTF character.</p>
<p>If one UTF characters is split over two consecutive
- binaries in the <c>Data</c>, the conversion succeeds. This means
+ binaries in the <c><anno>Data</anno></c>, the conversion succeeds. This means
that a character can be decoded from a range of binaries as long
as the whole range is given as input without errors
occurring. Example:</p>
@@ -274,21 +264,11 @@
</desc>
</func>
<func>
- <name>characters_to_binary(Data,InEncoding) -> Result</name>
+ <name name="characters_to_binary" arity="2"/>
<fsummary>Convert a collection of characters to an UTF-8 binary</fsummary>
- <type>
- <v>Data = <seealso marker="#type-latin1_chardata">latin1_chardata()</seealso>
- | <seealso marker="#type-chardata">chardata()</seealso>
- | <seealso marker="#type-external_chardata">external_chardata()</seealso></v>
- <v>Result = binary() | {error, binary(), RestData} | {incomplete, binary(), binary()}</v>
- <v>RestData = <seealso marker="#type-latin1_chardata">latin1_chardata()</seealso>
- | <seealso marker="#type-chardata">chardata()</seealso>
- | <seealso marker="#type-external_chardata">external_chardata()</seealso></v>
- <v>InEncoding = <seealso marker="#type-encoding">encoding()</seealso></v>
- </type>
<desc>
- <p>Same as characters_to_binary(Data, InEncoding, unicode).</p>
+ <p>Same as characters_to_binary(<anno>Data</anno>, <anno>InEncoding</anno>, unicode).</p>
</desc>
</func>
<func>
diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml
index a7e010a05f..bbcd49a934 100644
--- a/lib/stdlib/doc/src/unicode_usage.xml
+++ b/lib/stdlib/doc/src/unicode_usage.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2011</year>
+ <year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -53,7 +53,7 @@
<item>Basically the same as UTF-32, but without some Unicode semantics, defined by IEEE and has little use as a separate encoding standard. For all normal (and possibly abnormal) usages, UTF-32 and UCS-4 are interchangeable.</item>
</taglist>
<p>Certain ranges of characters are left unused and certain ranges are even deemed invalid. The most notable invalid range is 16#D800 - 16#DFFF, as the UTF-16 encoding does not allow for encoding of these numbers. It can be speculated that the UTF-16 encoding standard was, from the beginning, expected to be able to hold all Unicode characters in one 16-bit entity, but then had to be extended, leaving a hole in the Unicode range to cope with backward compatibility.</p>
-<p>Additionally, the codepoint 16#FEFF is used for byte order marks (BOM's) and use of that character is not encouraged in other contexts than that. It actually is valid though, as the character "ZWNBS" (Zero Width Non Breaking Space). BOM's are used to identify encodings and byte order for programs where such parameters are not known in advance. Byte order marks are more seldom used than one could expect, put their use is becoming more widely spread as they provide the means for programs to make educated guesses about the Unicode format of a certain file.</p>
+<p>Additionally, the codepoint 16#FEFF is used for byte order marks (BOM's) and use of that character is not encouraged in other contexts than that. It actually is valid though, as the character "ZWNBS" (Zero Width Non Breaking Space). BOM's are used to identify encodings and byte order for programs where such parameters are not known in advance. Byte order marks are more seldom used than one could expect, but their use is becoming more widely spread as they provide the means for programs to make educated guesses about the Unicode format of a certain file.</p>
</section>
<section>
<title>Standard Unicode representation in Erlang</title>
@@ -210,6 +210,12 @@ Eshell V5.7 (abort with ^G)
</section>
</section>
<section>
+<title>Unicode in environment variables and parameters</title>
+<p>Environment variables and their interpretation is handled much in the same way as file names. If Unicode file names are enabled, environment variables as well as parameters to the Erlang VM are expected to be in Unicode.</p>
+<p>If Unicode file names are enabled, the calls to <seealso marker="kernel:os#getenv/0"><c>os:getenv/0</c></seealso>, <seealso marker="kernel:os#getenv/1"><c>os:getenv/1</c></seealso> and <seealso marker="kernel:os#putenv/2"><c>os:putenv/2</c></seealso> will handle Unicode strings. On Unix-like platforms, the built-in functions will translate environment variables in UTF-8 to/from Unicode strings, possibly with codepoints > 255. On Windows the Unicode versions of the environment system API will be used, also allowing for codepoints > 255.</p>
+<p>On Unix-like operating systems, parameters are expected to be UTF-8 without translation if Unicode file names are enabled.</p>
+</section>
+<section>
<title>Unicode-aware modules</title>
<p>Most of the modules in Erlang/OTP are of course Unicode-unaware in the sense that they have no notion of Unicode and really shouldn't have. Typically they handle non-textual or byte-oriented data (like <c>gen_tcp</c> etc).</p>
<p>Modules that actually handle textual data (like <c>io_lib</c>, <c>string</c> etc) are sometimes subject to conversion or extension to be able to handle Unicode characters.</p>
diff --git a/lib/stdlib/examples/erl_id_trans.erl b/lib/stdlib/examples/erl_id_trans.erl
index 72e41d6473..34c6ecc394 100644
--- a/lib/stdlib/examples/erl_id_trans.erl
+++ b/lib/stdlib/examples/erl_id_trans.erl
@@ -283,15 +283,6 @@ gexpr({call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,F}},As0}) ->
true -> As1 = gexpr_list(As0),
{call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,F}},As1}
end;
-% Unfortunately, writing calls as {M,F}(...) is also allowed.
-gexpr({call,Line,{tuple,La,[{atom,Lb,erlang},{atom,Lc,F}]},As0}) ->
- case erl_internal:guard_bif(F, length(As0)) or
- erl_internal:arith_op(F, length(As0)) or
- erl_internal:comp_op(F, length(As0)) or
- erl_internal:bool_op(F, length(As0)) of
- true -> As1 = gexpr_list(As0),
- {call,Line,{tuple,La,[{atom,Lb,erlang},{atom,Lc,F}]},As1}
- end;
gexpr({bin,Line,Fs}) ->
Fs2 = pattern_grp(Fs),
{bin,Line,Fs2};
diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile
index 8bdaae57fd..14304824d3 100644
--- a/lib/stdlib/src/Makefile
+++ b/lib/stdlib/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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
@@ -167,6 +167,7 @@ docs:
# This is a trick so that the preloaded files will get the correct type
# specifications.
primary_bootstrap_compiler: \
+ $(BOOTSTRAP_COMPILER)/ebin/epp.beam \
$(BOOTSTRAP_COMPILER)/ebin/erl_scan.beam \
$(BOOTSTRAP_COMPILER)/ebin/erl_parse.beam \
$(BOOTSTRAP_COMPILER)/ebin/erl_lint.beam \
diff --git a/lib/stdlib/src/binary.erl b/lib/stdlib/src/binary.erl
index cb1e12ae46..1ed3422bc5 100644
--- a/lib/stdlib/src/binary.erl
+++ b/lib/stdlib/src/binary.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -18,29 +18,185 @@
%%
-module(binary).
%%
-%% The following functions implemented as BIF's
-%% binary:compile_pattern/1
-%% binary:match/{2,3}
-%% binary:matches/{2,3}
-%% binary:longest_common_prefix/1
-%% binary:longest_common_suffix/1
-%% binary:first/1
-%% binary:last/1
-%% binary:at/2
-%% binary:part/{2,3}
-%% binary:bin_to_list/{1,2,3}
-%% binary:list_to_bin/1
-%% binary:copy/{1,2}
-%% binary:referenced_byte_size/1
-%% binary:decode_unsigned/{1,2}
-%% - Not yet:
-%%
%% Implemented in this module:
-export([split/2,split/3,replace/3,replace/4]).
--opaque cp() :: tuple().
+-opaque cp() :: {'am' | 'bm', binary()}.
-type part() :: {Start :: non_neg_integer(), Length :: integer()}.
+%%% BIFs.
+
+-export([at/2, bin_to_list/1, bin_to_list/2, bin_to_list/3,
+ compile_pattern/1, copy/1, copy/2, decode_unsigned/1,
+ decode_unsigned/2, encode_unsigned/1, encode_unsigned/2,
+ first/1, last/1, list_to_bin/1, longest_common_prefix/1,
+ longest_common_suffix/1, match/2, match/3, matches/2,
+ matches/3, part/2, part/3, referenced_byte_size/1]).
+
+-spec at(Subject, Pos) -> byte() when
+ Subject :: binary(),
+ Pos :: non_neg_integer().
+
+at(_, _) ->
+ erlang:nif_error(undef).
+
+-spec bin_to_list(Subject) -> [byte()] when
+ Subject :: binary().
+
+bin_to_list(_) ->
+ erlang:nif_error(undef).
+
+-spec bin_to_list(Subject, PosLen) -> [byte()] when
+ Subject :: binary(),
+ PosLen :: part().
+
+bin_to_list(_, _) ->
+ erlang:nif_error(undef).
+
+-spec bin_to_list(Subject, Pos, Len) -> [byte()] when
+ Subject :: binary(),
+ Pos :: non_neg_integer(),
+ Len :: non_neg_integer().
+
+bin_to_list(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec compile_pattern(Pattern) -> cp() when
+ Pattern :: binary() | [binary()].
+
+compile_pattern(_) ->
+ erlang:nif_error(undef).
+
+-spec copy(Subject) -> binary() when
+ Subject :: binary().
+
+copy(_) ->
+ erlang:nif_error(undef).
+
+-spec copy(Subject, N) -> binary() when
+ Subject :: binary(),
+ N :: non_neg_integer().
+
+copy(_, _) ->
+ erlang:nif_error(undef).
+
+-spec decode_unsigned(Subject) -> Unsigned when
+ Subject :: binary(),
+ Unsigned :: non_neg_integer().
+
+decode_unsigned(_) ->
+ erlang:nif_error(undef).
+
+-spec decode_unsigned(Subject, Endianess) -> Unsigned when
+ Subject :: binary(),
+ Endianess :: big | little,
+ Unsigned :: non_neg_integer().
+
+decode_unsigned(_, _) ->
+ erlang:nif_error(undef).
+
+-spec encode_unsigned(Unsigned) -> binary() when
+ Unsigned :: non_neg_integer().
+
+encode_unsigned(_) ->
+ erlang:nif_error(undef).
+
+-spec encode_unsigned(Unsigned, Endianess) -> binary() when
+ Unsigned :: non_neg_integer(),
+ Endianess :: big | little.
+
+encode_unsigned(_, _) ->
+ erlang:nif_error(undef).
+
+-spec first(Subject) -> byte() when
+ Subject :: binary().
+
+first(_) ->
+ erlang:nif_error(undef).
+
+-spec last(Subject) -> byte() when
+ Subject :: binary().
+
+last(_) ->
+ erlang:nif_error(undef).
+
+-spec list_to_bin(ByteList) -> binary() when
+ ByteList :: iodata().
+
+list_to_bin(_) ->
+ erlang:nif_error(undef).
+
+-spec longest_common_prefix(Binaries) -> non_neg_integer() when
+ Binaries :: [binary()].
+
+longest_common_prefix(_) ->
+ erlang:nif_error(undef).
+
+-spec longest_common_suffix(Binaries) -> non_neg_integer() when
+ Binaries :: [binary()].
+
+longest_common_suffix(_) ->
+ erlang:nif_error(undef).
+
+-spec match(Subject, Pattern) -> Found | nomatch when
+ Subject :: binary(),
+ Pattern :: binary() | [binary()] | cp(),
+ Found :: part().
+
+match(_, _) ->
+ erlang:nif_error(undef).
+
+-spec match(Subject, Pattern, Options) -> Found | nomatch when
+ Subject :: binary(),
+ Pattern :: binary() | [binary()] | cp(),
+ Found :: part(),
+ Options :: [Option],
+ Option :: {scope, part()}.
+
+match(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec matches(Subject, Pattern) -> Found when
+ Subject :: binary(),
+ Pattern :: binary() | [binary()] | cp(),
+ Found :: [part()].
+
+matches(_, _) ->
+ erlang:nif_error(undef).
+
+-spec matches(Subject, Pattern, Options) -> Found when
+ Subject :: binary(),
+ Pattern :: binary() | [binary()] | cp(),
+ Found :: [part()],
+ Options :: [Option],
+ Option :: {scope, part()}.
+
+matches(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec part(Subject, PosLen) -> binary() when
+ Subject :: binary(),
+ PosLen :: part().
+
+part(_, _) ->
+ erlang:nif_error(undef).
+
+-spec part(Subject, Pos, Len) -> binary() when
+ Subject :: binary(),
+ Pos :: non_neg_integer(),
+ Len :: non_neg_integer().
+
+part(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec referenced_byte_size(Binary) -> non_neg_integer() when
+ Binary :: binary().
+
+referenced_byte_size(_) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% split
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl
index c0f9ce34b0..6a937f8fa2 100644
--- a/lib/stdlib/src/dets.erl
+++ b/lib/stdlib/src/dets.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -319,7 +319,7 @@ foldr(Fun, Acc, Tab) ->
foldl(Fun, Acc, Tab) ->
Ref = make_ref(),
- do_traverse(Fun, Acc, Tab, Ref).
+ badarg(do_traverse(Fun, Acc, Tab, Ref), [Fun, Acc, Tab]).
-spec from_ets(Name, EtsTab) -> 'ok' | {'error', Reason} when
Name :: tab_name(),
@@ -515,7 +515,7 @@ match(Tab, Pat) ->
Reason :: term().
match(Tab, Pat, N) ->
- badarg(init_chunk_match(Tab, Pat, bindings, N), [Tab, Pat, N]).
+ badarg(init_chunk_match(Tab, Pat, bindings, N, no_safe), [Tab, Pat, N]).
-spec match(Continuation) ->
{[Match], Continuation2} | '$end_of_table' | {'error', Reason} when
@@ -525,7 +525,7 @@ match(Tab, Pat, N) ->
Reason :: term().
match(State) when State#dets_cont.what =:= bindings ->
- badarg(chunk_match(State), [State]);
+ badarg(chunk_match(State, no_safe), [State]);
match(Term) ->
erlang:error(badarg, [Term]).
@@ -538,26 +538,26 @@ match_delete(Tab, Pat) ->
badarg(match_delete(Tab, Pat, delete), [Tab, Pat]).
match_delete(Tab, Pat, What) ->
- safe_fixtable(Tab, true),
case compile_match_spec(What, Pat) of
{Spec, MP} ->
- Proc = dets_server:get_pid(Tab),
- R = req(Proc, {match_delete_init, MP, Spec}),
- do_match_delete(Tab, Proc, R, What, 0);
+ case catch dets_server:get_pid(Tab) of
+ {'EXIT', _Reason} ->
+ badarg;
+ Proc ->
+ R = req(Proc, {match_delete_init, MP, Spec}),
+ do_match_delete(Proc, R, What, 0)
+ end;
badarg ->
badarg
end.
-do_match_delete(Tab, _Proc, {done, N1}, select, N) ->
- safe_fixtable(Tab, false),
+do_match_delete(_Proc, {done, N1}, select, N) ->
N + N1;
-do_match_delete(Tab, _Proc, {done, _N1}, _What, _N) ->
- safe_fixtable(Tab, false),
+do_match_delete(_Proc, {done, _N1}, _What, _N) ->
ok;
-do_match_delete(Tab, Proc, {cont, State, N1}, What, N) ->
- do_match_delete(Tab, Proc, req(Proc, {match_delete, State}), What, N+N1);
-do_match_delete(Tab, _Proc, Error, _What, _N) ->
- safe_fixtable(Tab, false),
+do_match_delete(Proc, {cont, State, N1}, What, N) ->
+ do_match_delete(Proc, req(Proc, {match_delete, State}), What, N+N1);
+do_match_delete(_Proc, Error, _What, _N) ->
Error.
-spec match_object(Name, Pattern) -> Objects | {'error', Reason} when
@@ -579,7 +579,7 @@ match_object(Tab, Pat) ->
Reason :: term().
match_object(Tab, Pat, N) ->
- badarg(init_chunk_match(Tab, Pat, object, N), [Tab, Pat, N]).
+ badarg(init_chunk_match(Tab, Pat, object, N, no_safe), [Tab, Pat, N]).
-spec match_object(Continuation) ->
{Objects, Continuation2} | '$end_of_table' | {'error', Reason} when
@@ -589,7 +589,7 @@ match_object(Tab, Pat, N) ->
Reason :: term().
match_object(State) when State#dets_cont.what =:= object ->
- badarg(chunk_match(State), [State]);
+ badarg(chunk_match(State, no_safe), [State]);
match_object(Term) ->
erlang:error(badarg, [Term]).
@@ -712,7 +712,7 @@ select(Tab, Pat) ->
Reason :: term().
select(Tab, Pat, N) ->
- badarg(init_chunk_match(Tab, Pat, select, N), [Tab, Pat, N]).
+ badarg(init_chunk_match(Tab, Pat, select, N, no_safe), [Tab, Pat, N]).
-spec select(Continuation) ->
{Selection, Continuation2} | '$end_of_table' | {'error', Reason} when
@@ -722,7 +722,7 @@ select(Tab, Pat, N) ->
Reason :: term().
select(State) when State#dets_cont.what =:= select ->
- badarg(chunk_match(State), [State]);
+ badarg(chunk_match(State, no_safe), [State]);
select(Term) ->
erlang:error(badarg, [Term]).
@@ -898,7 +898,7 @@ traverse(Tab, Fun) ->
throw({Ref, Other})
end
end,
- do_traverse(TFun, [], Tab, Ref).
+ badarg(do_traverse(TFun, [], Tab, Ref), [Tab, Fun]).
-spec update_counter(Name, Key, Increment) -> Result when
Name :: tab_name(),
@@ -929,20 +929,21 @@ where(Tab, Object) ->
badarg(treq(Tab, {where, Object}), [Tab, Object]).
do_traverse(Fun, Acc, Tab, Ref) ->
- safe_fixtable(Tab, true),
- Proc = dets_server:get_pid(Tab),
- try
- do_trav(Proc, Acc, Fun)
- catch {Ref, Result} ->
- Result
- after
- safe_fixtable(Tab, false)
+ case catch dets_server:get_pid(Tab) of
+ {'EXIT', _Reason} ->
+ badarg;
+ Proc ->
+ try
+ do_trav(Proc, Acc, Fun)
+ catch {Ref, Result} ->
+ Result
+ end
end.
do_trav(Proc, Acc, Fun) ->
{Spec, MP} = compile_match_spec(object, '_'),
%% MP not used
- case req(Proc, {match, MP, Spec, default}) of
+ case req(Proc, {match, MP, Spec, default, safe}) of
{cont, State} ->
do_trav(State, Proc, Acc, Fun);
Error ->
@@ -952,7 +953,7 @@ do_trav(Proc, Acc, Fun) ->
do_trav(#dets_cont{bin = eof}, _Proc, Acc, _Fun) ->
Acc;
do_trav(State, Proc, Acc, Fun) ->
- case req(Proc, {match_init, State}) of
+ case req(Proc, {match_init, State, safe}) of
{cont, {Bins, NewState}} ->
do_trav_bins(NewState, Proc, Acc, Fun, lists:reverse(Bins));
Error ->
@@ -972,44 +973,47 @@ do_trav_bins(State, Proc, Acc, Fun, [Bin | Bins]) ->
end.
safe_match(Tab, Pat, What) ->
- safe_fixtable(Tab, true),
- R = do_safe_match(init_chunk_match(Tab, Pat, What, default), []),
- safe_fixtable(Tab, false),
- R.
+ do_safe_match(init_chunk_match(Tab, Pat, What, default, safe), []).
do_safe_match({error, Error}, _L) ->
{error, Error};
do_safe_match({L, C}, LL) ->
- do_safe_match(chunk_match(C), L++LL);
+ do_safe_match(chunk_match(C, safe), L++LL);
do_safe_match('$end_of_table', L) ->
L;
do_safe_match(badarg, _L) ->
badarg.
%% What = object | bindings | select
-init_chunk_match(Tab, Pat, What, N) when is_integer(N), N >= 0;
- N =:= default ->
+init_chunk_match(Tab, Pat, What, N, Safe) when is_integer(N), N >= 0;
+ N =:= default ->
case compile_match_spec(What, Pat) of
{Spec, MP} ->
- Proc = dets_server:get_pid(Tab),
- case req(Proc, {match, MP, Spec, N}) of
- {done, L} ->
- {L, #dets_cont{tab = Tab, proc = Proc, what = What,
- bin = eof}};
- {cont, State} ->
- chunk_match(State#dets_cont{what = What, tab = Tab,
- proc = Proc});
- Error ->
- Error
+ case catch dets_server:get_pid(Tab) of
+ {'EXIT', _Reason} ->
+ badarg;
+ Proc ->
+ case req(Proc, {match, MP, Spec, N, Safe}) of
+ {done, L} ->
+ {L, #dets_cont{tab = Tab, proc = Proc,
+ what = What, bin = eof}};
+ {cont, State} ->
+ chunk_match(State#dets_cont{what = What,
+ tab = Tab,
+ proc = Proc},
+ Safe);
+ Error ->
+ Error
+ end
end;
badarg ->
badarg
end;
-init_chunk_match(_Tab, _Pat, _What, _) ->
+init_chunk_match(_Tab, _Pat, _What, _N, _Safe) ->
badarg.
-chunk_match(#dets_cont{proc = Proc}=State) ->
- case req(Proc, {match_init, State}) of
+chunk_match(#dets_cont{proc = Proc}=State, Safe) ->
+ case req(Proc, {match_init, State, Safe}) of
'$end_of_table'=Reply ->
Reply;
{cont, {Bins, NewState}} ->
@@ -1024,7 +1028,7 @@ chunk_match(#dets_cont{proc = Proc}=State) ->
badarg
end;
[] ->
- chunk_match(NewState);
+ chunk_match(NewState, Safe);
Terms ->
{Terms, NewState}
end;
@@ -1301,7 +1305,7 @@ open_file_loop(Head, N) ->
%% - wait 1 ms after each update.
%% next is normally followed by lookup, but since lookup is also
%% used when not traversing the table, it is not prioritized.
- ?DETS_CALL(From, {match_init, _State} = Op) ->
+ ?DETS_CALL(From, {match_init, _State, _Safe} = Op) ->
do_apply_op(Op, From, Head, N);
?DETS_CALL(From, {bchunk, _State} = Op) ->
do_apply_op(Op, From, Head, N);
@@ -1558,12 +1562,17 @@ apply_op(Op, From, Head, N) ->
H2;
{lookup_keys, _Keys} ->
stream_op(Op, From, [], Head, N);
- {match_init, State} ->
- {H2, Res} = fmatch_init(Head, State),
+ {match_init, State, Safe} ->
+ {H1, Res} = fmatch_init(Head, State),
+ H2 = case Res of
+ {cont,_} -> H1;
+ _ when Safe =:= no_safe-> H1;
+ _ when Safe =:= safe -> do_safe_fixtable(H1, From, false)
+ end,
From ! {self(), Res},
H2;
- {match, MP, Spec, NObjs} ->
- {H2, Res} = fmatch(Head, MP, Spec, NObjs),
+ {match, MP, Spec, NObjs, Safe} ->
+ {H2, Res} = fmatch(Head, MP, Spec, NObjs, Safe, From),
From ! {self(), Res},
H2;
{member, Key} when Head#head.version =:= 8 ->
@@ -1577,11 +1586,15 @@ apply_op(Op, From, Head, N) ->
From ! {self(), Res},
H2;
{match_delete, State} when Head#head.update_mode =:= dirty ->
- {H2, Res} = fmatch_delete(Head, State),
+ {H1, Res} = fmatch_delete(Head, State),
+ H2 = case Res of
+ {cont,_S,_N} -> H1;
+ _ -> do_safe_fixtable(H1, From, false)
+ end,
From ! {self(), Res},
{N + 1, H2};
{match_delete_init, MP, Spec} when Head#head.update_mode =:= dirty ->
- {H2, Res} = fmatch_delete_init(Head, MP, Spec),
+ {H2, Res} = fmatch_delete_init(Head, MP, Spec, From),
From ! {self(), Res},
{N + 1, H2};
{safe_fixtable, Bool} ->
@@ -2229,13 +2242,18 @@ fmatch_init(Head, C) ->
end.
%% -> {NewHead, Result}
-fmatch(Head, MP, Spec, N) ->
+fmatch(Head, MP, Spec, N, Safe, From) ->
KeyPos = Head#head.keypos,
case find_all_keys(Spec, KeyPos, []) of
[] ->
%% Complete match
case catch write_cache(Head) of
- {NewHead, []} ->
+ {Head1, []} ->
+ NewHead =
+ case Safe of
+ safe -> do_safe_fixtable(Head1, From, true);
+ no_safe -> Head1
+ end,
C0 = init_scan(NewHead, N),
{NewHead, {cont, C0#dets_cont{match_program = MP}}};
{NewHead, _} = HeadError when is_record(NewHead, head) ->
@@ -2300,12 +2318,12 @@ contains_variable(_) ->
false.
%% -> {NewHead, Res}
-fmatch_delete_init(Head, MP, Spec) ->
+fmatch_delete_init(Head, MP, Spec, From) ->
KeyPos = Head#head.keypos,
case catch
case find_all_keys(Spec, KeyPos, []) of
[] ->
- do_fmatch_delete_var_keys(Head, MP, Spec);
+ do_fmatch_delete_var_keys(Head, MP, Spec, From);
List ->
Keys = lists:usort(List),
do_fmatch_constant_keys(Head, Keys, MP)
@@ -2336,7 +2354,7 @@ fmatch_delete(Head, C) ->
end
end.
-do_fmatch_delete_var_keys(Head, _MP, ?PATTERN_TO_TRUE_MATCH_SPEC('_'))
+do_fmatch_delete_var_keys(Head, _MP, ?PATTERN_TO_TRUE_MATCH_SPEC('_'), _From)
when Head#head.fixed =:= false ->
%% Handle the case where the file is emptied efficiently.
%% Empty the cache just to get the number of objects right.
@@ -2348,8 +2366,9 @@ do_fmatch_delete_var_keys(Head, _MP, ?PATTERN_TO_TRUE_MATCH_SPEC('_'))
Reply ->
Reply
end;
-do_fmatch_delete_var_keys(Head, MP, _Spec) ->
- {NewHead, []} = write_cache(Head),
+do_fmatch_delete_var_keys(Head, MP, _Spec, From) ->
+ Head1 = do_safe_fixtable(Head, From, true),
+ {NewHead, []} = write_cache(Head1),
C0 = init_scan(NewHead, default),
{NewHead, {cont, C0#dets_cont{match_program = MP}, 0}}.
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index ccc14610d7..2c8d84a9e1 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index abab81d31f..1e5f962375 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -248,6 +248,8 @@ format_error({illegal_guard_local_call, {F,A}}) ->
io_lib:format("call to local/imported function ~w/~w is illegal in guard",
[F,A]);
format_error(illegal_guard_expr) -> "illegal guard expression";
+format_error(deprecated_tuple_fun) ->
+ "tuple funs are deprecated and will be removed in R16";
%% --- exports ---
format_error({explicit_export,F,A}) ->
io_lib:format("in this release, the call to ~w/~w must be written "
@@ -363,6 +365,12 @@ format_error(callback_wrong_arity) ->
format_error({imported_predefined_type, Name}) ->
io_lib:format("referring to built-in type ~w as a remote type; "
"please take out the module name", [Name]);
+format_error({not_exported_opaque, {TypeName, Arity}}) ->
+ io_lib:format("opaque type ~w~s is not exported",
+ [TypeName, gen_type_paren(Arity)]);
+format_error({underspecified_opaque, {TypeName, Arity}}) ->
+ io_lib:format("opaque type ~w~s is underspecified and therefore meaningless",
+ [TypeName, gen_type_paren(Arity)]);
%% --- obsolete? unused? ---
format_error({format_error, {Fmt, Args}}) ->
io_lib:format(Fmt, Args);
@@ -849,7 +857,8 @@ post_traversal_check(Forms, St0) ->
StC = check_untyped_records(Forms, StB),
StD = check_on_load(StC),
StE = check_unused_records(Forms, StD),
- check_callback_information(StE).
+ StF = check_local_opaque_types(StE),
+ check_callback_information(StF).
%% check_behaviour(State0) -> State
%% Check that the behaviour attribute is valid.
@@ -1914,8 +1923,6 @@ gexpr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,_Lf,F}},As}, Vt, St0) ->
true -> {Asvt,St1};
false -> {Asvt,add_error(Line, illegal_guard_expr, St1)}
end;
-gexpr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,F}]},As}, Vt, St) ->
- gexpr({call,L,{remote,Lt,{atom,Lm,erlang},{atom,Lf,F}},As}, Vt, St);
gexpr({op,Line,Op,A}, Vt, St0) ->
{Avt,St1} = gexpr(A, Vt, St0),
case is_gexpr_op(Op, 1) of
@@ -2554,15 +2561,24 @@ find_field(_F, []) -> error.
%% Attr :: 'type' | 'opaque'
%% Checks that a type definition is valid.
+-record(typeinfo, {attr, line}).
+
type_def(_Attr, _Line, {record, _RecName}, Fields, [], St0) ->
%% The record field names and such are checked in the record format.
%% We only need to check the types.
Types = [T || {typed_record_field, _, T} <- Fields],
check_type({type, -1, product, Types}, St0);
-type_def(_Attr, Line, TypeName, ProtoType, Args, St0) ->
+type_def(Attr, Line, TypeName, ProtoType, Args, St0) ->
TypeDefs = St0#lint.types,
Arity = length(Args),
TypePair = {TypeName, Arity},
+ Info = #typeinfo{attr = Attr, line = Line},
+ StoreType =
+ fun(St) ->
+ NewDefs = dict:store(TypePair, Info, TypeDefs),
+ CheckType = {type, -1, product, [ProtoType|Args]},
+ check_type(CheckType, St#lint{types=NewDefs})
+ end,
case (dict:is_key(TypePair, TypeDefs) orelse is_var_arity_type(TypeName)) of
true ->
case dict:is_key(TypePair, default_types()) of
@@ -2572,20 +2588,29 @@ type_def(_Attr, Line, TypeName, ProtoType, Args, St0) ->
true ->
Warn = {new_builtin_type, TypePair},
St1 = add_warning(Line, Warn, St0),
- NewDefs = dict:store(TypePair, Line, TypeDefs),
- CheckType = {type, -1, product, [ProtoType|Args]},
- check_type(CheckType, St1#lint{types=NewDefs});
+ StoreType(St1);
false ->
add_error(Line, {builtin_type, TypePair}, St0)
end;
false -> add_error(Line, {redefine_type, TypePair}, St0)
end;
false ->
- NewDefs = dict:store(TypePair, Line, TypeDefs),
- CheckType = {type, -1, product, [ProtoType|Args]},
- check_type(CheckType, St0#lint{types=NewDefs})
+ St1 = case
+ Attr =:= opaque andalso
+ is_underspecified(ProtoType, Arity)
+ of
+ true ->
+ Warn = {underspecified_opaque, TypePair},
+ add_warning(Line, Warn, St0);
+ false -> St0
+ end,
+ StoreType(St1)
end.
+is_underspecified({type,_,term,[]}, 0) -> true;
+is_underspecified({type,_,any,[]}, 0) -> true;
+is_underspecified(_ProtType, _Arity) -> false.
+
check_type(Types, St) ->
{SeenVars, St1} = check_type(Types, dict:new(), St),
dict:fold(fun(Var, {seen_once, Line}, AccSt) ->
@@ -2895,7 +2920,7 @@ check_unused_types(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) ->
fun(_Type, -1, AccSt) ->
%% Default type
AccSt;
- (Type, FileLine, AccSt) ->
+ (Type, #typeinfo{line = FileLine}, AccSt) ->
case loc(FileLine) of
{FirstFile, _} ->
case gb_sets:is_member(Type, UsedTypes) of
@@ -2914,6 +2939,24 @@ check_unused_types(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) ->
St
end.
+check_local_opaque_types(St) ->
+ #lint{types=Ts, exp_types=ExpTs} = St,
+ FoldFun =
+ fun(_Type, -1, AccSt) ->
+ %% Default type
+ AccSt;
+ (_Type, #typeinfo{attr = type}, AccSt) ->
+ AccSt;
+ (Type, #typeinfo{attr = opaque, line = FileLine}, AccSt) ->
+ case gb_sets:is_element(Type, ExpTs) of
+ true -> AccSt;
+ false ->
+ Warn = {not_exported_opaque,Type},
+ add_warning(FileLine, Warn, AccSt)
+ end
+ end,
+ dict:fold(FoldFun, St, Ts).
+
%% icrt_clauses(Clauses, In, ImportVarTable, State) ->
%% {NewVts,State}.
diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl
index 10b2ed2e49..8e59e01f48 100644
--- a/lib/stdlib/src/erl_scan.erl
+++ b/lib/stdlib/src/erl_scan.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index 27e70ac4d4..498d850df3 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.erl
@@ -22,7 +22,7 @@
-export([script_name/0, create/2, extract/2]).
%% Internal API.
--export([start/0, start/1]).
+-export([start/0, start/1, parse_file/1]).
%%-----------------------------------------------------------------------
@@ -346,7 +346,8 @@ parse_and_run(File, Args, Options) ->
case Source of
archive ->
{ok, FileInfo} = file:read_file_info(File),
- case code:set_primary_archive(File, FormsOrBin, FileInfo) of
+ case code:set_primary_archive(File, FormsOrBin, FileInfo,
+ fun escript:parse_file/1) of
ok when CheckOnly ->
case code:load_file(Module) of
{module, _} ->
@@ -396,6 +397,19 @@ parse_and_run(File, Args, Options) ->
%% Parse script
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Only used as callback by erl_prim_loader
+parse_file(File) ->
+ try parse_file(File, false) of
+ {_Source, _Module, FormsOrBin, _HasRecs, _Mode}
+ when is_binary(FormsOrBin) ->
+ {ok, FormsOrBin};
+ _ ->
+ {error, no_archive_bin}
+ catch
+ throw:Reason ->
+ {error, Reason}
+ end.
+
parse_file(File, CheckOnly) ->
{HeaderSz, NextLineNo, Fd, Sections} =
parse_header(File, false),
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index afa914a456..ee6cff1b75 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -42,11 +42,16 @@
-export([i/0, i/1, i/2, i/3]).
--export_type([tab/0, tid/0, match_spec/0]).
+-export_type([tab/0, tid/0, match_spec/0, comp_match_spec/0]).
%%-----------------------------------------------------------------------------
+-type access() :: public | protected | private.
-type tab() :: atom() | tid().
+-type type() :: set | ordered_set | bag | duplicate_bag.
+-type continuation() :: '$end_of_table'
+ | {tab(),integer(),integer(),binary(),list(),integer()}
+ | {tab(),_,_,integer(),binary(),list(),integer(),integer()}.
%% a similar definition is also in erl_types
-opaque tid() :: integer().
@@ -57,59 +62,398 @@
%%-----------------------------------------------------------------------------
-%% The following functions used to be found in this module, but
-%% are now BIFs (i.e. implemented in C).
-%%
-%% all/0
-%% new/2
-%% delete/1
-%% delete/2
-%% first/1
-%% info/1
-%% info/2
-%% safe_fixtable/2
-%% lookup/2
-%% lookup_element/3
-%% insert/2
-%% is_compiled_ms/1
-%% last/1
-%% member/2
-%% next/2
-%% prev/2
-%% rename/2
-%% slot/2
-%% match/1
-%% match/2
-%% match/3
-%% match_object/1
-%% match_object/2
-%% match_object/3
-%% match_spec_compile/1
-%% match_spec_run_r/3
-%% select/1
-%% select/2
-%% select/3
-%% select_count/2
-%% select_reverse/1
-%% select_reverse/2
-%% select_reverse/3
-%% select_delete/2
-%% setopts/2
-%% update_counter/3
-%% update_element/3
-%%
+%%% BIFs
+
+-export([all/0, delete/1, delete/2, delete_all_objects/1,
+ delete_object/2, first/1, give_away/3, info/1, info/2,
+ insert/2, insert_new/2, is_compiled_ms/1, last/1, lookup/2,
+ lookup_element/3, match/1, match/2, match/3, match_object/1,
+ match_object/2, match_object/3, match_spec_compile/1,
+ match_spec_run_r/3, member/2, new/2, next/2, prev/2,
+ rename/2, safe_fixtable/2, select/1, select/2, select/3,
+ select_count/2, select_delete/2, select_reverse/1,
+ select_reverse/2, select_reverse/3, setopts/2, slot/2,
+ update_counter/3, update_element/3]).
+
+-spec all() -> [Tab] when
+ Tab :: tab().
+
+all() ->
+ erlang:nif_error(undef).
+
+-spec delete(Tab) -> true when
+ Tab :: tab().
+
+delete(_) ->
+ erlang:nif_error(undef).
+
+-spec delete(Tab, Key) -> true when
+ Tab :: tab(),
+ Key :: term().
+
+delete(_, _) ->
+ erlang:nif_error(undef).
+
+-spec delete_all_objects(Tab) -> true when
+ Tab :: tab().
+
+delete_all_objects(_) ->
+ erlang:nif_error(undef).
+
+-spec delete_object(Tab, Object) -> true when
+ Tab :: tab(),
+ Object :: tuple().
+
+delete_object(_, _) ->
+ erlang:nif_error(undef).
+
+-spec first(Tab) -> Key | '$end_of_table' when
+ Tab :: tab(),
+ Key :: term().
+
+first(_) ->
+ erlang:nif_error(undef).
+
+-spec give_away(Tab, Pid, GiftData) -> true when
+ Tab :: tab(),
+ Pid :: pid(),
+ GiftData :: term().
+
+give_away(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec info(Tab) -> InfoList | undefined when
+ Tab :: tab(),
+ InfoList :: [InfoTuple],
+ InfoTuple :: {compressed, boolean()}
+ | {heir, pid() | none}
+ | {keypos, pos_integer()}
+ | {memory, non_neg_integer()}
+ | {name, atom()}
+ | {named_table, boolean()}
+ | {node, node()}
+ | {owner, pid()}
+ | {protection, access()}
+ | {size, non_neg_integer()}
+ | {type, type()}.
+
+info(_) ->
+ erlang:nif_error(undef).
+
+-spec info(Tab, Item) -> Value | undefined when
+ Tab :: tab(),
+ Item :: compressed | fixed | heir | keypos | memory
+ | name | named_table | node | owner | protection
+ | safe_fixed | size | stats | type,
+ Value :: term().
+
+info(_, _) ->
+ erlang:nif_error(undef).
+
+-spec insert(Tab, ObjectOrObjects) -> true when
+ Tab :: tab(),
+ ObjectOrObjects :: tuple() | [tuple()].
+
+insert(_, _) ->
+ erlang:nif_error(undef).
+
+-spec insert_new(Tab, ObjectOrObjects) -> boolean() when
+ Tab :: tab(),
+ ObjectOrObjects :: tuple() | [tuple()].
+
+insert_new(_, _) ->
+ erlang:nif_error(undef).
+
+-spec is_compiled_ms(Term) -> boolean() when
+ Term :: term().
+
+is_compiled_ms(_) ->
+ erlang:nif_error(undef).
+
+-spec last(Tab) -> Key | '$end_of_table' when
+ Tab :: tab(),
+ Key :: term().
+
+last(_) ->
+ erlang:nif_error(undef).
+
+-spec lookup(Tab, Key) -> [Object] when
+ Tab :: tab(),
+ Key :: term(),
+ Object :: tuple().
+
+lookup(_, _) ->
+ erlang:nif_error(undef).
+
+-spec lookup_element(Tab, Key, Pos) -> Elem when
+ Tab :: tab(),
+ Key :: term(),
+ Pos :: pos_integer(),
+ Elem :: term() | [term()].
+
+lookup_element(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec match(Tab, Pattern) -> [Match] when
+ Tab :: tab(),
+ Pattern :: match_pattern(),
+ Match :: [term()].
+
+match(_, _) ->
+ erlang:nif_error(undef).
+
+-spec match(Tab, Pattern, Limit) -> {[Match], Continuation} |
+ '$end_of_table' when
+ Tab :: tab(),
+ Pattern :: match_pattern(),
+ Limit :: pos_integer(),
+ Match :: [term()],
+ Continuation :: continuation().
+
+match(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec match(Continuation) -> {[Match], Continuation} |
+ '$end_of_table' when
+ Match :: [term()],
+ Continuation :: continuation().
+
+match(_) ->
+ erlang:nif_error(undef).
+
+-spec match_object(Tab, Pattern) -> [Object] when
+ Tab :: tab(),
+ Pattern :: match_pattern(),
+ Object :: tuple().
+
+match_object(_, _) ->
+ erlang:nif_error(undef).
+
+-spec match_object(Tab, Pattern, Limit) -> {[Match], Continuation} |
+ '$end_of_table' when
+ Tab :: tab(),
+ Pattern :: match_pattern(),
+ Limit :: pos_integer(),
+ Match :: [term()],
+ Continuation :: continuation().
+
+match_object(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec match_object(Continuation) -> {[Match], Continuation} |
+ '$end_of_table' when
+ Match :: [term()],
+ Continuation :: continuation().
+
+match_object(_) ->
+ erlang:nif_error(undef).
+
+-spec match_spec_compile(MatchSpec) -> CompiledMatchSpec when
+ MatchSpec :: match_spec(),
+ CompiledMatchSpec :: comp_match_spec().
--opaque comp_match_spec() :: any(). %% this one is REALLY opaque
+match_spec_compile(_) ->
+ erlang:nif_error(undef).
--spec match_spec_run([tuple()], comp_match_spec()) -> [term()].
+-spec match_spec_run_r(List, CompiledMatchSpec, list()) -> list() when
+ List :: [tuple()],
+ CompiledMatchSpec :: comp_match_spec().
+
+match_spec_run_r(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec member(Tab, Key) -> boolean() when
+ Tab :: tab(),
+ Key :: term().
+
+member(_, _) ->
+ erlang:nif_error(undef).
+
+-spec new(Name, Options) -> tid() | atom() when
+ Name :: atom(),
+ Options :: [Option],
+ Option :: Type | Access | named_table | {keypos,Pos}
+ | {heir, Pid :: pid(), HeirData} | {heir, none} | Tweaks,
+ Type :: type(),
+ Access :: access(),
+ Tweaks :: {write_concurrency, boolean()}
+ | {read_concurrency, boolean()}
+ | compressed,
+ Pos :: pos_integer(),
+ HeirData :: term().
+
+new(_, _) ->
+ erlang:nif_error(undef).
+
+-spec next(Tab, Key1) -> Key2 | '$end_of_table' when
+ Tab :: tab(),
+ Key1 :: term(),
+ Key2 :: term().
+
+next(_, _) ->
+ erlang:nif_error(undef).
+
+-spec prev(Tab, Key1) -> Key2 | '$end_of_table' when
+ Tab :: tab(),
+ Key1 :: term(),
+ Key2 :: term().
+
+prev(_, _) ->
+ erlang:nif_error(undef).
+
+%% Shadowed by erl_bif_types: ets:rename/2
+-spec rename(Tab, Name) -> Name when
+ Tab :: tab(),
+ Name :: atom().
+
+rename(_, _) ->
+ erlang:nif_error(undef).
+
+-spec safe_fixtable(Tab, Fix) -> true when
+ Tab :: tab(),
+ Fix :: boolean().
+
+safe_fixtable(_, _) ->
+ erlang:nif_error(undef).
+
+-spec select(Tab, MatchSpec) -> [Match] when
+ Tab :: tab(),
+ MatchSpec :: match_spec(),
+ Match :: term().
+
+select(_, _) ->
+ erlang:nif_error(undef).
+
+-spec select(Tab, MatchSpec, Limit) -> {[Match],Continuation} |
+ '$end_of_table' when
+ Tab :: tab(),
+ MatchSpec :: match_spec(),
+ Limit :: pos_integer(),
+ Match :: term(),
+ Continuation :: continuation().
+
+select(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec select(Continuation) -> {[Match],Continuation} | '$end_of_table' when
+ Match :: term(),
+ Continuation :: continuation().
+
+select(_) ->
+ erlang:nif_error(undef).
+
+-spec select_count(Tab, MatchSpec) -> NumMatched when
+ Tab :: tab(),
+ MatchSpec :: match_spec(),
+ NumMatched :: non_neg_integer().
+
+select_count(_, _) ->
+ erlang:nif_error(undef).
+
+-spec select_delete(Tab, MatchSpec) -> NumDeleted when
+ Tab :: tab(),
+ MatchSpec :: match_spec(),
+ NumDeleted :: non_neg_integer().
+
+select_delete(_, _) ->
+ erlang:nif_error(undef).
+
+-spec select_reverse(Tab, MatchSpec) -> [Match] when
+ Tab :: tab(),
+ MatchSpec :: match_spec(),
+ Match :: term().
+
+select_reverse(_, _) ->
+ erlang:nif_error(undef).
+
+-spec select_reverse(Tab, MatchSpec, Limit) -> {[Match],Continuation} |
+ '$end_of_table' when
+ Tab :: tab(),
+ MatchSpec :: match_spec(),
+ Limit :: pos_integer(),
+ Match :: term(),
+ Continuation :: continuation().
+
+select_reverse(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec select_reverse(Continuation) -> {[Match],Continuation} |
+ '$end_of_table' when
+ Continuation :: continuation(),
+ Match :: term().
+
+select_reverse(_) ->
+ erlang:nif_error(undef).
+
+-spec setopts(Tab, Opts) -> true when
+ Tab :: tab(),
+ Opts :: Opt | [Opt],
+ Opt :: {heir, pid(), HeirData} | {heir,none},
+ HeirData :: term().
+
+setopts(_, _) ->
+ erlang:nif_error(undef).
+
+-spec slot(Tab, I) -> [Object] | '$end_of_table' when
+ Tab :: tab(),
+ I :: non_neg_integer(),
+ Object :: tuple().
+
+slot(_, _) ->
+ erlang:nif_error(undef).
+
+-spec update_counter(Tab, Key, UpdateOp) -> Result when
+ Tab :: tab(),
+ Key :: term(),
+ UpdateOp :: {Pos, Incr} | {Pos, Incr, Threshold, SetValue},
+ Pos :: integer(),
+ Incr :: integer(),
+ Threshold :: integer(),
+ SetValue :: integer(),
+ Result :: integer();
+ (Tab, Key, [UpdateOp]) -> [Result] when
+ Tab :: tab(),
+ Key :: term(),
+ UpdateOp :: {Pos, Incr} | {Pos, Incr, Threshold, SetValue},
+ Pos :: integer(),
+ Incr :: integer(),
+ Threshold :: integer(),
+ SetValue :: integer(),
+ Result :: integer();
+ (Tab, Key, Incr) -> Result when
+ Tab :: tab(),
+ Key :: term(),
+ Incr :: integer(),
+ Result :: integer().
+
+update_counter(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec update_element(Tab, Key, ElementSpec :: {Pos, Value}) -> boolean() when
+ Tab :: tab(),
+ Key :: term(),
+ Pos :: pos_integer(),
+ Value :: term();
+ (Tab, Key, ElementSpec :: [{Pos, Value}]) -> boolean() when
+ Tab :: tab(),
+ Key :: term(),
+ Pos :: pos_integer(),
+ Value :: term().
+
+update_element(_, _, _) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
+
+-opaque comp_match_spec() :: binary(). %% this one is REALLY opaque
+
+-spec match_spec_run(List, CompiledMatchSpec) -> list() when
+ List :: [tuple()],
+ CompiledMatchSpec :: comp_match_spec().
match_spec_run(List, CompiledMS) ->
lists:reverse(ets:match_spec_run_r(List, CompiledMS, [])).
--type continuation() :: '$end_of_table'
- | {tab(),integer(),integer(),binary(),list(),integer()}
- | {tab(),_,_,integer(),binary(),list(),integer(),integer()}.
-
-spec repair_continuation(Continuation, MatchSpec) -> Continuation when
Continuation :: continuation(),
MatchSpec :: match_spec().
diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl
index b098d4cb91..318f3b87b8 100644
--- a/lib/stdlib/src/filelib.erl
+++ b/lib/stdlib/src/filelib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -132,6 +132,8 @@ do_wildcard_comp({compiled_wildcard,{exists,File}}, Mod) ->
{ok,_} -> [File];
_ -> []
end;
+do_wildcard_comp({compiled_wildcard,[cwd,Base|Rest]}, Mod) ->
+ do_wildcard_1([Base], Rest, Mod);
do_wildcard_comp({compiled_wildcard,[Base|Rest]}, Mod) ->
do_wildcard_1([Base], Rest, Mod).
@@ -143,7 +145,11 @@ do_wildcard_comp({compiled_wildcard,{exists,File}}, Cwd, Mod) ->
{ok,_} -> [File];
_ -> []
end;
-do_wildcard_comp({compiled_wildcard,[current|Rest]}, Cwd0, Mod) ->
+do_wildcard_comp({compiled_wildcard,[cwd|Rest0]}, Cwd0, Mod) ->
+ case Rest0 of
+ [current|Rest] -> ok;
+ Rest -> ok
+ end,
{Cwd,PrefixLen} = case filename:join([Cwd0]) of
Bin when is_binary(Bin) -> {Bin,byte_size(Bin)+1};
Other -> {Other,length(Other)+1}
@@ -295,6 +301,8 @@ do_wildcard_2([File|Rest], Pattern, Result, Mod) ->
do_wildcard_2([], _, Result, _Mod) ->
Result.
+do_wildcard_3(Base, [[double_star]|Rest], Result, Mod) ->
+ lists:sort(do_double_star(current, [Base], Rest, Result, Mod, true));
do_wildcard_3(Base, [Pattern|Rest], Result, Mod) ->
case do_list_dir(Base, Mod) of
{ok, Files0} ->
@@ -328,6 +336,8 @@ wildcard_5([question|Rest1], [_|Rest2]) ->
wildcard_5(Rest1, Rest2);
wildcard_5([accept], _) ->
true;
+wildcard_5([double_star], _) ->
+ true;
wildcard_5([star|Rest], File) ->
do_star(Rest, File);
wildcard_5([{one_of, Ordset}|Rest], [C|File]) ->
@@ -348,6 +358,21 @@ wildcard_5([], [_|_]) ->
wildcard_5([_|_], []) ->
false.
+do_double_star(Base, [H|T], Rest, Result, Mod, Root) ->
+ Full = join(Base, H),
+ Result1 = case do_list_dir(Full, Mod) of
+ {ok, Files} ->
+ do_double_star(Full, Files, Rest, Result, Mod, false);
+ _ -> Result
+ end,
+ Result2 = case Root andalso Rest == [] of
+ true -> Result1;
+ false -> do_wildcard_3(Full, Rest, Result1, Mod)
+ end,
+ do_double_star(Base, T, Rest, Result2, Mod, Root);
+do_double_star(_Base, [], _Rest, Result, _Mod, _Root) ->
+ Result.
+
do_star(Pattern, [X|Rest]) ->
case wildcard_5(Pattern, [X|Rest]) of
true -> true;
@@ -383,7 +408,10 @@ compile_wildcard_1(Pattern) ->
[Root|Rest] = filename:split(Pattern),
case filename:pathtype(Root) of
relative ->
- compile_wildcard_2([Root|Rest], current);
+ case compile_wildcard_2([Root|Rest], current) of
+ {exists,_}=Wc -> Wc;
+ [_|_]=Wc -> [cwd|Wc]
+ end;
_ ->
compile_wildcard_2(Rest, [Root])
end.
@@ -416,6 +444,10 @@ compile_part([$}|Rest], true, Result) ->
{ok, $}, lists:reverse(Result), Rest};
compile_part([$?|Rest], Upto, Result) ->
compile_part(Rest, Upto, [question|Result]);
+compile_part([$*,$*], Upto, Result) ->
+ compile_part([], Upto, [double_star|Result]);
+compile_part([$*,$*|Rest], Upto, Result) ->
+ compile_part(Rest, Upto, [star|Result]);
compile_part([$*], Upto, Result) ->
compile_part([], Upto, [accept|Result]);
compile_part([$*|Rest], Upto, Result) ->
diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl
index dbfcbea4f7..a6b42cc68c 100644
--- a/lib/stdlib/src/filename.erl
+++ b/lib/stdlib/src/filename.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -726,6 +726,8 @@ nativename(Name0) ->
_ -> Name
end.
+win32_nativename(Name) when is_binary(Name) ->
+ binary:replace(Name, <<"/">>, <<"\\">>, [global]);
win32_nativename([$/|Rest]) ->
[$\\|win32_nativename(Rest)];
win32_nativename([C|Rest]) ->
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index 59c6d240ba..04308a51b7 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -270,7 +270,7 @@ enter_loop(Mod, Options, State) ->
enter_loop(Mod, Options, State, self(), infinity).
enter_loop(Mod, Options, State, ServerName = {Scope, _})
- when Scope == local; Scope == local ->
+ when Scope == local; Scope == global ->
enter_loop(Mod, Options, State, ServerName, infinity);
enter_loop(Mod, Options, State, ServerName = {via, _, _}) ->
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index 0252cdf742..ab62b72519 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -82,7 +82,10 @@
-type chars() :: [char() | chars()].
-type depth() :: -1 | non_neg_integer().
--opaque continuation() :: {_, _, _, _}. % XXX: refine
+-opaque continuation() :: {Format :: string(),
+ Stack :: chars(),
+ Nchars :: non_neg_integer(),
+ Results :: [term()]}.
%%----------------------------------------------------------------------
diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl
index e73c087753..961c060019 100644
--- a/lib/stdlib/src/lists.erl
+++ b/lib/stdlib/src/lists.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -33,9 +33,6 @@
keysort/2, keymerge/3, rkeymerge/3, rukeymerge/3,
ukeysort/2, ukeymerge/3, keymap/3]).
-%% Bifs: member/2, reverse/2
-%% Bifs: keymember/3, keysearch/3, keyfind/3
-
-export([merge/3, rmerge/3, sort/2, umerge/3, rumerge/3, usort/2]).
-export([all/2,any/2,map/2,flatmap/2,foldl/3,foldr/3,filter/2,
@@ -43,6 +40,60 @@
mapfoldl/3,mapfoldr/3,foreach/2,takewhile/2,dropwhile/2,splitwith/2,
split/2]).
+%%% BIFs
+-export([keyfind/3, keymember/3, keysearch/3, member/2, reverse/2]).
+
+%% Shadowed by erl_bif_types: lists:keyfind/3
+-spec keyfind(Key, N, TupleList) -> Tuple | false when
+ Key :: term(),
+ N :: pos_integer(),
+ TupleList :: [Tuple],
+ Tuple :: tuple().
+
+keyfind(_, _, _) ->
+ erlang:nif_error(undef).
+
+%% Shadowed by erl_bif_types: lists:keymember/3
+-spec keymember(Key, N, TupleList) -> boolean() when
+ Key :: term(),
+ N :: pos_integer(),
+ TupleList :: [Tuple],
+ Tuple :: tuple().
+
+keymember(_, _, _) ->
+ erlang:nif_error(undef).
+
+%% Shadowed by erl_bif_types: lists:keysearch/3
+-spec keysearch(Key, N, TupleList) -> {value, Tuple} | false when
+ Key :: term(),
+ N :: pos_integer(),
+ TupleList :: [Tuple],
+ Tuple :: tuple().
+
+keysearch(_, _, _) ->
+ erlang:nif_error(undef).
+
+%% Shadowed by erl_bif_types: lists:member/2
+-spec member(Elem, List) -> boolean() when
+ Elem :: T,
+ List :: [T],
+ T :: term().
+
+member(_, _) ->
+ erlang:nif_error(undef).
+
+%% Shadowed by erl_bif_types: lists:reverse/2
+-spec reverse(List1, Tail) -> List2 when
+ List1 :: [T],
+ Tail :: term(),
+ List2 :: [T],
+ T :: term().
+
+reverse(_, _) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
+
%% member(X, L) -> (true | false)
%% test if X is a member of the list L
%% Now a BIF!
@@ -84,7 +135,7 @@ append([]) -> [].
subtract(L1, L2) -> L1 -- L2.
-%% reverse(L) reverse all elements in the list L. Is now a BIF!
+%% reverse(L) reverse all elements in the list L. reverse/2 is now a BIF!
-spec reverse(List1) -> List2 when
List1 :: [T],
@@ -581,6 +632,7 @@ flatlength([_|T], L) ->
flatlength([], L) -> L.
%% keymember(Key, Index, [Tuple]) Now a BIF!
+%% keyfind(Key, Index, [Tuple]) A BIF!
%% keysearch(Key, Index, [Tuple]) Now a BIF!
%% keydelete(Key, Index, [Tuple])
%% keyreplace(Key, Index, [Tuple], NewTuple)
@@ -1126,8 +1178,7 @@ rumerge(T1, [H2 | T2]) ->
%% takewhile(Predicate, List)
%% dropwhile(Predicate, List)
%% splitwith(Predicate, List)
-%% for list programming. Function here is a 'fun'. For backward compatibility,
-%% {Module,Function} is still accepted.
+%% for list programming. Function here is a 'fun'.
%%
%% The name zf is a joke!
%%
diff --git a/lib/stdlib/src/math.erl b/lib/stdlib/src/math.erl
index b2ea6195c5..c3fb684ec3 100644
--- a/lib/stdlib/src/math.erl
+++ b/lib/stdlib/src/math.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -20,6 +20,116 @@
-export([pi/0]).
+%%% BIFs
+
+-export([sin/1, cos/1, tan/1, asin/1, acos/1, atan/1, atan2/2, sinh/1,
+ cosh/1, tanh/1, asinh/1, acosh/1, atanh/1, exp/1, log/1,
+ log10/1, pow/2, sqrt/1, erf/1, erfc/1]).
+
+-spec acos(X) -> float() when
+ X :: number().
+acos(_) ->
+ erlang:nif_error(undef).
+
+-spec acosh(X) -> float() when
+ X :: number().
+acosh(_) ->
+ erlang:nif_error(undef).
+
+-spec asin(X) -> float() when
+ X :: number().
+asin(_) ->
+ erlang:nif_error(undef).
+
+-spec asinh(X) -> float() when
+ X :: number().
+asinh(_) ->
+ erlang:nif_error(undef).
+
+-spec atan(X) -> float() when
+ X :: number().
+atan(_) ->
+ erlang:nif_error(undef).
+
+-spec atan2(X, Y) -> float() when
+ X :: number(),
+ Y :: number().
+atan2(_, _) ->
+ erlang:nif_error(undef).
+
+-spec atanh(X) -> float() when
+ X :: number().
+atanh(_) ->
+ erlang:nif_error(undef).
+
+-spec cos(X) -> float() when
+ X :: number().
+cos(_) ->
+ erlang:nif_error(undef).
+
+-spec cosh(X) -> float() when
+ X :: number().
+cosh(_) ->
+ erlang:nif_error(undef).
+
+-spec erf(X) -> float() when
+ X :: number().
+erf(_) ->
+ erlang:nif_error(undef).
+
+-spec erfc(X) -> float() when
+ X :: number().
+erfc(_) ->
+ erlang:nif_error(undef).
+
+-spec exp(X) -> float() when
+ X :: number().
+exp(_) ->
+ erlang:nif_error(undef).
+
+-spec log(X) -> float() when
+ X :: number().
+log(_) ->
+ erlang:nif_error(undef).
+
+-spec log10(X) -> float() when
+ X :: number().
+log10(_) ->
+ erlang:nif_error(undef).
+
+-spec pow(X, Y) -> float() when
+ X :: number(),
+ Y :: number().
+pow(_, _) ->
+ erlang:nif_error(undef).
+
+-spec sin(X) -> float() when
+ X :: number().
+sin(_) ->
+ erlang:nif_error(undef).
+
+-spec sinh(X) -> float() when
+ X :: number().
+sinh(_) ->
+ erlang:nif_error(undef).
+
+-spec sqrt(X) -> float() when
+ X :: number().
+sqrt(_) ->
+ erlang:nif_error(undef).
+
+-spec tan(X) -> float() when
+ X :: number().
+tan(_) ->
+ erlang:nif_error(undef).
+
+-spec tanh(X) -> float() when
+ X :: number().
+tanh(_) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
+
-spec pi() -> float().
pi() -> 3.1415926535897932.
diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl
index b9fbef9ed0..cddf345c76 100644
--- a/lib/stdlib/src/otp_internal.erl
+++ b/lib/stdlib/src/otp_internal.erl
@@ -349,7 +349,7 @@ obsolete_1(asn1rt, F, _) when F == load_driver; F == unload_driver ->
obsolete_1(ssl, pid, 1) ->
{deprecated,"deprecated (will be removed in R17); is no longer needed"};
obsolete_1(inviso, _, _) ->
- {deprecated,"the inviso application has been deprecated and will be removed in R16"};
+ {removed,"the inviso application was removed in R16"};
%% Added in R15B01.
obsolete_1(gs, _, _) ->
diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl
index 21504d707b..ad25fd559c 100644
--- a/lib/stdlib/src/qlc_pt.erl
+++ b/lib/stdlib/src/qlc_pt.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. 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
@@ -2186,7 +2186,7 @@ try_ms(E, P, Fltr, State) ->
{function,L,foo,0,[{clause,L,[],[],[MS0]}]} = lists:last(X),
MS = erl_parse:normalise(var2const(MS0)),
XMS = ets:match_spec_compile(MS),
- true = is_binary(XMS),
+ true = ets:is_compiled_ms(XMS),
{ok, MS, MS0}
end of
{'EXIT', _Reason} ->
diff --git a/lib/stdlib/src/re.erl b/lib/stdlib/src/re.erl
index 70566e48dd..c5109ec455 100644
--- a/lib/stdlib/src/re.erl
+++ b/lib/stdlib/src/re.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. 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
@@ -30,11 +30,65 @@
| {newline, nl_spec()}| bsr_anycrlf
| bsr_unicode.
-%% Emulator builtins in this module:
-%% re:compile/1
-%% re:compile/2
-%% re:run/2
-%% re:run/3
+%%% BIFs
+
+-export([compile/1, compile/2, run/2, run/3]).
+
+-spec compile(Regexp) -> {ok, MP} | {error, ErrSpec} when
+ Regexp :: iodata(),
+ MP :: mp(),
+ ErrSpec :: {ErrString :: string(), Position :: non_neg_integer()}.
+
+compile(_) ->
+ erlang:nif_error(undef).
+
+-spec compile(Regexp, Options) -> {ok, MP} | {error, ErrSpec} when
+ Regexp :: iodata() | unicode:charlist(),
+ Options :: [Option],
+ Option :: compile_option(),
+ MP :: mp(),
+ ErrSpec :: {ErrString :: string(), Position :: non_neg_integer()}.
+
+compile(_, _) ->
+ erlang:nif_error(undef).
+
+-spec run(Subject, RE) -> {match, Captured} | nomatch when
+ Subject :: iodata() | unicode:charlist(),
+ RE :: mp() | iodata(),
+ Captured :: [CaptureData],
+ CaptureData :: {integer(), integer()}.
+
+run(_, _) ->
+ erlang:nif_error(undef).
+
+-spec run(Subject, RE, Options) -> {match, Captured} |
+ match |
+ nomatch when
+ Subject :: iodata() | unicode:charlist(),
+ RE :: mp() | iodata() | unicode:charlist(),
+ Options :: [Option],
+ Option :: anchored | global | notbol | noteol | notempty
+ | {offset, non_neg_integer()} |
+ {newline, NLSpec :: nl_spec()} |
+ bsr_anycrlf | bsr_unicode | {capture, ValueSpec} |
+ {capture, ValueSpec, Type} | CompileOpt,
+ Type :: index | list | binary,
+ ValueSpec :: all | all_but_first | first | none | ValueList,
+ ValueList :: [ValueID],
+ ValueID :: integer() | string() | atom(),
+ CompileOpt :: compile_option(),
+ Captured :: [CaptureData] | [[CaptureData]],
+ CaptureData :: {integer(), integer()}
+ | ListConversionData
+ | binary(),
+ ListConversionData :: string()
+ | {error, string(), binary()}
+ | {incomplete, string(), binary()}.
+
+run(_, _, _) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
-spec split(Subject, RE) -> SplitList when
Subject :: iodata() | unicode:charlist(),
diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src
index 94e81188b5..55c8087475 100644
--- a/lib/stdlib/src/stdlib.appup.src
+++ b/lib/stdlib/src/stdlib.appup.src
@@ -17,11 +17,11 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max two major revisions back
- [{<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
- {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
- {<<"1\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R13
+ [{<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R14
%% Down to - max two major revisions back
- [{<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
- {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
- {<<"1\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R13
+ [{<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R14
}.
diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl
index 30eac4f07d..fc029a582f 100644
--- a/lib/stdlib/src/string.erl
+++ b/lib/stdlib/src/string.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -29,6 +29,30 @@
%%---------------------------------------------------------------------------
+%%% BIFs
+
+-export([to_float/1, to_integer/1]).
+
+-spec to_float(String) -> {Float, Rest} | {error, Reason} when
+ String :: string(),
+ Float :: float(),
+ Rest :: string(),
+ Reason :: no_float | not_a_list.
+
+to_float(_) ->
+ erlang:nif_error(undef).
+
+-spec to_integer(String) -> {Int, Rest} | {error, Reason} when
+ String :: string(),
+ Int :: integer(),
+ Rest :: string(),
+ Reason :: no_integer | not_a_list.
+
+to_integer(_) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
+
%% Robert's bit
%% len(String)
diff --git a/lib/stdlib/src/unicode.erl b/lib/stdlib/src/unicode.erl
index e9b90befe6..8b9412fb1b 100644
--- a/lib/stdlib/src/unicode.erl
+++ b/lib/stdlib/src/unicode.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. 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
@@ -18,13 +18,6 @@
%%
-module(unicode).
-%% Implemented in the emulator:
-%% characters_to_binary/2 (will trap to characters_to_binary_int/2
-%% if InEncoding is not {latin1 | unicode | utf8})
-%% characters_to_list/2 (will trap to characters_to_list_int/2 if
-%% InEncoding is not {latin1 | unicode | utf8})
-%%
-
-export([characters_to_list/1, characters_to_list_int/2,
characters_to_binary/1, characters_to_binary_int/2,
characters_to_binary/3,
@@ -52,6 +45,45 @@
-type latin1_charlist() :: [latin1_char() | latin1_binary()
| latin1_charlist()].
+%%% BIFs
+%%%
+%%% characters_to_binary/2 (will trap to characters_to_binary_int/2
+%%% if InEncoding is not {latin1 | unicode | utf8})
+%%% characters_to_list/2 (will trap to characters_to_list_int/2 if
+%%% InEncoding is not {latin1 | unicode | utf8})
+
+-export([bin_is_7bit/1, characters_to_binary/2, characters_to_list/2]).
+
+-spec bin_is_7bit(Binary) -> boolean() when
+ Binary :: binary().
+
+bin_is_7bit(_) ->
+ erlang:nif_error(undef).
+
+-spec characters_to_binary(Data, InEncoding) -> Result when
+ Data :: latin1_chardata() | chardata() | external_chardata(),
+ InEncoding :: encoding(),
+ Result :: binary()
+ | {error, binary(), RestData}
+ | {incomplete, binary(), binary()},
+ RestData :: latin1_chardata() | chardata() | external_chardata().
+
+characters_to_binary(_, _) ->
+ erlang:nif_error(undef).
+
+-spec characters_to_list(Data, InEncoding) -> Result when
+ Data :: latin1_chardata() | chardata() | external_chardata(),
+ InEncoding :: encoding(),
+ Result :: list()
+ | {error, list(), RestData}
+ | {incomplete, list(), binary()},
+ RestData :: latin1_chardata() | chardata() | external_chardata().
+
+characters_to_list(_, _) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
+
-spec characters_to_list(Data) -> Result when
Data :: latin1_chardata() | chardata() | external_chardata(),
Result :: list()
diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl
index c82c8159b6..c383540db7 100644
--- a/lib/stdlib/src/zip.erl
+++ b/lib/stdlib/src/zip.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. 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
@@ -1017,7 +1017,7 @@ cd_file_header_from_lh_and_pos(LH, Pos) ->
file_name_length = FileNameLength,
extra_field_length = ExtraFieldLength,
file_comment_length = 0, % FileCommentLength,
- disk_num_start = 1, % DiskNumStart,
+ disk_num_start = 0, % DiskNumStart,
internal_attr = 0, % InternalAttr,
external_attr = 0, % ExternalAttr,
local_header_offset = Pos}.
diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl
index 6f77cff2b9..66799f4d05 100644
--- a/lib/stdlib/test/dets_SUITE.erl
+++ b/lib/stdlib/test/dets_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -38,7 +38,7 @@
-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, newly_started/1, basic_v8/1, basic_v9/1,
+ newly_started/1, basic_v8/1, basic_v9/1,
open_v8/1, open_v9/1, sets_v8/1, sets_v9/1, bags_v8/1,
bags_v9/1, duplicate_bags_v8/1, duplicate_bags_v9/1,
access_v8/1, access_v9/1, dirty_mark/1, dirty_mark2/1,
@@ -95,27 +95,25 @@ end_per_testcase(_Case, _Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- case os:type() of
- vxworks -> [not_run];
- _ ->
- [basic_v8, basic_v9, open_v8, open_v9, sets_v8, sets_v9,
- bags_v8, bags_v9, duplicate_bags_v8, duplicate_bags_v9,
- newly_started, open_file_v8, open_file_v9,
- init_table_v8, init_table_v9, repair_v8, repair_v9,
- access_v8, access_v9, oldbugs_v8, oldbugs_v9,
- unsafe_assumptions, truncated_segment_array_v8,
- truncated_segment_array_v9, dirty_mark, dirty_mark2,
- bag_next_v8, bag_next_v9, hash_v8b_v8c, phash, fold_v8,
- fold_v9, fixtable_v8, fixtable_v9, match_v8, match_v9,
- select_v8, select_v9, update_counter, badarg,
- cache_sets_v8, cache_sets_v9, cache_bags_v8,
- cache_bags_v9, cache_duplicate_bags_v8,
- cache_duplicate_bags_v9, otp_4208, otp_4989,
- many_clients, otp_4906, otp_5402, simultaneous_open,
- insert_new, repair_continuation, otp_5487, otp_6206,
- otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898,
- otp_8899, otp_8903, otp_8923, otp_9282, otp_9607]
- end.
+ [
+ basic_v8, basic_v9, open_v8, open_v9, sets_v8, sets_v9,
+ bags_v8, bags_v9, duplicate_bags_v8, duplicate_bags_v9,
+ newly_started, open_file_v8, open_file_v9,
+ init_table_v8, init_table_v9, repair_v8, repair_v9,
+ access_v8, access_v9, oldbugs_v8, oldbugs_v9,
+ unsafe_assumptions, truncated_segment_array_v8,
+ truncated_segment_array_v9, dirty_mark, dirty_mark2,
+ bag_next_v8, bag_next_v9, hash_v8b_v8c, phash, fold_v8,
+ fold_v9, fixtable_v8, fixtable_v9, match_v8, match_v9,
+ select_v8, select_v9, update_counter, badarg,
+ cache_sets_v8, cache_sets_v9, cache_bags_v8,
+ cache_bags_v9, cache_duplicate_bags_v8,
+ cache_duplicate_bags_v9, otp_4208, otp_4989,
+ many_clients, otp_4906, otp_5402, simultaneous_open,
+ insert_new, repair_continuation, otp_5487, otp_6206,
+ otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898,
+ otp_8899, otp_8903, otp_8923, otp_9282, otp_9607
+ ].
groups() ->
[].
@@ -132,10 +130,6 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-not_run(suite) -> [];
-not_run(Conf) when is_list(Conf) ->
- {comment, "Not runnable VxWorks/NFS"}.
-
newly_started(doc) ->
["OTP-3621"];
newly_started(suite) ->
@@ -1949,7 +1943,7 @@ match(Config, Version) ->
%% match, badarg
MSpec = [{'_',[],['$_']}],
?line check_badarg(catch dets:match(no_table, '_'),
- dets, safe_fixtable, [no_table,true]),
+ dets, match, [no_table,'_']),
?line check_badarg(catch dets:match(T, '_', not_a_number),
dets, match, [T,'_',not_a_number]),
?line {EC1, _} = dets:select(T, MSpec, 1),
@@ -1958,7 +1952,7 @@ match(Config, Version) ->
%% match_object, badarg
?line check_badarg(catch dets:match_object(no_table, '_'),
- dets, safe_fixtable, [no_table,true]),
+ dets, match_object, [no_table,'_']),
?line check_badarg(catch dets:match_object(T, '_', not_a_number),
dets, match_object, [T,'_',not_a_number]),
?line {EC2, _} = dets:select(T, MSpec, 1),
@@ -2127,7 +2121,7 @@ select(Config, Version) ->
%% badarg
MSpec = [{'_',[],['$_']}],
?line check_badarg(catch dets:select(no_table, MSpec),
- dets, safe_fixtable, [no_table,true]),
+ dets, select, [no_table,MSpec]),
?line check_badarg(catch dets:select(T, <<17>>),
dets, select, [T,<<17>>]),
?line check_badarg(catch dets:select(T, []),
@@ -2330,7 +2324,7 @@ badarg(Config) when is_list(Config) ->
%% match_delete
?line check_badarg(catch dets:match_delete(no_table, '_'),
- dets, safe_fixtable, [no_table,true]),
+ dets, match_delete, [no_table,'_']),
%% delete_all_objects
?line check_badarg(catch dets:delete_all_objects(no_table),
@@ -2339,17 +2333,19 @@ badarg(Config) when is_list(Config) ->
%% select_delete
MSpec = [{'_',[],['$_']}],
?line check_badarg(catch dets:select_delete(no_table, MSpec),
- dets, safe_fixtable, [no_table,true]),
+ dets, select_delete, [no_table,MSpec]),
?line check_badarg(catch dets:select_delete(T, <<17>>),
dets, select_delete, [T, <<17>>]),
%% traverse, fold
- ?line check_badarg(catch dets:traverse(no_table, fun(_) -> continue end),
- dets, safe_fixtable, [no_table,true]),
- ?line check_badarg(catch dets:foldl(fun(_, A) -> A end, [], no_table),
- dets, safe_fixtable, [no_table,true]),
- ?line check_badarg(catch dets:foldr(fun(_, A) -> A end, [], no_table),
- dets, safe_fixtable, [no_table,true]),
+ TF = fun(_) -> continue end,
+ ?line check_badarg(catch dets:traverse(no_table, TF),
+ dets, traverse, [no_table,TF]),
+ FF = fun(_, A) -> A end,
+ ?line check_badarg(catch dets:foldl(FF, [], no_table),
+ dets, foldl, [FF,[],no_table]),
+ ?line check_badarg(catch dets:foldr(FF, [], no_table),
+ dets, foldl, [FF,[],no_table]),
%% close
?line ok = dets:close(T),
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index b0c7d562d5..47792d1052 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -216,13 +216,13 @@ guard_4(doc) ->
guard_4(suite) ->
[];
guard_4(Config) when is_list(Config) ->
- ?line check(fun() -> if {erlang,'+'}(3,a) -> true ; true -> false end end,
- "if {erlang,'+'}(3,a) -> true ; true -> false end.",
- false),
- ?line check(fun() -> if {erlang,is_integer}(3) -> true ; true -> false end
- end,
- "if {erlang,is_integer}(3) -> true ; true -> false end.",
- true),
+ check(fun() -> if erlang:'+'(3,a) -> true ; true -> false end end,
+ "if erlang:'+'(3,a) -> true ; true -> false end.",
+ false),
+ check(fun() -> if erlang:is_integer(3) -> true ; true -> false end
+ end,
+ "if erlang:is_integer(3) -> true ; true -> false end.",
+ true),
?line check(fun() -> [X || X <- [1,2,3], erlang:is_integer(X)] end,
"[X || X <- [1,2,3], erlang:is_integer(X)].",
[1,2,3]),
@@ -230,11 +230,11 @@ guard_4(Config) when is_list(Config) ->
end,
"if is_atom(is_integer(a)) -> true ; true -> false end.",
true),
- ?line check(fun() -> if {erlang,is_atom}({erlang,is_integer}(a)) -> true;
- true -> false end end,
- "if {erlang,is_atom}({erlang,is_integer}(a)) -> true; "
- "true -> false end.",
- true),
+ check(fun() -> if erlang:is_atom(erlang:is_integer(a)) -> true;
+ true -> false end end,
+ "if erlang:is_atom(erlang:is_integer(a)) -> true; "
+ "true -> false end.",
+ true),
?line check(fun() -> if is_atom(3+a) -> true ; true -> false end end,
"if is_atom(3+a) -> true ; true -> false end.",
false),
@@ -1077,11 +1077,6 @@ do_funs(LFH, EFH) ->
concat(["begin F1 = fun(F,N) -> apply(", M,
",count_down,[F, N]) end, F1(F1,1000) end."]),
0, ['F1'], LFH, EFH),
- ?line check(fun() -> F1 = fun(F,N) -> {?MODULE,count_down}(F,N)
- end, F1(F1, 1000) end,
- concat(["begin F1 = fun(F,N) -> {", M,
- ",count_down}(F, N) end, F1(F1,1000) end."]),
- 0, ['F1'], LFH, EFH),
?line check(fun() -> F = fun(F,N) when N > 0 -> apply(F,[F,N-1]);
(_F,0) -> ok end,
F(F, 1000)
@@ -1113,11 +1108,11 @@ do_funs(LFH, EFH) ->
true = {2,3} == F(2) end,
"begin F = fun(X) -> A = 1+X, {X,A} end,
true = {2,3} == F(2) end.", true, ['F'], LFH, EFH),
- ?line check(fun() -> F = fun(X) -> {erlang,'+'}(X,2) end,
- true = 3 == F(1) end,
- "begin F = fun(X) -> {erlang,'+'}(X,2) end,"
- " true = 3 == F(1) end.", true, ['F'],
- LFH, EFH),
+ check(fun() -> F = fun(X) -> erlang:'+'(X,2) end,
+ true = 3 == F(1) end,
+ "begin F = fun(X) -> erlang:'+'(X,2) end,"
+ " true = 3 == F(1) end.", true, ['F'],
+ LFH, EFH),
?line check(fun() -> F = fun(X) -> byte_size(X) end,
?MODULE:do_apply(F,<<"hej">>) end,
concat(["begin F = fun(X) -> size(X) end,",
diff --git a/lib/stdlib/test/erl_expand_records_SUITE.erl b/lib/stdlib/test/erl_expand_records_SUITE.erl
index 01cdb92d7b..e248934e10 100644
--- a/lib/stdlib/test/erl_expand_records_SUITE.erl
+++ b/lib/stdlib/test/erl_expand_records_SUITE.erl
@@ -157,7 +157,7 @@ expr(Config) when is_list(Config) ->
One = 1 = fun f/1(1),
2 = fun(X) -> X end(One + One),
3 = fun exprec_test:f/1(3),
- 4 = {exprec_test,f}(4),
+ 4 = exprec_test:f(4),
5 = ''.f(5),
L = receive
{a,message,L0} ->
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index dcd622b984..90a37f6441 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -50,7 +50,8 @@
unsafe_vars_try/1,
guard/1, otp_4886/1, otp_4988/1, otp_5091/1, otp_5276/1, otp_5338/1,
otp_5362/1, otp_5371/1, otp_7227/1, otp_5494/1, otp_5644/1, otp_5878/1,
- otp_5917/1, otp_6585/1, otp_6885/1, export_all/1,
+ otp_5917/1, otp_6585/1, otp_6885/1, otp_10436/1,
+ export_all/1,
bif_clash/1,
behaviour_basic/1, behaviour_multiple/1,
otp_7550/1,
@@ -80,7 +81,7 @@ all() ->
unsafe_vars, unsafe_vars2, unsafe_vars_try, guard,
otp_4886, otp_4988, otp_5091, otp_5276, otp_5338,
otp_5362, otp_5371, otp_7227, otp_5494, otp_5644,
- otp_5878, otp_5917, otp_6585, otp_6885, export_all,
+ otp_5878, otp_5917, otp_6585, otp_6885, otp_10436, export_all,
bif_clash, behaviour_basic, behaviour_multiple,
otp_7550, otp_8051, format_warn, {group, on_load},
too_many_arguments].
@@ -1307,41 +1308,30 @@ guard(Config) when is_list(Config) ->
foo;
t3(A) when erlang:is_record(A, {apa}) ->
foo;
- t3(A) when {erlang,is_record}(A, {apa}) ->
- foo;
t3(A) when is_record(A, {apa}, 1) ->
foo;
t3(A) when erlang:is_record(A, {apa}, 1) ->
foo;
- t3(A) when {erlang,is_record}(A, {apa}, 1) ->
- foo;
t3(A) when is_record(A, apa, []) ->
foo;
t3(A) when erlang:is_record(A, apa, []) ->
foo;
- t3(A) when {erlang,is_record}(A, apa, []) ->
- foo;
t3(A) when record(A, apa) ->
foo;
t3(A) when is_record(A, apa) ->
foo;
t3(A) when erlang:is_record(A, apa) ->
- foo;
- t3(A) when {erlang,is_record}(A, apa) ->
foo.
">>,
[warn_unused_vars, nowarn_obsolete_guard],
{errors,[{2,erl_lint,illegal_guard_expr},
- {4,erl_lint,illegal_guard_expr},
- {6,erl_lint,illegal_guard_expr},
- {8,erl_lint,illegal_guard_expr},
+ {4,erl_lint,illegal_guard_expr},
+ {6,erl_lint,illegal_guard_expr},
+ {8,erl_lint,illegal_guard_expr},
{10,erl_lint,illegal_guard_expr},
{12,erl_lint,illegal_guard_expr},
- {14,erl_lint,illegal_guard_expr},
- {16,erl_lint,illegal_guard_expr},
- {18,erl_lint,illegal_guard_expr},
- {20,erl_lint,illegal_guard_expr}],
- []}},
+ {14,erl_lint,illegal_guard_expr}],
+ []}},
{guard6,
<<"-record(apa,{a=a,b=foo:bar()}).
apa() ->
@@ -2397,6 +2387,28 @@ otp_6885(Config) when is_list(Config) ->
[]} = run_test2(Config, Ts, []),
ok.
+otp_10436(doc) ->
+ "OTP-6885. Warnings for opaque types.";
+otp_10436(suite) -> [];
+otp_10436(Config) when is_list(Config) ->
+ Ts = <<"-module(otp_10436).
+ -export_type([t1/0]).
+ -opaque t1() :: {i, integer()}.
+ -opaque t2() :: {a, atom()}.
+ ">>,
+ {warnings,[{4,erl_lint,{not_exported_opaque,{t2,0}}},
+ {4,erl_lint,{unused_type,{t2,0}}}]} =
+ run_test2(Config, Ts, []),
+ Ts2 = <<"-module(otp_10436_2).
+ -export_type([t1/0, t2/0]).
+ -opaque t1() :: term().
+ -opaque t2() :: any().
+ ">>,
+ {warnings,[{3,erl_lint,{underspecified_opaque,{t1,0}}},
+ {4,erl_lint,{underspecified_opaque,{t2,0}}}]} =
+ run_test2(Config, Ts2, []),
+ ok.
+
export_all(doc) ->
"OTP-7392. Warning for export_all.";
export_all(Config) when is_list(Config) ->
@@ -2845,10 +2857,10 @@ otp_8051(doc) ->
otp_8051(Config) when is_list(Config) ->
Ts = [{otp_8051,
<<"-opaque foo() :: bar().
+ -export_type([foo/0]).
">>,
[],
- {error,[{1,erl_lint,{undefined_type,{bar,0}}}],
- [{1,erl_lint,{unused_type,{foo,0}}}]}}],
+ {errors,[{1,erl_lint,{undefined_type,{bar,0}}}],[]}}],
?line [] = run(Config, Ts),
ok.
diff --git a/lib/stdlib/test/escript_SUITE.erl b/lib/stdlib/test/escript_SUITE.erl
index 7ed1ee742a..5b592c65cc 100644
--- a/lib/stdlib/test/escript_SUITE.erl
+++ b/lib/stdlib/test/escript_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -29,6 +29,7 @@
module_script/1,
beam_script/1,
archive_script/1,
+ archive_script_file_access/1,
epp/1,
create_and_extract/1,
foldl/1,
@@ -44,7 +45,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[basic, errors, strange_name, emulator_flags,
module_script, beam_script, archive_script, epp,
- create_and_extract, foldl, overflow].
+ create_and_extract, foldl, overflow,
+ archive_script_file_access].
groups() ->
[].
@@ -62,7 +64,7 @@ end_per_group(_GroupName, Config) ->
Config.
init_per_testcase(_Case, Config) ->
- ?line Dog = ?t:timetrap(?t:minutes(2)),
+ ?line Dog = ?t:timetrap(?t:minutes(5)),
[{watchdog,Dog}|Config].
end_per_testcase(_Case, Config) ->
@@ -356,7 +358,7 @@ beam_script(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create an archive file containing two entire applications plus two
%% alternate main modules. Generate a new escript containing the archive
-%% (with .app and .beam files and ) and the escript header.
+%% (with .app and .beam files and) and the escript header.
archive_script(Config) when is_list(Config) ->
%% Copy the orig files to priv_dir
@@ -464,6 +466,147 @@ archive_script(Config) when is_list(Config) ->
ok.
+%% Test the correction of OTP-10071
+%% The errors identified are
+%%
+%% a) If primary archive was named "xxx", then a file in the same
+%% directory named "xxxyyy" would be interpreted as a file named yyy
+%% inside the archive.
+%%
+%% b) erl_prim_loader did not correctly create and normalize absolute
+%% paths for primary archive and files inside it, so unless given
+%% with exact same path files inside the archive would not be
+%% found. E.g. if escript was started as ./xxx then "xxx/file"
+%% would not be found since erl_prim_loader would try to match
+%% /full/path/to/xxx with /full/path/to/./xxx. Same problem with
+%% ../. Also, the use of symlinks in the path to the archive would
+%% cause problems.
+%%
+%% c) Depending on how the primary archive was built,
+%% erl_prim_loader:list_dir/1 would sometimes return an empty string
+%% inside the file list. This was a virtual element representing the
+%% top directory of the archive. This shall not occur.
+%%
+archive_script_file_access(Config) when is_list(Config) ->
+ %% Copy the orig files to priv_dir
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ MainMod = "archive_script_file_access",
+ MainSrc = MainMod ++ ".erl",
+ MainBeam = MainMod ++ ".beam",
+
+ Archive = filename:join([PrivDir, "archive_script_file_access.zip"]),
+ ?line {ok, _} = zip:create(Archive, ["archive_script_file_access"],
+ [{compress, []}, {cwd, DataDir}]),
+ ?line {ok, _} = zip:extract(Archive, [{cwd, PrivDir}]),
+ TopDir = filename:join([PrivDir, "archive_script_file_access"]),
+
+ %% Compile the code
+ ?line ok = compile_files([MainSrc], TopDir, TopDir),
+
+ %% First, create a file structure which will be included in the archive:
+ %%
+ %% dir1/
+ %% dir1/subdir1/
+ %% dir1/subdir1/file1
+ %%
+ {ok, OldDir} = file:get_cwd(),
+ ok = file:set_cwd(TopDir),
+ DummyDir = "dir1",
+ DummySubDir = filename:join(DummyDir, "subdir1"),
+ RelDummyFile = filename:join(DummySubDir, "file1"),
+ DummyFile = filename:join(TopDir,RelDummyFile),
+ ok = filelib:ensure_dir(DummyFile),
+ ok = file:write_file(DummyFile, ["foo\nbar\nbaz"]),
+
+ %% 1. Create zip archive by adding the dummy file and the beam
+ %% file as binaries to zip.
+ %%
+ %% This used to provoke the following issues when the script was run as
+ %% "./<script_name>":
+ %% a. erl_prim_loader:read_file_info/1 returning 'error'
+ %% b. erl_prim_loader:list_dir/1 returning {ok, ["dir1", [], "file1"]}
+ %% leading to an infinite loop in reltool_target:spec_dir/1
+ Files1 =
+ lists:map(fun(Filename) ->
+ {ok, Bin} = file:read_file(Filename),
+ {Filename,Bin}
+ end,
+ [RelDummyFile,MainBeam]),
+ {ok, {"mem", Bin1}} = zip:create("mem", Files1, [memory]),
+
+ %% Create the escript
+ ScriptName1 = "archive_script_file_access1",
+ Script1 = filename:join([PrivDir, ScriptName1]),
+ Flags = "-escript main " ++ MainMod,
+ ok = escript:create(Script1,[shebang,{emu_args,Flags},{archive,Bin1}]),
+ ok = file:change_mode(Script1,8#00744),
+
+ %% If supported, create a symlink to the script. This is used to
+ %% test error b) described above this test case.
+ SymlinkName1 = "symlink_to_"++ScriptName1,
+ Symlink1 = filename:join([PrivDir, SymlinkName1]),
+ file:make_symlink(ScriptName1,Symlink1), % will fail if not supported
+
+ %% Also add a dummy file in the same directory with the same name
+ %% as the script except is also has an extension. This used to
+ %% test error a) described above this test case.
+ ok = file:write_file(Script1 ++ ".extension",
+ <<"same name as script, but with extension">>),
+
+ %% Change to script's directory and run it as "./<script_name>"
+ ok = file:set_cwd(PrivDir),
+ run(PrivDir, "./" ++ ScriptName1 ++ " " ++ ScriptName1,
+ [<<"ExitCode:0">>]),
+ ok = file:set_cwd(TopDir),
+
+
+ %% 2. Create zip archive by letting zip read the files from the file system
+ %%
+ %% The difference compared to the archive_script_file_access1 is
+ %% that this will have a file element for each directory in the
+ %% archive - while archive_script_file_access1 will only have a
+ %% file element per regular file.
+ Files2 = [DummyDir,MainBeam],
+ {ok, {"mem", Bin2}} = zip:create("mem", Files2, [memory]),
+
+ %% Create the escript
+ ScriptName2 = "archive_script_file_access2",
+ Script2 = filename:join([PrivDir, ScriptName2]),
+ ok = escript:create(Script2,[shebang,{emu_args,Flags},{archive,Bin2}]),
+ ok = file:change_mode(Script2,8#00744),
+
+ %% Also add a dummy file in the same directory with the same name
+ %% as the script except is also has an extension. This used to
+ %% test error a) described above this test case.
+ ok = file:write_file(Script2 ++ ".extension",
+ <<"same name as script, but with extension">>),
+
+ %% If supported, create a symlink to the script. This is used to
+ %% test error b) described above this test case.
+ SymlinkName2 = "symlink_to_"++ScriptName2,
+ Symlink2 = filename:join([PrivDir, SymlinkName2]),
+ file:make_symlink(ScriptName2,Symlink2), % will fail if not supported
+
+ %% Change to script's directory and run it as "./<script_name>"
+ ok = file:set_cwd(PrivDir),
+ run(PrivDir, "./" ++ ScriptName2 ++ " " ++ ScriptName2,
+ [<<"ExitCode:0">>]),
+
+ %% 3. If symlinks are supported, run one of the scripts via a symlink.
+ %%
+ %% This is in order to test error b) described above this test case.
+ case file:read_link(Symlink2) of
+ {ok,_} ->
+ run(PrivDir, "./" ++ SymlinkName2 ++ " " ++ ScriptName2,
+ [<<"ExitCode:0">>]);
+ _ -> % not supported
+ ok
+ end,
+ ok = file:set_cwd(OldDir).
+
+
compile_app(TopDir, AppName) ->
AppDir = filename:join([TopDir, AppName]),
SrcDir = filename:join([AppDir, "src"]),
@@ -475,7 +618,7 @@ compile_files([File | Files], SrcDir, OutDir) ->
case filename:extension(File) of
".erl" ->
AbsFile = filename:join([SrcDir, File]),
- case compile:file(AbsFile, [{outdir, OutDir}]) of
+ case compile:file(AbsFile, [{outdir, OutDir},report_errors]) of
{ok, _Mod} ->
compile_files(Files, SrcDir, OutDir);
Error ->
diff --git a/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl b/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl
index de56579998..1cddef0e3d 100644
--- a/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl
+++ b/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. 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
@@ -21,6 +21,8 @@
-export([main/1]).
+-include_lib("kernel/include/file.hrl").
+
-define(DUMMY, archive_script_dummy).
-define(DICT, archive_script_dict).
@@ -32,7 +34,7 @@ main(MainArgs) ->
io:format("dummy:~p\n",[[E || E <- ErlArgs, element(1, E) =:= ?DUMMY]]),
%% Start the applications
- {error, {not_started, ?DICT}} = application:start(archive_script_dummy),
+ {error, {not_started, ?DICT}} = application:start(?DUMMY),
ok = application:start(?DICT),
ok = application:start(?DUMMY),
@@ -57,4 +59,17 @@ main(MainArgs) ->
ok = ?DICT:erase(Tab, Key),
error = ?DICT:find(Tab, Key),
ok = ?DICT:erase(Tab),
+
+ %% Check mtime related caching bug with escript/primary archive files
+ Escript = escript:script_name(),
+ {ok, FileInfo} = file:read_file_info(Escript),
+ %% Modify mtime of archive file and try to reload module
+ FileInfo2 = FileInfo#file_info{mtime=calendar:now_to_local_time(now())},
+ ok = file:write_file_info(Escript, FileInfo2),
+ Module = ?DICT,
+ {file, _} = code:is_loaded(Module),
+ true = code:delete(Module),
+ false = code:is_loaded(Module),
+ {module, Module} = code:ensure_loaded(Module),
+
ok.
diff --git a/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl b/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl
new file mode 100644
index 0000000000..b03c8ba70d
--- /dev/null
+++ b/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl
@@ -0,0 +1,105 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(archive_script_file_access).
+-behaviour(escript).
+
+-export([main/1]).
+
+-include_lib("kernel/include/file.hrl").
+
+main([RelArchiveFile]) ->
+
+ AbsArchiveFile = filename:absname(RelArchiveFile),
+ DotSlashArchiveFile = "./" ++ RelArchiveFile,
+
+ Beam = atom_to_list(?MODULE) ++ ".beam",
+ AbsBeam = filename:join(AbsArchiveFile,Beam),
+ RelBeam = filename:join(RelArchiveFile,Beam),
+ DotSlashBeam = filename:join(DotSlashArchiveFile,Beam),
+ Dir = "dir1",
+ AbsDir = filename:join(AbsArchiveFile,Dir),
+ RelDir = filename:join(RelArchiveFile,Dir),
+ DotSlashDir = filename:join(DotSlashArchiveFile,Dir),
+ SubDir = "subdir1",
+ AbsSubDir = filename:join(AbsDir,SubDir),
+ RelSubDir = filename:join(RelDir,SubDir),
+ DotSlashSubDir = filename:join(DotSlashDir,SubDir),
+
+ {ok,List1} = erl_prim_loader:list_dir(AbsArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(RelArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(DotSlashArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(AbsArchiveFile ++ "/"),
+ {ok,List1} = erl_prim_loader:list_dir(AbsArchiveFile ++ "/."),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([AbsDir,".."])),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([RelDir,".."])),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([DotSlashDir,".."])),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([AbsSubDir,"..",".."])),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([RelSubDir,"..",".."])),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([DotSlashSubDir,"..",".."])),
+ false = lists:member([],List1),
+
+ %% If symlinks are supported on this platform...
+ RelSymlinkArchiveFile = "symlink_to_" ++ RelArchiveFile,
+ case file:read_link(RelSymlinkArchiveFile) of
+ {ok,_} ->
+ DotSlashSymlinkArchiveFile = "./" ++ RelSymlinkArchiveFile,
+ AbsSymlinkArchiveFile=filename:join(filename:dirname(AbsArchiveFile),
+ RelSymlinkArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(AbsSymlinkArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(RelSymlinkArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(DotSlashSymlinkArchiveFile);
+ _ -> % not supported
+ ok
+ end,
+
+
+ {ok,List2} = erl_prim_loader:list_dir(AbsDir),
+ {ok,List2} = erl_prim_loader:list_dir(RelDir),
+ {ok,List2} = erl_prim_loader:list_dir(DotSlashDir),
+ false = lists:member([],List2),
+
+ error = erl_prim_loader:list_dir(AbsBeam),
+ error = erl_prim_loader:list_dir(RelBeam),
+ error = erl_prim_loader:list_dir(DotSlashBeam),
+
+ error = erl_prim_loader:get_file(AbsArchiveFile),
+ error = erl_prim_loader:get_file(RelArchiveFile),
+ error = erl_prim_loader:get_file(DotSlashArchiveFile),
+ error = erl_prim_loader:get_file(AbsArchiveFile ++ "/"),
+ error = erl_prim_loader:get_file(AbsArchiveFile ++ "/."),
+ {ok,Bin,AbsBeam} = erl_prim_loader:get_file(AbsBeam),
+ {ok,Bin,RelBeam} = erl_prim_loader:get_file(RelBeam),
+ {ok,Bin,DotSlashBeam} = erl_prim_loader:get_file(DotSlashBeam),
+
+ {ok,#file_info{type=directory}=DFI} =
+ erl_prim_loader:read_file_info(AbsArchiveFile),
+ {ok,DFI} = erl_prim_loader:read_file_info(RelArchiveFile),
+ {ok,DFI} = erl_prim_loader:read_file_info(DotSlashArchiveFile),
+ {ok,DFI} = erl_prim_loader:read_file_info(AbsArchiveFile ++ "/"),
+ {ok,DFI} = erl_prim_loader:read_file_info(AbsArchiveFile ++ "/."),
+ {ok,#file_info{type=regular}=RFI} = erl_prim_loader:read_file_info(AbsBeam),
+ {ok,RFI} = erl_prim_loader:read_file_info(RelBeam),
+ {ok,RFI} = erl_prim_loader:read_file_info(DotSlashBeam),
+
+ F = AbsArchiveFile ++ ".extension",
+ error = erl_prim_loader:list_dir(F),
+ {ok,_,_} = erl_prim_loader:get_file(F),
+ {ok,#file_info{type=regular}} = erl_prim_loader:read_file_info(F),
+
+ ok.
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 297c4ec1c9..d5c1c5276e 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -74,6 +74,7 @@
-export([bad_table/1, types/1]).
-export([otp_9932/1]).
-export([otp_9423/1]).
+-export([otp_10182/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
%% Convenience for manual testing
@@ -146,6 +147,7 @@ all() ->
exit_many_large_table_owner, exit_many_tables_owner,
exit_many_many_tables_owner, write_concurrency, heir,
give_away, setopts, bad_table, types,
+ otp_10182,
otp_9932,
otp_9423].
@@ -1024,6 +1026,8 @@ t_test_ms(Config) when is_list(Config) ->
[{{'$1','$2'},[{'<','$1','$2'}],['$$']}]),
?line {ok,false} = ets:test_ms({a,b},
[{{'$1','$2'},[{'>','$1','$2'}],['$$']}]),
+ Tpl = {a,gb_sets:new()},
+ ?line {ok,Tpl} = ets:test_ms(Tpl, [{{'_','_'}, [], ['$_']}]), % OTP-10190
?line {error,[{error,String}]} = ets:test_ms({a,b},
[{{'$1','$2'},
[{'flurp','$1','$2'}],
@@ -4204,21 +4208,13 @@ heavy_lookup_element(Config) when is_list(Config) ->
repeat_for_opts(heavy_lookup_element_do).
heavy_lookup_element_do(Opts) ->
- ?line EtsMem = etsmem(),
- ?line Tab = ets_new(foobar_table, [set, protected, {keypos, 2} | Opts]),
- ?line ok = fill_tab2(Tab, 0, 7000),
- case os:type() of
- vxworks ->
- ?line ?t:do_times(5, ?MODULE, do_lookup_element,
- [Tab, 6999, 1]);
- % lookup ALL elements 5 times.
- _ ->
- ?line ?t:do_times(50, ?MODULE, do_lookup_element,
- [Tab, 6999, 1])
- % lookup ALL elements 50 times.
- end,
- ?line true = ets:delete(Tab),
- ?line verify_etsmem(EtsMem).
+ EtsMem = etsmem(),
+ Tab = ets_new(foobar_table, [set, protected, {keypos, 2} | Opts]),
+ ok = fill_tab2(Tab, 0, 7000),
+ % lookup ALL elements 50 times
+ ?t:do_times(50, ?MODULE, do_lookup_element, [Tab, 6999, 1]),
+ true = ets:delete(Tab),
+ verify_etsmem(EtsMem).
do_lookup_element(_Tab, 0, _) -> ok;
do_lookup_element(Tab, N, M) ->
@@ -5470,6 +5466,20 @@ otp_9423(Config) when is_list(Config) ->
Skipped -> Skipped
end.
+
+%% Corrupted binary in compressed table
+otp_10182(Config) when is_list(Config) ->
+ Bin = <<"aHR0cDovL2hvb3RzdWl0ZS5jb20vYy9wcm8tYWRyb2xsLWFi">>,
+ Key = {test, Bin},
+ Value = base64:decode(Bin),
+ In = {Key,Value},
+ Db = ets:new(undefined, [set, protected, {read_concurrency, true}, compressed]),
+ ets:insert(Db, In),
+ [Out] = ets:lookup(Db, Key),
+ io:format("In : ~p\nOut: ~p\n", [In,Out]),
+ ets:delete(Db),
+ In = Out.
+
diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl
index 1de639a166..1fd7518519 100644
--- a/lib/stdlib/test/filelib_SUITE.erl
+++ b/lib/stdlib/test/filelib_SUITE.erl
@@ -176,9 +176,64 @@ do_wildcard_5(Dir, Wcf) ->
%% Cleanup
?line del(Files),
- ?line foreach(fun(D) -> ok = file:del_dir(filename:join(Dir, D)) end, Dirs).
+ ?line foreach(fun(D) -> ok = file:del_dir(filename:join(Dir, D)) end, Dirs),
+ do_wildcard_6(Dir, Wcf).
+
+do_wildcard_6(Dir, Wcf) ->
+ ok = file:make_dir(filename:join(Dir, "xbin")),
+ All = ["xbin/a.x","xbin/b.x","xbin/c.x"],
+ Files = mkfiles(All, Dir),
+ All = Wcf("xbin/*.x"),
+ All = Wcf("xbin/*"),
+ ["xbin"] = Wcf("*"),
+ All = Wcf("*/*"),
+ del(Files),
+ ok = file:del_dir(filename:join(Dir, "xbin")),
+ do_wildcard_7(Dir, Wcf).
+
+do_wildcard_7(Dir, Wcf) ->
+ Dirs = ["blurf","xa","yyy"],
+ SubDirs = ["blurf/nisse"],
+ foreach(fun(D) ->
+ ok = file:make_dir(filename:join(Dir, D))
+ end, Dirs ++ SubDirs),
+ All = ["blurf/nisse/baz","xa/arne","xa/kalle","yyy/arne"],
+ Files = mkfiles(lists:reverse(All), Dir),
+ %% Test.
+ Listing = Wcf("**"),
+ ["blurf","blurf/nisse","blurf/nisse/baz",
+ "xa","xa/arne","xa/kalle","yyy","yyy/arne"] = Listing,
+ Listing = Wcf("**/*"),
+ ["xa/arne","yyy/arne"] = Wcf("**/arne"),
+ ["blurf/nisse"] = Wcf("**/nisse"),
+ [] = Wcf("mountain/**"),
+
+ %% Cleanup
+ del(Files),
+ foreach(fun(D) ->
+ ok = file:del_dir(filename:join(Dir, D))
+ end, SubDirs ++ Dirs),
+ do_wildcard_8(Dir, Wcf).
+
+do_wildcard_8(Dir, Wcf) ->
+ Dirs0 = ["blurf"],
+ Dirs1 = ["blurf/nisse"],
+ Dirs2 = ["blurf/nisse/a", "blurf/nisse/b"],
+ foreach(fun(D) ->
+ ok = file:make_dir(filename:join(Dir, D))
+ end, Dirs0 ++ Dirs1 ++ Dirs2),
+ All = ["blurf/nisse/a/1.txt", "blurf/nisse/b/2.txt", "blurf/nisse/b/3.txt"],
+ Files = mkfiles(lists:reverse(All), Dir),
+ %% Test.
+ All = Wcf("**/blurf/**/*.txt"),
+
+ %% Cleanup
+ del(Files),
+ foreach(fun(D) ->
+ ok = file:del_dir(filename:join(Dir, D))
+ end, Dirs2 ++ Dirs1 ++ Dirs0).
fold_files(Config) when is_list(Config) ->
?line Dir = filename:join(?config(priv_dir, Config), "fold_files"),
diff --git a/lib/stdlib/test/filename_SUITE.erl b/lib/stdlib/test/filename_SUITE.erl
index 4cfa589660..232df6a13f 100644
--- a/lib/stdlib/test/filename_SUITE.erl
+++ b/lib/stdlib/test/filename_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -25,7 +25,7 @@
-export([pathtype/1,rootname/1,split/1,find_src/1]).
-export([absname_bin/1, absname_bin_2/1,
basename_bin_1/1, basename_bin_2/1,
- dirname_bin/1, extension_bin/1, join_bin/1]).
+ dirname_bin/1, extension_bin/1, join_bin/1, t_nativename_bin/1]).
-export([pathtype_bin/1,rootname_bin/1,split_bin/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -38,7 +38,7 @@ all() ->
join, pathtype, rootname, split, t_nativename, find_src,
absname_bin, absname_bin_2, basename_bin_1, basename_bin_2, dirname_bin,
extension_bin,
- join_bin, pathtype_bin, rootname_bin, split_bin].
+ join_bin, pathtype_bin, rootname_bin, split_bin, t_nativename_bin].
groups() ->
[].
@@ -112,19 +112,6 @@ absname(Config) when is_list(Config) ->
?line "/erlang/src" = filename:absname(["/erl",'a','ng',"/",'s',"rc"]),
?line "/erlang/src" = filename:absname("/erlang///src"),
?line "/file_sorter.erl" = filename:absname([file_sorter|'.erl']),
- ok;
- vxworks ->
- Test_dir = ?config(priv_dir, Config),
- Test1 = Test_dir ++ "/foo",
- Test2 = Test_dir ++ "/ebin",
- ?line ok = file:set_cwd(Test_dir),
- ?line Test1 = filename:absname(foo),
- ?line Test1= filename:absname("foo"),
- ?line Test2 = filename:absname("foo/../ebin"),
- ?line "/erlang" = filename:absname("/erlang"),
- ?line "/erlang/src" = filename:absname("/erlang/src"),
- ?line "/erlang/src" = filename:absname(["/erlan",'g/s',"rc"]),
- ?line "/erlang/src" = filename:absname("/erlang///src"),
ok
end.
@@ -179,15 +166,6 @@ absname_2(Config) when is_list(Config) ->
?line "/erlang" = filename:absname("/erlang", "/"),
?line "/erlang/src" = filename:absname("/erlang/src", "/"),
?line "/erlang/src" = filename:absname("/erlang///src", "/"),
- ok;
- vxworks ->
- ?line "/usr/foo" = filename:absname(foo, "/usr"),
- ?line "/usr/foo" = filename:absname("foo", "/usr"),
- ?line "/usr/ebin" = filename:absname("../ebin", "/usr"),
- ?line "/usr/ebin" = filename:absname("../ebin", "/usr/src"),
- ?line "/erlang" = filename:absname("/erlang", "/usr"),
- ?line "/erlang/src" = filename:absname("/erlang/src", "/usr"),
- ?line "/erlang/src" = filename:absname("/erlang///src", "/usr"),
ok
end.
@@ -213,11 +191,7 @@ basename_1(Config) when is_list(Config) ->
?line "foo" = filename:basename("A:foo");
{unix, _} ->
?line "strange\\but\\true" =
- filename:basename("strange\\but\\true");
- vxworks ->
- ?line "foo" = filename:basename(["usr\\foo\\"]),
- ?line "foo" = filename:basename("elrond:usr\\foo\\"),
- ?line "foo" = filename:basename("disk:/foo")
+ filename:basename("strange\\but\\true")
end,
?line test_server:timetrap_cancel(Dog),
ok.
@@ -249,15 +223,7 @@ basename_2(Config) when is_list(Config) ->
?line "strange\\but\\true" =
filename:basename("strange\\but\\true.erl", ".erl"),
?line "strange\\but\\true" =
- filename:basename("strange\\but\\true", ".erl");
- vxworks ->
- ?line "foo" = filename:basename("net:foo", ".erl"),
- ?line "foo.erl" = filename:basename("net:\\usr\\foo.erl",
- ".hrl"),
- ?line "foo.erl" =
- filename:basename("/disk0:\\usr.hrl\\foo.erl",
- ".hrl"),
- ?line "foo" = filename:basename("/home\\usr\\foo", ".hrl")
+ filename:basename("strange\\but\\true", ".erl")
end,
?line test_server:timetrap_cancel(Dog),
ok.
@@ -267,37 +233,25 @@ basename_2(Config) when is_list(Config) ->
dirname(Config) when is_list(Config) ->
case os:type() of
{win32,_} ->
- ?line "A:/usr" = filename:dirname("A:/usr/foo.erl"),
- ?line "A:usr" = filename:dirname("A:usr/foo.erl"),
- ?line "/usr" = filename:dirname("\\usr\\foo.erl"),
- ?line "/" = filename:dirname("\\usr"),
- ?line "A:" = filename:dirname("A:");
- vxworks ->
- ?line "net:/usr" = filename:dirname("net:/usr/foo.erl"),
- ?line "/disk0:/usr" = filename:dirname("/disk0:/usr/foo.erl"),
- ?line "/usr" = filename:dirname("\\usr\\foo.erl"),
- ?line "/usr" = filename:dirname("\\usr"),
- ?line "net:" = filename:dirname("net:");
+ "A:/usr" = filename:dirname("A:/usr/foo.erl"),
+ "A:usr" = filename:dirname("A:usr/foo.erl"),
+ "/usr" = filename:dirname("\\usr\\foo.erl"),
+ "/" = filename:dirname("\\usr"),
+ "A:" = filename:dirname("A:");
_ -> true
end,
- ?line "usr" = filename:dirname("usr///foo.erl"),
- ?line "." = filename:dirname("foo.erl"),
- ?line "." = filename:dirname("."),
- ?line "usr" = filename:dirname('usr/foo.erl'),
- ?line "usr" = filename:dirname(['usr','/foo.erl']),
- ?line "usr" = filename:dirname(['us','r/foo.erl']),
- ?line "usr" = filename:dirname(['usr/','/foo.erl']),
- ?line "usr" = filename:dirname(['usr/','foo.erl']),
- ?line "usr" = filename:dirname(['usr/'|'foo.erl']),
- ?line "usr" = filename:dirname(['usr/f','oo.erl']),
- case os:type() of
- vxworks ->
- ?line "/" = filename:dirname("/"),
- ?line "/usr" = filename:dirname("/usr");
- _ ->
- ?line "/" = filename:dirname("/"),
- ?line "/" = filename:dirname("/usr")
- end,
+ "usr" = filename:dirname("usr///foo.erl"),
+ "." = filename:dirname("foo.erl"),
+ "." = filename:dirname("."),
+ "usr" = filename:dirname('usr/foo.erl'),
+ "usr" = filename:dirname(['usr','/foo.erl']),
+ "usr" = filename:dirname(['us','r/foo.erl']),
+ "usr" = filename:dirname(['usr/','/foo.erl']),
+ "usr" = filename:dirname(['usr/','foo.erl']),
+ "usr" = filename:dirname(['usr/'|'foo.erl']),
+ "usr" = filename:dirname(['usr/f','oo.erl']),
+ "/" = filename:dirname("/"),
+ "/" = filename:dirname("/usr"),
ok.
@@ -319,12 +273,6 @@ extension(Config) when is_list(Config) ->
filename:extension("A:/usr.bar/foo.nisse.erl"),
?line "" = filename:extension("A:/usr.bar/foo"),
ok;
- vxworks ->
- ?line "" = filename:extension("/disk0:\\usr\\foo"),
- ?line ".erl" =
- filename:extension("net:/usr.bar/foo.nisse.erl"),
- ?line "" = filename:extension("net:/usr.bar/foo"),
- ok;
_ -> ok
end.
@@ -369,25 +317,6 @@ join(Config) when is_list(Config) ->
filename:join(["A:","C:usr","foo.erl"]),
?line "d:/foo" = filename:join([$D, $:, $/, []], "foo"),
ok;
- vxworks ->
- ?line "Net:" = filename:join(["Net:/"]),
- ?line "net:" = filename:join(["net:\\"]),
- ?line "net:/abc" = filename:join(["net:/", "abc"]),
- ?line "net:/abc" = filename:join(["net:", "abc"]),
- ?line "a/b/c/d/e/f/g" =
- filename:join(["a//b\\c//\\/\\d/\\e/f\\g"]),
- ?line "net:/usr/foo.erl" =
- filename:join(["net:","usr","foo.erl"]),
- ?line "/usr/foo.erl" =
- filename:join(["net:","/usr","foo.erl"]),
- ?line "/target:usr" = filename:join("net:","/target:usr"),
- ?line "kernel:/usr" = filename:join("net:", "kernel:/usr"),
- ?line "foo:/usr/foo.erl" =
- filename:join(["A:","foo:/usr","foo.erl"]),
- ?line "/disk0:usr/foo.erl" =
- filename:join(["kalle:","/disk0:usr","foo.erl"]),
- ?line "D:/foo" = filename:join([$D, $:, $/, []], "foo"),
- ok;
{unix, _} ->
ok
end.
@@ -406,10 +335,6 @@ pathtype(Config) when is_list(Config) ->
{unix, _} ->
?line absolute = filename:pathtype("/"),
?line absolute = filename:pathtype("/usr/local/bin"),
- ok;
- vxworks ->
- ?line absolute = filename:pathtype("/usr/local/bin"),
- ?line absolute = filename:pathtype("net:usr/local/bin"),
ok
end.
@@ -424,12 +349,7 @@ rootname(Config) when is_list(Config) ->
ok.
split(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- ?line ["/usr","local","bin"] = filename:split("/usr/local/bin");
- _ ->
- ?line ["/","usr","local","bin"] = filename:split("/usr/local/bin")
- end,
+ ?line ["/","usr","local","bin"] = filename:split("/usr/local/bin"),
?line ["foo","bar"]= filename:split("foo/bar"),
?line ["foo", "bar", "hello"]= filename:split("foo////bar//hello"),
?line ["foo", "bar", "hello"]= filename:split(["foo//",'//bar//h',"ello"]),
@@ -447,18 +367,6 @@ split(Config) when is_list(Config) ->
?line ["a:","msdev","include"] =
filename:split("a:msdev\\include"),
ok;
- vxworks ->
- ?line ["net:","msdev","include"] =
- filename:split("net:/msdev/include"),
- ?line ["Target:","msdev","include"] =
- filename:split("Target:/msdev/include"),
- ?line ["msdev","include"] =
- filename:split("msdev\\include"),
- ?line ["/disk0:","msdev","include"] =
- filename:split("/disk0:\\msdev\\include"),
- ?line ["a:","msdev","include"] =
- filename:split("a:msdev\\include"),
- ok;
_ ->
ok
end.
@@ -657,56 +565,38 @@ basename_bin_2(Config) when is_list(Config) ->
dirname_bin(Config) when is_list(Config) ->
case os:type() of
{win32,_} ->
- ?line <<"A:/usr">> = filename:dirname(<<"A:/usr/foo.erl">>),
- ?line <<"A:usr">> = filename:dirname(<<"A:usr/foo.erl">>),
- ?line <<"/usr">> = filename:dirname(<<"\\usr\\foo.erl">>),
- ?line <<"/">> = filename:dirname(<<"\\usr">>),
- ?line <<"A:">> = filename:dirname(<<"A:">>);
- vxworks ->
- ?line <<"net:/usr">> = filename:dirname(<<"net:/usr/foo.erl">>),
- ?line <<"/disk0:/usr">> = filename:dirname(<<"/disk0:/usr/foo.erl">>),
- ?line <<"/usr">> = filename:dirname(<<"\\usr\\foo.erl">>),
- ?line <<"/usr">> = filename:dirname(<<"\\usr">>),
- ?line <<"net:">> = filename:dirname(<<"net:">>);
+ <<"A:/usr">> = filename:dirname(<<"A:/usr/foo.erl">>),
+ <<"A:usr">> = filename:dirname(<<"A:usr/foo.erl">>),
+ <<"/usr">> = filename:dirname(<<"\\usr\\foo.erl">>),
+ <<"/">> = filename:dirname(<<"\\usr">>),
+ <<"A:">> = filename:dirname(<<"A:">>);
_ -> true
end,
- ?line <<"usr">> = filename:dirname(<<"usr///foo.erl">>),
- ?line <<".">> = filename:dirname(<<"foo.erl">>),
- ?line <<".">> = filename:dirname(<<".">>),
- case os:type() of
- vxworks ->
- ?line <<"/">> = filename:dirname(<<"/">>),
- ?line <<"/usr">> = filename:dirname(<<"/usr">>);
- _ ->
- ?line <<"/">> = filename:dirname(<<"/">>),
- ?line <<"/">> = filename:dirname(<<"/usr">>)
- end,
+ <<"usr">> = filename:dirname(<<"usr///foo.erl">>),
+ <<".">> = filename:dirname(<<"foo.erl">>),
+ <<".">> = filename:dirname(<<".">>),
+ <<"/">> = filename:dirname(<<"/">>),
+ <<"/">> = filename:dirname(<<"/usr">>),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
extension_bin(Config) when is_list(Config) ->
- ?line <<".erl">> = filename:extension(<<"A:/usr/foo.erl">>),
- ?line <<".erl">> = filename:extension(<<"A:/usr/foo.nisse.erl">>),
- ?line <<".erl">> = filename:extension(<<"A:/usr.bar/foo.nisse.erl">>),
- ?line <<"">> = filename:extension(<<"A:/usr.bar/foo">>),
- ?line <<"">> = filename:extension(<<"A:/usr/foo">>),
- ?line case os:type() of
- {win32, _} ->
- ?line <<"">> = filename:extension(<<"A:\\usr\\foo">>),
- ?line <<".erl">> =
- filename:extension(<<"A:/usr.bar/foo.nisse.erl">>),
- ?line <<"">> = filename:extension(<<"A:/usr.bar/foo">>),
- ok;
- vxworks ->
- ?line <<"">> = filename:extension(<<"/disk0:\\usr\\foo">>),
- ?line <<".erl">> =
- filename:extension(<<"net:/usr.bar/foo.nisse.erl">>),
- ?line <<"">> = filename:extension(<<"net:/usr.bar/foo">>),
- ok;
- _ -> ok
- end.
+ <<".erl">> = filename:extension(<<"A:/usr/foo.erl">>),
+ <<".erl">> = filename:extension(<<"A:/usr/foo.nisse.erl">>),
+ <<".erl">> = filename:extension(<<"A:/usr.bar/foo.nisse.erl">>),
+ <<"">> = filename:extension(<<"A:/usr.bar/foo">>),
+ <<"">> = filename:extension(<<"A:/usr/foo">>),
+ case os:type() of
+ {win32, _} ->
+ ?line <<"">> = filename:extension(<<"A:\\usr\\foo">>),
+ ?line <<".erl">> =
+ filename:extension(<<"A:/usr.bar/foo.nisse.erl">>),
+ ?line <<"">> = filename:extension(<<"A:/usr.bar/foo">>),
+ ok;
+ _ -> ok
+ end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -754,53 +644,58 @@ join_bin(Config) when is_list(Config) ->
end.
pathtype_bin(Config) when is_list(Config) ->
- ?line relative = filename:pathtype(<<"..">>),
- ?line relative = filename:pathtype(<<"foo">>),
- ?line relative = filename:pathtype(<<"foo/bar">>),
- ?line relative = filename:pathtype('foo/bar'),
+ relative = filename:pathtype(<<"..">>),
+ relative = filename:pathtype(<<"foo">>),
+ relative = filename:pathtype(<<"foo/bar">>),
+ relative = filename:pathtype('foo/bar'),
case os:type() of
{win32, _} ->
- ?line volumerelative = filename:pathtype(<<"/usr/local/bin">>),
- ?line volumerelative = filename:pathtype(<<"A:usr/local/bin">>),
+ volumerelative = filename:pathtype(<<"/usr/local/bin">>),
+ volumerelative = filename:pathtype(<<"A:usr/local/bin">>),
ok;
{unix, _} ->
- ?line absolute = filename:pathtype(<<"/">>),
- ?line absolute = filename:pathtype(<<"/usr/local/bin">>),
+ absolute = filename:pathtype(<<"/">>),
+ absolute = filename:pathtype(<<"/usr/local/bin">>),
ok
end.
rootname_bin(Config) when is_list(Config) ->
- ?line <<"/jam.src/kalle">> = filename:rootname(<<"/jam.src/kalle">>),
- ?line <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>),
- ?line <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>, <<".erl">>),
- ?line <<"/jam.src/foo.jam">> = filename:rootname(<<"/jam.src/foo.jam">>, <<".erl">>),
- ?line <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j',"am"],<<".erl">>),
- ?line <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j'|am],<<".erl">>),
+ <<"/jam.src/kalle">> = filename:rootname(<<"/jam.src/kalle">>),
+ <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>),
+ <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>, <<".erl">>),
+ <<"/jam.src/foo.jam">> = filename:rootname(<<"/jam.src/foo.jam">>, <<".erl">>),
+ <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j',"am"],<<".erl">>),
+ <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j'|am],<<".erl">>),
ok.
split_bin(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- ?line [<<"/usr">>,<<"local">>,<<"bin">>] = filename:split(<<"/usr/local/bin">>);
- _ ->
- ?line [<<"/">>,<<"usr">>,<<"local">>,<<"bin">>] = filename:split(<<"/usr/local/bin">>)
- end,
- ?line [<<"foo">>,<<"bar">>]= filename:split(<<"foo/bar">>),
- ?line [<<"foo">>, <<"bar">>, <<"hello">>]= filename:split(<<"foo////bar//hello">>),
+ [<<"/">>,<<"usr">>,<<"local">>,<<"bin">>] = filename:split(<<"/usr/local/bin">>),
+ [<<"foo">>,<<"bar">>]= filename:split(<<"foo/bar">>),
+ [<<"foo">>, <<"bar">>, <<"hello">>]= filename:split(<<"foo////bar//hello">>),
case os:type() of
{win32,_} ->
- ?line [<<"a:/">>,<<"msdev">>,<<"include">>] =
+ [<<"a:/">>,<<"msdev">>,<<"include">>] =
filename:split(<<"a:/msdev/include">>),
- ?line [<<"a:/">>,<<"msdev">>,<<"include">>] =
+ [<<"a:/">>,<<"msdev">>,<<"include">>] =
filename:split(<<"A:/msdev/include">>),
- ?line [<<"msdev">>,<<"include">>] =
+ [<<"msdev">>,<<"include">>] =
filename:split(<<"msdev\\include">>),
- ?line [<<"a:/">>,<<"msdev">>,<<"include">>] =
+ [<<"a:/">>,<<"msdev">>,<<"include">>] =
filename:split(<<"a:\\msdev\\include">>),
- ?line [<<"a:">>,<<"msdev">>,<<"include">>] =
+ [<<"a:">>,<<"msdev">>,<<"include">>] =
filename:split(<<"a:msdev\\include">>),
ok;
_ ->
ok
end.
+t_nativename_bin(Config) when is_list(Config) ->
+ ?line <<"abcedf">> = filename:nativename(<<"abcedf">>),
+ case os:type() of
+ {win32, _} ->
+ ?line <<"a:\\temp\\arne.exe">> =
+ filename:nativename(<<"A:/temp//arne.exe/">>);
+ _ ->
+ ?line <<"/usr/tmp/arne">> =
+ filename:nativename(<<"/usr/tmp//arne/">>)
+ end.
diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl
index bdb4ea65b5..22f66a6c14 100644
--- a/lib/stdlib/test/gen_fsm_SUITE.erl
+++ b/lib/stdlib/test/gen_fsm_SUITE.erl
@@ -281,21 +281,12 @@ start12(Config) when is_list(Config) ->
%% Check that time outs in calls work
abnormal1(suite) -> [];
abnormal1(Config) when is_list(Config) ->
- ?line {ok, _Pid} =
- gen_fsm:start({local, my_fsm}, gen_fsm_SUITE, [], []),
+ {ok, _Pid} = gen_fsm:start({local, my_fsm}, gen_fsm_SUITE, [], []),
%% timeout call.
- case os:type() of
- vxworks ->
- %% timeout call for VxWorks must be in 16ms increments.
- ?line delayed = gen_fsm:sync_send_event(my_fsm, {delayed_answer,1}, 17),
- ?line {'EXIT',{timeout,_}} =
- (catch gen_fsm:sync_send_event(my_fsm, {delayed_answer,17}, 1));
- _ ->
- ?line delayed = gen_fsm:sync_send_event(my_fsm, {delayed_answer,1}, 100),
- ?line {'EXIT',{timeout,_}} =
- (catch gen_fsm:sync_send_event(my_fsm, {delayed_answer,10}, 1))
- end,
+ delayed = gen_fsm:sync_send_event(my_fsm, {delayed_answer,1}, 100),
+ {'EXIT',{timeout,_}} =
+ (catch gen_fsm:sync_send_event(my_fsm, {delayed_answer,10}, 1)),
test_server:messages_get(),
ok.
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index cdf15ba017..2abb01ba24 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -37,7 +37,8 @@
% spawn export
-export([spec_init_local/2, spec_init_global/2, spec_init_via/2,
- spec_init_default_timeout/2, spec_init_anonymous/1,
+ spec_init_default_timeout/2, spec_init_global_default_timeout/2,
+ spec_init_anonymous/1,
spec_init_anonymous_default_timeout/1,
spec_init_not_proc_lib/1, cast_fast_messup/0]).
@@ -230,14 +231,6 @@ start(Config) when is_list(Config) ->
end,
test_server:messages_get(),
- %% Must wait for all error messages before going to next test.
- %% (otherwise it interferes too much with real time characteristics).
- case os:type() of
- vxworks ->
- receive after 5000 -> ok end;
- _ ->
- ok
- end,
process_flag(trap_exit, OldFl),
ok.
@@ -749,7 +742,7 @@ spec_init(suite) ->
spec_init(Config) when is_list(Config) ->
OldFlag = process_flag(trap_exit, true),
-
+
?line {ok, Pid0} = start_link(spec_init_local, [{ok, my_server}, []]),
?line ok = gen_server:call(Pid0, started_p),
?line ok = gen_server:call(Pid0, stop),
@@ -819,6 +812,14 @@ spec_init(Config) when is_list(Config) ->
test_server:fail(gen_server_did_not_die)
end,
+ %% Before the OTP-10130 fix this failed because a timeout message
+ %% was generated as the spawned process crashed because a {global, Name}
+ %% was matched as a timeout value instead of matching on scope.
+ {ok, _PidHurra} =
+ start_link(spec_init_global_default_timeout, [{ok, hurra}, []]),
+ timer:sleep(1000),
+ ok = gen_server:call(_PidHurra, started_p),
+
?line Pid5 =
erlang:spawn_link(?MODULE, spec_init_not_proc_lib, [[]]),
receive
@@ -1125,6 +1126,15 @@ spec_init_default_timeout({ok, Name}, Options) ->
%% Supervised init can occur here ...
gen_server:enter_loop(?MODULE, Options, {}, {local, Name}).
+%% OTP-10130, A bug was introduced where global scope was not matched when
+%% enter_loop/4 was called (no timeout).
+spec_init_global_default_timeout({ok, Name}, Options) ->
+ process_flag(trap_exit, true),
+ global:register_name(Name, self()),
+ proc_lib:init_ack({ok, self()}),
+ %% Supervised init can occur here ...
+ gen_server:enter_loop(?MODULE, Options, {}, {global, Name}).
+
spec_init_anonymous(Options) ->
process_flag(trap_exit, true),
proc_lib:init_ack({ok, self()}),
diff --git a/lib/stdlib/test/id_transform_SUITE.erl b/lib/stdlib/test/id_transform_SUITE.erl
index e1972a100e..233b0d0a78 100644
--- a/lib/stdlib/test/id_transform_SUITE.erl
+++ b/lib/stdlib/test/id_transform_SUITE.erl
@@ -26,7 +26,7 @@
id_transform/1]).
-export([check/2,check2/1,g/0,f/1,t/1,t1/1,t2/1,t3/1,t4/1,
- t5/1,t6/1,apa/1,new_fun/0]).
+ t5/1,apa/1,new_fun/0]).
% Serves as test...
-hej(hopp).
@@ -388,8 +388,6 @@ t3(A) when is_tuple(A) or is_tuple(A) ->
is_tuple;
t3(A) when record(A, apa) ->
foo;
-t3(A) when {erlang,is_record}(A, apa) ->
- foo;
t3(A) when erlang:is_record(A, apa) ->
foo;
t3(A) when is_record(A, apa) ->
@@ -397,13 +395,10 @@ t3(A) when is_record(A, apa) ->
t3(A) when record({apa}, apa) ->
{A,foo}.
-t4(_) when {erlang,is_record}({apa}, apa) ->
- foo.
-
-t5(A) when erlang:is_record({apa}, apa) ->
+t4(A) when erlang:is_record({apa}, apa) ->
{A,foo}.
-t6(A) when is_record({apa}, apa) ->
+t5(A) when is_record({apa}, apa) ->
{A,foo}.
-record(apa2,{a=a,b=foo:bar()}).
diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl
index 1e74ad7727..e3090e4a47 100644
--- a/lib/stdlib/test/qlc_SUITE.erl
+++ b/lib/stdlib/test/qlc_SUITE.erl
@@ -2969,13 +2969,6 @@ lookup1(Config) when is_list(Config) ->
[3] = lookup_keys(Q)
end, [{1,a},{3,3}])">>,
- <<"A = 3,
- etsc(fun(E) ->
- Q = qlc:q([X || X <- ets:table(E), A =:= {erlang,element}(1, X)]),
- [{3,3}] = qlc:e(Q),
- [3] = lookup_keys(Q)
- end, [{1,a},{3,3}])">>,
-
<<"etsc(fun(E) ->
A = 3,
Q = qlc:q([X || X <- ets:table(E),
@@ -3439,12 +3432,6 @@ lookup2(Config) when is_list(Config) ->
[r] = qlc:e(Q),
[r] = lookup_keys(Q)
end, [{keypos,1}], [#r{}])">>,
- <<"etsc(fun(E) ->
- Q = qlc:q([element(1, X) || X <- ets:table(E),
- {erlang,is_record}(X, r, 2)]),
- [r] = qlc:e(Q),
- [r] = lookup_keys(Q)
- end, [{keypos,1}], [#r{}])">>,
{cres,
<<"etsc(fun(E) ->
Q = qlc:q([element(1, X) || X <- ets:table(E),
@@ -3464,12 +3451,6 @@ lookup2(Config) when is_list(Config) ->
is_record(X, r)]),
[r] = qlc:e(Q),
[r] = lookup_keys(Q)
- end, [{keypos,1}], [#r{}])">>,
- <<"etsc(fun(E) ->
- Q = qlc:q([element(1, X) || X <- ets:table(E),
- {erlang,is_record}(X, r)]),
- [r] = qlc:e(Q),
- [r] = lookup_keys(Q)
end, [{keypos,1}], [#r{}])">>
],
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index 4b83e42ee0..d49416c150 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -817,9 +817,6 @@ otp_5916(Config) when is_list(Config) ->
true = if is_record(#r1{},r1,3) -> true; true -> false end,
false = if is_record(#r2{},r1,3) -> true; true -> false end,
- true = if {erlang,is_record}(#r1{},r1,3) -> true; true -> false end,
- false = if {erlang,is_record}(#r2{},r1,3) -> true; true -> false end,
-
ok.">>,
[ok] = scan(C),
ok.
@@ -2282,12 +2279,6 @@ otp_5990(doc) ->
otp_5990(suite) -> [];
otp_5990(Config) when is_list(Config) ->
?line [true] =
- scan(<<"rd(foo,{bar}), {erlang,is_record}(#foo{}, foo).">>),
- ?line [3] =
- scan(<<"rd(foo,{bar}), A = #foo{}, "
- "{if {erlang,is_record}(A, foo) -> erlang; "
- "true -> not_a_module end, length}([1,2,3]).">>),
- ?line [true] =
scan(<<"rd('OrdSet', {orddata = {},ordtype = type}), "
"S = #'OrdSet'{ordtype = {}}, "
"if tuple(S#'OrdSet'.ordtype) -> true; true -> false end.">>),
diff --git a/lib/stdlib/test/stdlib.spec.vxworks b/lib/stdlib/test/stdlib.spec.vxworks
deleted file mode 100644
index ddc804b831..0000000000
--- a/lib/stdlib/test/stdlib.spec.vxworks
+++ /dev/null
@@ -1,8 +0,0 @@
-{topcase, {dir, "../stdlib_test"}}.
-{skip,{dets_SUITE,"Not runnable VxWorks/NFS"}}.
-{skip,{slave_SUITE,"VxWorks: slave nodes are not supported"}}.
-{skip,{tar_SUITE,errors,"VxWorks filesystem too primitive"}}.
-{skip,{tar_SUITE,create_long_names,"VxWorks names too short"}}.
-{skip,{epp_SUITE,"Test not adopted to VxWorks"}}.
-{skip,{select_SUITE,"Test too memory consuming for VxWorks"}}.
-{skip,{beam_lib_SUITE,error,"All sections not present in stripped beam files"}}.
diff --git a/lib/stdlib/test/sys_SUITE.erl b/lib/stdlib/test/sys_SUITE.erl
index fe039e8bcc..b2e1d12b2a 100644
--- a/lib/stdlib/test/sys_SUITE.erl
+++ b/lib/stdlib/test/sys_SUITE.erl
@@ -56,70 +56,60 @@ end_per_group(_GroupName, Config) ->
log(suite) -> [];
log(Config) when is_list(Config) ->
- ?line {ok,_Server} = start(),
- ?line ok = sys:log(?server,true),
- ?line {ok,-44} = public_call(44),
- ?line ok = sys:log(?server,false),
- ?line ok = sys:log(?server,print),
- ?line stop(),
+ {ok,_Server} = start(),
+ ok = sys:log(?server,true),
+ {ok,-44} = public_call(44),
+ ok = sys:log(?server,false),
+ ok = sys:log(?server,print),
+ stop(),
ok.
log_to_file(suite) -> [];
log_to_file(Config) when is_list(Config) ->
TempName = test_server:temp_name(?config(priv_dir,Config) ++ "sys."),
- ?line {ok,_Server} = start(),
- ?line ok = sys:log_to_file(?server,TempName),
- ?line {ok,-44} = public_call(44),
- ?line ok = sys:log_to_file(?server,false),
- ?line {ok,Fd} = file:open(TempName,[read]),
- ?line Msg1 = io:get_line(Fd,''),
- ?line Msg2 = io:get_line(Fd,''),
- ?line file:close(Fd),
- ?line lists:prefix("*DBG* sys_SUITE_server got call {req,44} from ",Msg1),
- ?line lists:prefix("*DBG* sys_SUITE_server sent {ok,-44} to ",Msg2),
- ?line stop(),
+ {ok,_Server} = start(),
+ ok = sys:log_to_file(?server,TempName),
+ {ok,-44} = public_call(44),
+ ok = sys:log_to_file(?server,false),
+ {ok,Fd} = file:open(TempName,[read]),
+ Msg1 = io:get_line(Fd,''),
+ Msg2 = io:get_line(Fd,''),
+ file:close(Fd),
+ lists:prefix("*DBG* sys_SUITE_server got call {req,44} from ",Msg1),
+ lists:prefix("*DBG* sys_SUITE_server sent {ok,-44} to ",Msg2),
+ stop(),
ok.
stats(suite) -> [];
stats(Config) when is_list(Config) ->
- ?line Self = self(),
- ?line {ok,_Server} = start(),
- ?line ok = sys:statistics(?server,true),
- ?line {ok,-44} = public_call(44),
- ?line {ok,Stats} = sys:statistics(?server,get),
- ?line lists:member({messages_in,1},Stats),
- ?line lists:member({messages_out,1},Stats),
- ?line ok = sys:statistics(?server,false),
- ?line {status,_Pid,{module,_Mod},[_PDict,running,Self,_,_]} =
+ Self = self(),
+ {ok,_Server} = start(),
+ ok = sys:statistics(?server,true),
+ {ok,-44} = public_call(44),
+ {ok,Stats} = sys:statistics(?server,get),
+ lists:member({messages_in,1},Stats),
+ lists:member({messages_out,1},Stats),
+ ok = sys:statistics(?server,false),
+ {status,_Pid,{module,_Mod},[_PDict,running,Self,_,_]} =
sys:get_status(?server),
- ?line {ok,no_statistics} = sys:statistics(?server,get),
- ?line stop(),
+ {ok,no_statistics} = sys:statistics(?server,get),
+ stop(),
ok.
trace(suite) -> [];
trace(Config) when is_list(Config) ->
- ?line {ok,_Server} = start(),
- case os:type() of
- vxworks ->
- ?line test_server:sleep(20000);
- _ ->
- ?line test_server:sleep(2000)
- end,
- ?line test_server:capture_start(),
- ?line sys:trace(?server,true),
- ?line {ok,-44} = public_call(44),
+ {ok,_Server} = start(),
+ test_server:sleep(2000),
+ test_server:capture_start(),
+ sys:trace(?server,true),
+ {ok,-44} = public_call(44),
%% ho, hum, allow for the io to reach us..
- case os:type() of
- vxworks ->
- ?line test_server:sleep(10000);
- _ ->
- ?line test_server:sleep(1000)
- end,
- ?line test_server:capture_stop(),
- ?line [Msg1,Msg2] = test_server:capture_get(),
- ?line lists:prefix("*DBG* sys_SUITE_server got call {req,44} from ",Msg1),
- ?line lists:prefix("*DBG* sys_SUITE_server sent {ok,-44} to ",Msg2),
- ?line stop(),
+ test_server:sleep(1000),
+ test_server:capture_stop(),
+ [Msg1,Msg2] = test_server:capture_get(),
+ lists:prefix("*DBG* sys_SUITE_server got call {req,44} from ",Msg1),
+ lists:prefix("*DBG* sys_SUITE_server sent {ok,-44} to ",Msg2),
+ stop(),
ok.
suspend(suite) -> [];
diff --git a/lib/stdlib/test/timer_SUITE.erl b/lib/stdlib/test/timer_SUITE.erl
index f84c72b0f8..1110891ab8 100644
--- a/lib/stdlib/test/timer_SUITE.erl
+++ b/lib/stdlib/test/timer_SUITE.erl
@@ -32,7 +32,6 @@
%% functions I guess. But I don't have time for that now.
%%
%% Expect it to run for at least 5-10 minutes!
-%% Except for VxWorks of course, where a couple of hours is more apropriate...
%% The main test case in this module is "do_big_test", which
@@ -77,12 +76,7 @@ end_per_group(_GroupName, Config) ->
do_big_test(TConfig) when is_list(TConfig) ->
Dog = ?t:timetrap(?t:minutes(20)),
Save = process_flag(trap_exit, true),
- Result = case os:type() of
- vxworks ->
- big_test(10);
- _ ->
- big_test(200)
- end,
+ Result = big_test(200),
process_flag(trap_exit, Save),
?t:timetrap_cancel(Dog),
report_result(Result).
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index 3df7495ec4..33d7a57cc3 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 1.18.2
+STDLIB_VSN = 1.19
diff --git a/lib/syntax_tools/doc/overview.edoc b/lib/syntax_tools/doc/overview.edoc
index 23eadce8fe..df02ad0b3a 100644
--- a/lib/syntax_tools/doc/overview.edoc
+++ b/lib/syntax_tools/doc/overview.edoc
@@ -1,5 +1,9 @@
+ -*- html -*-
-@author Richard Carlsson <[email protected]>
+ Syntax Tools overview page
+
+
+@author Richard Carlsson <[email protected]>
@copyright 1997-2004 Richard Carlsson
@version {@version}
@title Erlang Syntax Tools
diff --git a/lib/syntax_tools/doc/src/Makefile b/lib/syntax_tools/doc/src/Makefile
index d1c6989bd9..2502bf877a 100644
--- a/lib/syntax_tools/doc/src/Makefile
+++ b/lib/syntax_tools/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2010. All Rights Reserved.
+# Copyright Ericsson AB 2006-2012. 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/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml
index 434a3e721e..fdfa414ad2 100644
--- a/lib/syntax_tools/doc/src/notes.xml
+++ b/lib/syntax_tools/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2007</year><year>2011</year>
+ <year>2007</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,24 @@
<p>This document describes the changes made to the Syntax_Tools
application.</p>
+<section><title>Syntax_Tools 1.6.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Syntax Tools 1.6.9</p>
+ <p>
+ Minor bugfixes, spec annotations and documentation
+ cleanup. Thanks to Richard Carlsson</p>
+ <p>
+ Own Id: OTP-10208</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Syntax_Tools 1.6.8</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/syntax_tools/src/epp_dodger.erl b/lib/syntax_tools/src/epp_dodger.erl
index 9f6f7d815e..b3ced34c14 100644
--- a/lib/syntax_tools/src/epp_dodger.erl
+++ b/lib/syntax_tools/src/epp_dodger.erl
@@ -14,10 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @copyright 2001-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
diff --git a/lib/syntax_tools/src/erl_comment_scan.erl b/lib/syntax_tools/src/erl_comment_scan.erl
index 108ab3bffd..b833e1c069 100644
--- a/lib/syntax_tools/src/erl_comment_scan.erl
+++ b/lib/syntax_tools/src/erl_comment_scan.erl
@@ -16,7 +16,7 @@
%%
%% =====================================================================
%% @copyright 1997-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl
index 7caf0b3db6..f4bbf975c3 100644
--- a/lib/syntax_tools/src/erl_prettypr.erl
+++ b/lib/syntax_tools/src/erl_prettypr.erl
@@ -14,10 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @copyright 1997-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
diff --git a/lib/syntax_tools/src/erl_recomment.erl b/lib/syntax_tools/src/erl_recomment.erl
index fc7c515700..7b2f9f7adb 100644
--- a/lib/syntax_tools/src/erl_recomment.erl
+++ b/lib/syntax_tools/src/erl_recomment.erl
@@ -14,10 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @copyright 1997-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index 32fd3722d6..151f04b03b 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -14,10 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @copyright 1997-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
@@ -26,23 +24,20 @@
%% This module defines an abstract data type for representing Erlang
%% source code as syntax trees, in a way that is backwards compatible
%% with the data structures created by the Erlang standard library
-%% parser module <code>erl_parse</code> (often referred to as "parse
+%% parser module `erl_parse' (often referred to as "parse
%% trees", which is a bit of a misnomer). This means that all
-%% <code>erl_parse</code> trees are valid abstract syntax trees, but the
+%% `erl_parse' trees are valid abstract syntax trees, but the
%% reverse is not true: abstract syntax trees can in general not be used
-%% as input to functions expecting an <code>erl_parse</code> tree.
+%% as input to functions expecting an `erl_parse' tree.
%% However, as long as an abstract syntax tree represents a correct
-%% Erlang program, the function <a
-%% href="#revert-1"><code>revert/1</code></a> should be able to
-%% transform it to the corresponding <code>erl_parse</code>
+%% Erlang program, the function {@link revert/1} should be able to
+%% transform it to the corresponding `erl_parse'
%% representation.
%%
-%% A recommended starting point for the first-time user is the
-%% documentation of the <a
-%% href="#type-syntaxTree"><code>syntaxTree()</code></a> data type, and
-%% the function <a href="#type-1"><code>type/1</code></a>.
+%% A recommended starting point for the first-time user is the documentation
+%% of the {@link syntaxTree()} data type, and the function {@link type/1}.
%%
-%% <h3><b>NOTES:</b></h3>
+%% == NOTES: ==
%%
%% This module deals with the composition and decomposition of
%% <em>syntactic</em> entities (as opposed to semantic ones); its
@@ -52,36 +47,31 @@
%% in general, the user is assumed to pass type-correct arguments - if
%% this is not done, the effects are not defined.
%%
-%% With the exception of the <code>erl_parse</code> data structures,
+%% With the exception of the {@link erl_parse()} data structures,
%% the internal representations of abstract syntax trees are subject to
%% change without notice, and should not be documented outside this
%% module. Furthermore, we do not give any guarantees on how an abstract
%% syntax tree may or may not be represented, <em>with the following
%% exceptions</em>: no syntax tree is represented by a single atom, such
-%% as <code>none</code>, by a list constructor <code>[X | Y]</code>, or
-%% by the empty list <code>[]</code>. This can be relied on when writing
+%% as `none', by a list constructor `[X | Y]', or
+%% by the empty list `[]'. This can be relied on when writing
%% functions that operate on syntax trees.
-%% @type syntaxTree(). An abstract syntax tree. The
-%% <code>erl_parse</code> "parse tree" representation is a subset of the
-%% <code>syntaxTree()</code> representation.
+%% @type syntaxTree(). An abstract syntax tree. The {@link erl_parse()}
+%% "parse tree" representation is a proper subset of the `syntaxTree()'
+%% representation.
%%
%% Every abstract syntax tree node has a <em>type</em>, given by the
-%% function <a href="#type-1"><code>type/1</code></a>. Each node also
-%% has associated <em>attributes</em>; see <a
-%% href="#get_attrs-1"><code>get_attrs/1</code></a> for details. The
-%% functions <a href="#make_tree-2"><code>make_tree/2</code></a> and <a
-%% href="#subtrees-1"><code>subtrees/1</code></a> are generic
+%% function {@link type/1}. Each node also has associated
+%% <em>attributes</em>; see {@link get_attrs/1} for details. The functions
+%% {@link make_tree/2} and {@link subtrees/1} are generic
%% constructor/decomposition functions for abstract syntax trees. The
-%% functions <a href="#abstract-1"><code>abstract/1</code></a> and <a
-%% href="#concrete-1"><code>concrete/1</code></a> convert between
+%% functions {@link abstract/1} and {@link concrete/1} convert between
%% constant Erlang terms and their syntactic representations. The set of
-%% syntax tree nodes is extensible through the <a
-%% href="#tree-2"><code>tree/2</code></a> function.
+%% syntax tree nodes is extensible through the {@link tree/2} function.
%%
-%% A syntax tree can be transformed to the <code>erl_parse</code>
-%% representation with the <a href="#revert-1"><code>revert/1</code></a>
-%% function.
+%% A syntax tree can be transformed to the {@link erl_parse()}
+%% representation with the {@link revert/1} function.
-module(erl_syntax).
@@ -309,7 +299,7 @@
data/1,
is_tree/1]).
--export_type([forms/0, syntaxTree/0, syntaxTreeAttributes/0]).
+-export_type([forms/0, syntaxTree/0, syntaxTreeAttributes/0, padding/0]).
%% =====================================================================
%% IMPLEMENTATION NOTES:
@@ -390,11 +380,15 @@
-record(wrapper, {type :: atom(),
attr = #attr{} :: #attr{},
- tree :: term()}).
+ tree :: erl_parse()}).
%% =====================================================================
--type syntaxTree() :: #tree{} | #wrapper{} | tuple(). % XXX: refine
+-type syntaxTree() :: #tree{} | #wrapper{} | erl_parse().
+
+-type erl_parse() :: erl_parse:abstract_form() | erl_parse:abstract_expr().
+%% The representation built by the Erlang standard library parser
+%% `erl_parse'. This is a subset of the {@link syntaxTree()} type.
%% =====================================================================
%%
@@ -404,12 +398,11 @@
%% =====================================================================
-%% @spec type(Node::syntaxTree()) -> atom()
-%%
-%% @doc Returns the type tag of <code>Node</code>. If <code>Node</code>
+%% @doc Returns the type tag of `Node'. If `Node'
%% does not represent a syntax tree, evaluation fails with reason
-%% <code>badarg</code>. Node types currently defined by this module are:
-%% <p><center><table border="1">
+%% `badarg'. Node types currently defined by this module are:
+%%
+%% <center><table border="1">
%% <tr>
%% <td>application</td>
%% <td>arity_qualifier</td>
@@ -476,12 +469,13 @@
%% <td>variable</td>
%% <td>warning_marker</td>
%% </tr>
-%% </table></center></p>
-%% <p>The user may (for special purposes) create additional nodes
-%% with other type tags, using the <code>tree/2</code> function.</p>
+%% </table></center>
+%%
+%% The user may (for special purposes) create additional nodes
+%% with other type tags, using the {@link tree/2} function.
%%
-%% <p>Note: The primary constructor functions for a node type should
-%% always have the same name as the node type itself.</p>
+%% Note: The primary constructor functions for a node type should
+%% always have the same name as the node type itself.
%%
%% @see tree/2
%% @see application/3
@@ -606,39 +600,38 @@ type(Node) ->
%% =====================================================================
-%% @spec is_leaf(Node::syntaxTree()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if <code>Node</code> is a leaf node,
-%% otherwise <code>false</code>. The currently recognised leaf node
+%% @doc Returns `true' if `Node' is a leaf node,
+%% otherwise `false'. The currently recognised leaf node
%% types are:
-%% <p><center><table border="1">
+%%
+%% <center><table border="1">
%% <tr>
-%% <td><code>atom</code></td>
-%% <td><code>char</code></td>
-%% <td><code>comment</code></td>
-%% <td><code>eof_marker</code></td>
-%% <td><code>error_marker</code></td>
+%% <td>`atom'</td>
+%% <td>`char'</td>
+%% <td>`comment'</td>
+%% <td>`eof_marker'</td>
+%% <td>`error_marker'</td>
%% </tr><tr>
-%% <td><code>float</code></td>
-%% <td><code>integer</code></td>
-%% <td><code>nil</code></td>
-%% <td><code>operator</code></td>
-%% <td><code>string</code></td>
+%% <td>`float'</td>
+%% <td>`integer'</td>
+%% <td>`nil'</td>
+%% <td>`operator'</td>
+%% <td>`string'</td>
%% </tr><tr>
-%% <td><code>text</code></td>
-%% <td><code>underscore</code></td>
-%% <td><code>variable</code></td>
-%% <td><code>warning_marker</code></td>
+%% <td>`text'</td>
+%% <td>`underscore'</td>
+%% <td>`variable'</td>
+%% <td>`warning_marker'</td>
%% </tr>
-%% </table></center></p>
-%% <p>A node of type <code>tuple</code> is a leaf node if and only if
-%% its arity is zero.</p>
+%% </table></center>
%%
-%% <p>Note: not all literals are leaf nodes, and vice versa. E.g.,
+%% A node of type `tuple' is a leaf node if and only if its arity is zero.
+%%
+%% Note: not all literals are leaf nodes, and vice versa. E.g.,
%% tuples with nonzero arity and nonempty lists may be literals, but are
%% not leaf nodes. Variables, on the other hand, are leaf nodes but not
-%% literals.</p>
-%%
+%% literals.
+%%
%% @see type/1
%% @see is_literal/1
@@ -666,29 +659,29 @@ is_leaf(Node) ->
%% =====================================================================
-%% @spec is_form(Node::syntaxTree()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if <code>Node</code> is a syntax tree
+%% @doc Returns `true' if `Node' is a syntax tree
%% representing a so-called "source code form", otherwise
-%% <code>false</code>. Forms are the Erlang source code units which,
+%% `false'. Forms are the Erlang source code units which,
%% placed in sequence, constitute an Erlang program. Current form types
%% are:
-%% <p><center><table border="1">
+%%
+%% <center><table border="1">
%% <tr>
-%% <td><code>attribute</code></td>
-%% <td><code>comment</code></td>
-%% <td><code>error_marker</code></td>
-%% <td><code>eof_marker</code></td>
-%% <td><code>form_list</code></td>
+%% <td>`attribute'</td>
+%% <td>`comment'</td>
+%% <td>`error_marker'</td>
+%% <td>`eof_marker'</td>
+%% <td>`form_list'</td>
%% </tr><tr>
-%% <td><code>function</code></td>
-%% <td><code>rule</code></td>
-%% <td><code>warning_marker</code></td>
-%% <td><code>text</code></td>
+%% <td>`function'</td>
+%% <td>`rule'</td>
+%% <td>`warning_marker'</td>
+%% <td>`text'</td>
%% </tr>
-%% </table></center></p>
+%% </table></center>
+%%
%% @see type/1
-%% @see attribute/2
+%% @see attribute/2
%% @see comment/2
%% @see eof_marker/0
%% @see error_marker/1
@@ -715,10 +708,8 @@ is_form(Node) ->
%% =====================================================================
-%% @spec get_pos(Node::syntaxTree()) -> term()
-%%
%% @doc Returns the position information associated with
-%% <code>Node</code>. This is usually a nonnegative integer (indicating
+%% `Node'. This is usually a nonnegative integer (indicating
%% the source code line number), but may be any term. By default, all
%% new tree nodes have their associated position information set to the
%% integer zero.
@@ -750,10 +741,7 @@ get_pos(Node) ->
%% =====================================================================
-%% @spec set_pos(Node::syntaxTree(), Pos::term()) -> syntaxTree()
-%%
-%% @doc Sets the position information of <code>Node</code> to
-%% <code>Pos</code>.
+%% @doc Sets the position information of `Node' to `Pos'.
%%
%% @see get_pos/1
%% @see copy_pos/2
@@ -774,14 +762,10 @@ set_pos(Node, Pos) ->
%% =====================================================================
-%% @spec copy_pos(Source::syntaxTree(), Target::syntaxTree()) ->
-%% syntaxTree()
-%%
-%% @doc Copies the position information from <code>Source</code> to
-%% <code>Target</code>.
+%% @doc Copies the position information from `Source' to `Target'.
%%
-%% <p>This is equivalent to <code>set_pos(Target,
-%% get_pos(Source))</code>, but potentially more efficient.</p>
+%% This is equivalent to `set_pos(Target,
+%% get_pos(Source))', but potentially more efficient.
%%
%% @see get_pos/1
%% @see set_pos/2
@@ -811,24 +795,20 @@ set_com(Node, Com) ->
%% =====================================================================
-%% @spec get_precomments(syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the associated pre-comments of a node. This is a
%% possibly empty list of abstract comments, in top-down textual order.
%% When the code is formatted, pre-comments are typically displayed
%% directly above the node. For example:
-%% <pre>
-%% % Pre-comment of function
-%% foo(X) -> {bar, X}.</pre>
+%% ```% Pre-comment of function
+%% foo(X) -> {bar, X}.'''
%%
-%% <p>If possible, the comment should be moved before any preceding
+%% If possible, the comment should be moved before any preceding
%% separator characters on the same line. E.g.:
-%% <pre>
-%% foo([X | Xs]) ->
-%% % Pre-comment of 'bar(X)' node
-%% [bar(X) | foo(Xs)];
-%% ...</pre>
-%% (where the comment is moved before the "<code>[</code>").</p>
+%% ```foo([X | Xs]) ->
+%% % Pre-comment of 'bar(X)' node
+%% [bar(X) | foo(Xs)];
+%% ...'''
+%% (where the comment is moved before the "`['").
%%
%% @see comment/2
%% @see set_precomments/2
@@ -846,11 +826,8 @@ get_precomments_1(#attr{com = #com{pre = Cs}}) -> Cs.
%% =====================================================================
-%% @spec set_precomments(Node::syntaxTree(),
-%% Comments::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Sets the pre-comments of <code>Node</code> to
-%% <code>Comments</code>. <code>Comments</code> should be a possibly
+%% @doc Sets the pre-comments of `Node' to
+%% `Comments'. `Comments' should be a possibly
%% empty list of abstract comments, in top-down textual order.
%%
%% @see comment/2
@@ -880,15 +857,11 @@ set_precomments_1(#attr{com = Com} = Attr, Cs) ->
%% =====================================================================
-%% @spec add_precomments(Comments::[syntaxTree()],
-%% Node::syntaxTree()) -> syntaxTree()
+%% @doc Appends `Comments' to the pre-comments of `Node'.
%%
-%% @doc Appends <code>Comments</code> to the pre-comments of
-%% <code>Node</code>.
-%%
-%% <p>Note: This is equivalent to <code>set_precomments(Node,
-%% get_precomments(Node) ++ Comments)</code>, but potentially more
-%% efficient.</p>
+%% Note: This is equivalent to `set_precomments(Node,
+%% get_precomments(Node) ++ Comments)', but potentially more
+%% efficient.
%%
%% @see comment/2
%% @see get_precomments/1
@@ -915,24 +888,20 @@ add_precomments_1(Cs, #attr{com = Com} = Attr) ->
%% =====================================================================
-%% @spec get_postcomments(syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the associated post-comments of a node. This is a
%% possibly empty list of abstract comments, in top-down textual order.
%% When the code is formatted, post-comments are typically displayed to
%% the right of and/or below the node. For example:
-%% <pre>
-%% {foo, X, Y} % Post-comment of tuple</pre>
+%% ```{foo, X, Y} % Post-comment of tuple'''
%%
-%% <p>If possible, the comment should be moved past any following
+%% If possible, the comment should be moved past any following
%% separator characters on the same line, rather than placing the
%% separators on the following line. E.g.:
-%% <pre>
-%% foo([X | Xs], Y) ->
-%% foo(Xs, bar(X)); % Post-comment of 'bar(X)' node
-%% ...</pre>
-%% (where the comment is moved past the rightmost "<code>)</code>" and
-%% the "<code>;</code>").</p>
+%% ```foo([X | Xs], Y) ->
+%% foo(Xs, bar(X)); % Post-comment of 'bar(X)' node
+%% ...'''
+%% (where the comment is moved past the rightmost "`)'" and
+%% the "`;'").
%%
%% @see comment/2
%% @see set_postcomments/2
@@ -950,11 +919,8 @@ get_postcomments_1(#attr{com = #com{post = Cs}}) -> Cs.
%% =====================================================================
-%% @spec set_postcomments(Node::syntaxTree(),
-%% Comments::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Sets the post-comments of <code>Node</code> to
-%% <code>Comments</code>. <code>Comments</code> should be a possibly
+%% @doc Sets the post-comments of `Node' to
+%% `Comments'. `Comments' should be a possibly
%% empty list of abstract comments, in top-down textual order
%%
%% @see comment/2
@@ -984,15 +950,11 @@ set_postcomments_1(#attr{com = Com} = Attr, Cs) ->
%% =====================================================================
-%% @spec add_postcomments(Comments::[syntaxTree()],
-%% Node::syntaxTree()) -> syntaxTree()
-%%
-%% @doc Appends <code>Comments</code> to the post-comments of
-%% <code>Node</code>.
+%% @doc Appends `Comments' to the post-comments of `Node'.
%%
-%% <p>Note: This is equivalent to <code>set_postcomments(Node,
-%% get_postcomments(Node) ++ Comments)</code>, but potentially more
-%% efficient.</p>
+%% Note: This is equivalent to `set_postcomments(Node,
+%% get_postcomments(Node) ++ Comments)', but potentially more
+%% efficient.
%%
%% @see comment/2
%% @see get_postcomments/1
@@ -1019,14 +981,12 @@ add_postcomments_1(Cs, #attr{com = Com} = Attr) ->
%% =====================================================================
-%% @spec has_comments(Node::syntaxTree()) -> boolean()
+%% @doc Yields `false' if the node has no associated
+%% comments, and `true' otherwise.
%%
-%% @doc Yields <code>false</code> if the node has no associated
-%% comments, and <code>true</code> otherwise.
-%%
-%% <p>Note: This is equivalent to <code>(get_precomments(Node) == [])
-%% and (get_postcomments(Node) == [])</code>, but potentially more
-%% efficient.</p>
+%% Note: This is equivalent to `(get_precomments(Node) == [])
+%% and (get_postcomments(Node) == [])', but potentially more
+%% efficient.
%%
%% @see get_precomments/1
%% @see get_postcomments/1
@@ -1050,13 +1010,11 @@ has_comments(_) -> false.
%% =====================================================================
-%% @spec remove_comments(Node::syntaxTree()) -> syntaxTree()
-%%
-%% @doc Clears the associated comments of <code>Node</code>.
+%% @doc Clears the associated comments of `Node'.
%%
-%% <p>Note: This is equivalent to
-%% <code>set_precomments(set_postcomments(Node, []), [])</code>, but
-%% potentially more efficient.</p>
+%% Note: This is equivalent to
+%% `set_precomments(set_postcomments(Node, []), [])', but
+%% potentially more efficient.
%%
%% @see set_precomments/2
%% @see set_postcomments/2
@@ -1075,16 +1033,12 @@ remove_comments(Node) ->
%% =====================================================================
-%% @spec copy_comments(Source::syntaxTree(), Target::syntaxTree()) ->
-%% syntaxTree()
-%%
-%% @doc Copies the pre- and postcomments from <code>Source</code> to
-%% <code>Target</code>.
+%% @doc Copies the pre- and postcomments from `Source' to `Target'.
%%
-%% <p>Note: This is equivalent to
-%% <code>set_postcomments(set_precomments(Target,
-%% get_precomments(Source)), get_postcomments(Source))</code>, but
-%% potentially more efficient.</p>
+%% Note: This is equivalent to
+%% `set_postcomments(set_precomments(Target,
+%% get_precomments(Source)), get_postcomments(Source))', but
+%% potentially more efficient.
%%
%% @see comment/2
%% @see get_precomments/1
@@ -1099,16 +1053,13 @@ copy_comments(Source, Target) ->
%% =====================================================================
-%% @spec join_comments(Source::syntaxTree(), Target::syntaxTree()) ->
-%% syntaxTree()
+%% @doc Appends the comments of `Source' to the current
+%% comments of `Target'.
%%
-%% @doc Appends the comments of <code>Source</code> to the current
-%% comments of <code>Target</code>.
-%%
-%% <p>Note: This is equivalent to
-%% <code>add_postcomments(get_postcomments(Source),
-%% add_precomments(get_precomments(Source), Target))</code>, but
-%% potentially more efficient.</p>
+%% Note: This is equivalent to
+%% `add_postcomments(get_postcomments(Source),
+%% add_precomments(get_precomments(Source), Target))', but
+%% potentially more efficient.
%%
%% @see comment/2
%% @see get_precomments/1
@@ -1125,8 +1076,6 @@ join_comments(Source, Target) ->
%% =====================================================================
-%% @spec get_ann(syntaxTree()) -> [term()]
-%%
%% @doc Returns the list of user annotations associated with a syntax
%% tree node. For a newly created node, this is the empty list. The
%% annotations may be any terms.
@@ -1142,11 +1091,7 @@ get_ann(_) -> [].
%% =====================================================================
-%% @spec set_ann(Node::syntaxTree(), Annotations::[term()]) ->
-%% syntaxTree()
-%%
-%% @doc Sets the list of user annotations of <code>Node</code> to
-%% <code>Annotations</code>.
+%% @doc Sets the list of user annotations of `Node' to `Annotations'.
%%
%% @see get_ann/1
%% @see add_ann/2
@@ -1168,13 +1113,11 @@ set_ann(Node, As) ->
%% =====================================================================
-%% @spec add_ann(Annotation::term(), Node::syntaxTree()) -> syntaxTree()
-%%
-%% @doc Appends the term <code>Annotation</code> to the list of user
-%% annotations of <code>Node</code>.
+%% @doc Appends the term `Annotation' to the list of user
+%% annotations of `Node'.
%%
-%% <p>Note: this is equivalent to <code>set_ann(Node, [Annotation |
-%% get_ann(Node)])</code>, but potentially more efficient.</p>
+%% Note: this is equivalent to `set_ann(Node, [Annotation |
+%% get_ann(Node)])', but potentially more efficient.
%%
%% @see get_ann/1
%% @see set_ann/2
@@ -1195,14 +1138,10 @@ add_ann(A, Node) ->
%% =====================================================================
-%% @spec copy_ann(Source::syntaxTree(), Target::syntaxTree()) ->
-%% syntaxTree()
+%% @doc Copies the list of user annotations from `Source' to `Target'.
%%
-%% @doc Copies the list of user annotations from <code>Source</code> to
-%% <code>Target</code>.
-%%
-%% <p>Note: this is equivalent to <code>set_ann(Target,
-%% get_ann(Source))</code>, but potentially more efficient.</p>
+%% Note: this is equivalent to `set_ann(Target,
+%% get_ann(Source))', but potentially more efficient.
%%
%% @see get_ann/1
%% @see set_ann/2
@@ -1214,23 +1153,20 @@ copy_ann(Source, Target) ->
%% =====================================================================
-%% @spec get_attrs(syntaxTree()) -> syntaxTreeAttributes()
-%%
%% @doc Returns a representation of the attributes associated with a
%% syntax tree node. The attributes are all the extra information that
%% can be attached to a node. Currently, this includes position
%% information, source code comments, and user annotations. The result
%% of this function cannot be inspected directly; only attached to
-%% another node (cf. <code>set_attrs/2</code>).
+%% another node (see {@link set_attrs/2}).
%%
-%% <p>For accessing individual attributes, see <code>get_pos/1</code>,
-%% <code>get_ann/1</code>, <code>get_precomments/1</code> and
-%% <code>get_postcomments/1</code>.</p>
+%% For accessing individual attributes, see {@link get_pos/1},
+%% {@link get_ann/1}, {@link get_precomments/1} and
+%% {@link get_postcomments/1}.
%%
%% @type syntaxTreeAttributes(). This is an abstract representation of
-%% syntax tree node attributes; see the function <a
-%% href="#get_attrs-1"><code>get_attrs/1</code></a>.
-%%
+%% syntax tree node attributes; see the function {@link get_attrs/1}.
+%%
%% @see set_attrs/2
%% @see get_pos/1
%% @see get_ann/1
@@ -1247,11 +1183,7 @@ get_attrs(Node) -> #attr{pos = get_pos(Node),
%% =====================================================================
-%% @spec set_attrs(Node::syntaxTree(),
-%% Attributes::syntaxTreeAttributes()) -> syntaxTree()
-%%
-%% @doc Sets the attributes of <code>Node</code> to
-%% <code>Attributes</code>.
+%% @doc Sets the attributes of `Node' to `Attributes'.
%%
%% @see get_attrs/1
%% @see copy_attrs/2
@@ -1270,14 +1202,10 @@ set_attrs(Node, Attr) ->
%% =====================================================================
-%% @spec copy_attrs(Source::syntaxTree(), Target::syntaxTree()) ->
-%% syntaxTree()
+%% @doc Copies the attributes from `Source' to `Target'.
%%
-%% @doc Copies the attributes from <code>Source</code> to
-%% <code>Target</code>.
-%%
-%% <p>Note: this is equivalent to <code>set_attrs(Target,
-%% get_attrs(Source))</code>, but potentially more efficient.</p>
+%% Note: this is equivalent to `set_attrs(Target,
+%% get_attrs(Source))', but potentially more efficient.
%%
%% @see get_attrs/1
%% @see set_attrs/2
@@ -1289,7 +1217,6 @@ copy_attrs(S, T) ->
%% =====================================================================
-%% @spec comment(Strings) -> syntaxTree()
%% @equiv comment(none, Strings)
-spec comment([string()]) -> syntaxTree().
@@ -1299,22 +1226,19 @@ comment(Strings) ->
%% =====================================================================
-%% @spec comment(Padding, Strings::[string()]) -> syntaxTree()
-%% Padding = none | integer()
-%%
%% @doc Creates an abstract comment with the given padding and text. If
-%% <code>Strings</code> is a (possibly empty) list
+%% `Strings' is a (possibly empty) list
%% <code>["<em>Txt1</em>", ..., "<em>TxtN</em>"]</code>, the result
%% represents the source code text
%% <pre>
-%% %<em>Txt1</em>
-%% ...
-%% %<em>TxtN</em></pre>
-%% <code>Padding</code> states the number of empty character positions
+%% %<em>Txt1</em>
+%% ...
+%% %<em>TxtN</em></pre>
+%% `Padding' states the number of empty character positions
%% to the left of the comment separating it horizontally from
-%% source code on the same line (if any). If <code>Padding</code> is
-%% <code>none</code>, a default positive number is used. If
-%% <code>Padding</code> is an integer less than 1, there should be no
+%% source code on the same line (if any). If `Padding' is
+%% `none', a default positive number is used. If
+%% `Padding' is an integer less than 1, there should be no
%% separating space. Comments are in themselves regarded as source
%% program forms.
%%
@@ -1338,8 +1262,6 @@ comment(Pad, Strings) ->
%% =====================================================================
-%% @spec comment_text(syntaxTree()) -> [string()]
-%%
%% @doc Returns the lines of text of the abstract comment.
%%
%% @see comment/2
@@ -1351,11 +1273,8 @@ comment_text(Node) ->
%% =====================================================================
-%% @spec comment_padding(syntaxTree()) -> none | integer()
-%%
%% @doc Returns the amount of padding before the comment, or
-%% <code>none</code>. The latter means that a default padding may be
-%% used.
+%% `none'. The latter means that a default padding may be used.
%%
%% @see comment/2
@@ -1366,23 +1285,21 @@ comment_padding(Node) ->
%% =====================================================================
-%% @spec form_list(Forms::[syntaxTree()]) -> syntaxTree()
-%%
%% @doc Creates an abstract sequence of "source code forms". If
-%% <code>Forms</code> is <code>[F1, ..., Fn]</code>, where each
-%% <code>Fi</code> is a form (cf. <code>is_form/1</code>, the result
+%% `Forms' is `[F1, ..., Fn]', where each
+%% `Fi' is a form (see {@link is_form/1}, the result
%% represents
%% <pre>
-%% <em>F1</em>
-%% ...
-%% <em>Fn</em></pre>
-%% where the <code>Fi</code> are separated by one or more line breaks. A
-%% node of type <code>form_list</code> is itself regarded as a source
-%% code form; cf. <code>flatten_form_list/1</code>.
-%%
-%% <p>Note: this is simply a way of grouping source code forms as a
+%% <em>F1</em>
+%% ...
+%% <em>Fn</em></pre>
+%% where the `Fi' are separated by one or more line breaks. A
+%% node of type `form_list' is itself regarded as a source
+%% code form; see {@link flatten_form_list/1}.
+%%
+%% Note: this is simply a way of grouping source code forms as a
%% single syntax tree, usually in order to form an Erlang module
-%% definition.</p>
+%% definition.
%%
%% @see form_list_elements/1
%% @see is_form/1
@@ -1401,9 +1318,7 @@ form_list(Forms) ->
%% =====================================================================
-%% @spec form_list_elements(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of subnodes of a <code>form_list</code> node.
+%% @doc Returns the list of subnodes of a `form_list' node.
%%
%% @see form_list/1
@@ -1414,10 +1329,8 @@ form_list_elements(Node) ->
%% =====================================================================
-%% @spec flatten_form_list(Node::syntaxTree()) -> syntaxTree()
-%%
-%% @doc Flattens sublists of a <code>form_list</code> node. Returns
-%% <code>Node</code> with all subtrees of type <code>form_list</code>
+%% @doc Flattens sublists of a `form_list' node. Returns
+%% `Node' with all subtrees of type `form_list'
%% recursively expanded, yielding a single "flat" abstract form
%% sequence.
%%
@@ -1443,10 +1356,8 @@ flatten_form_list_1([], As) ->
%% =====================================================================
-%% @spec text(String::string()) -> syntaxTree()
-%%
%% @doc Creates an abstract piece of source code text. The result
-%% represents exactly the sequence of characters in <code>String</code>.
+%% represents exactly the sequence of characters in `String'.
%% This is useful in cases when one wants full control of the resulting
%% output, e.g., for the appearance of floating-point numbers or macro
%% definitions.
@@ -1463,10 +1374,7 @@ text(String) ->
%% =====================================================================
-%% @spec text_string(syntaxTree()) -> string()
-%%
-%% @doc Returns the character sequence represented by a
-%% <code>text</code> node.
+%% @doc Returns the character sequence represented by a `text' node.
%%
%% @see text/1
@@ -1477,18 +1385,15 @@ text_string(Node) ->
%% =====================================================================
-%% @spec variable(Name) -> syntaxTree()
-%% Name = atom() | string()
-%%
%% @doc Creates an abstract variable with the given name.
-%% <code>Name</code> may be any atom or string that represents a
+%% `Name' may be any atom or string that represents a
%% lexically valid variable name, but <em>not</em> a single underscore
-%% character; cf. <code>underscore/0</code>.
+%% character; see {@link underscore/0}.
%%
-%% <p>Note: no checking is done whether the character sequence
+%% Note: no checking is done whether the character sequence
%% represents a proper variable name, i.e., whether or not its first
%% character is an uppercase Erlang character, or whether it does not
-%% contain control characters, whitespace, etc.</p>
+%% contain control characters, whitespace, etc.
%%
%% @see variable_name/1
%% @see variable_literal/1
@@ -1517,9 +1422,7 @@ revert_variable(Node) ->
%% =====================================================================
-%% @spec variable_name(syntaxTree()) -> atom()
-%%
-%% @doc Returns the name of a <code>variable</code> node as an atom.
+%% @doc Returns the name of a `variable' node as an atom.
%%
%% @see variable/1
@@ -1535,9 +1438,7 @@ variable_name(Node) ->
%% =====================================================================
-%% @spec variable_literal(syntaxTree()) -> string()
-%%
-%% @doc Returns the name of a <code>variable</code> node as a string.
+%% @doc Returns the name of a `variable' node as a string.
%%
%% @see variable/1
@@ -1553,9 +1454,7 @@ variable_literal(Node) ->
%% =====================================================================
-%% @spec underscore() -> syntaxTree()
-%%
-%% @doc Creates an abstract universal pattern ("<code>_</code>"). The
+%% @doc Creates an abstract universal pattern ("`_'"). The
%% lexical representation is a single underscore character. Note that
%% this is <em>not</em> a variable, lexically speaking.
%%
@@ -1579,10 +1478,8 @@ revert_underscore(Node) ->
%% =====================================================================
-%% @spec integer(Value::integer()) -> syntaxTree()
-%%
%% @doc Creates an abstract integer literal. The lexical representation
-%% is the canonical decimal numeral of <code>Value</code>.
+%% is the canonical decimal numeral of `Value'.
%%
%% @see integer_value/1
%% @see integer_literal/1
@@ -1608,11 +1505,8 @@ revert_integer(Node) ->
%% =====================================================================
-%% @spec is_integer(Node::syntaxTree(), Value::integer()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if <code>Node</code> has type
-%% <code>integer</code> and represents <code>Value</code>, otherwise
-%% <code>false</code>.
+%% @doc Returns `true' if `Node' has type
+%% `integer' and represents `Value', otherwise `false'.
%%
%% @see integer/1
@@ -1630,9 +1524,7 @@ is_integer(Node, Value) ->
%% =====================================================================
-%% @spec integer_value(syntaxTree()) -> integer()
-%%
-%% @doc Returns the value represented by an <code>integer</code> node.
+%% @doc Returns the value represented by an `integer' node.
%%
%% @see integer/1
@@ -1648,10 +1540,7 @@ integer_value(Node) ->
%% =====================================================================
-%% @spec integer_literal(syntaxTree()) -> string()
-%%
-%% @doc Returns the numeral string represented by an
-%% <code>integer</code> node.
+%% @doc Returns the numeral string represented by an `integer' node.
%%
%% @see integer/1
@@ -1662,11 +1551,8 @@ integer_literal(Node) ->
%% =====================================================================
-%% @spec float(Value::float()) -> syntaxTree()
-%%
%% @doc Creates an abstract floating-point literal. The lexical
-%% representation is the decimal floating-point numeral of
-%% <code>Value</code>.
+%% representation is the decimal floating-point numeral of `Value'.
%%
%% @see float_value/1
%% @see float_literal/1
@@ -1701,9 +1587,7 @@ revert_float(Node) ->
%% =====================================================================
-%% @spec float_value(syntaxTree()) -> float()
-%%
-%% @doc Returns the value represented by a <code>float</code> node. Note
+%% @doc Returns the value represented by a `float' node. Note
%% that floating-point values should usually not be compared for
%% equality.
%%
@@ -1721,10 +1605,7 @@ float_value(Node) ->
%% =====================================================================
-%% @spec float_literal(syntaxTree()) -> string()
-%%
-%% @doc Returns the numeral string represented by a <code>float</code>
-%% node.
+%% @doc Returns the numeral string represented by a `float' node.
%%
%% @see float/1
@@ -1735,17 +1616,15 @@ float_literal(Node) ->
%% =====================================================================
-%% @spec char(Value::char()) -> syntaxTree()
-%%
%% @doc Creates an abstract character literal. The result represents
-%% "<code>$<em>Name</em></code>", where <code>Name</code> corresponds to
-%% <code>Value</code>.
+%% "<code>$<em>Name</em></code>", where `Name' corresponds to
+%% `Value'.
%%
-%% <p>Note: the literal corresponding to a particular character value is
-%% not uniquely defined. E.g., the character "<code>a</code>" can be
-%% written both as "<code>$a</code>" and "<code>$\141</code>", and a Tab
-%% character can be written as "<code>$\11</code>", "<code>$\011</code>"
-%% or "<code>$\t</code>".</p>
+%% Note: the literal corresponding to a particular character value is
+%% not uniquely defined. E.g., the character "`a'" can be
+%% written both as "`$a'" and "`$\141'", and a Tab
+%% character can be written as "`$\11'", "`$\011'"
+%% or "`$\t'".
%%
%% @see char_value/1
%% @see char_literal/1
@@ -1771,11 +1650,8 @@ revert_char(Node) ->
%% =====================================================================
-%% @spec is_char(Node::syntaxTree(), Value::char()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if <code>Node</code> has type
-%% <code>char</code> and represents <code>Value</code>, otherwise
-%% <code>false</code>.
+%% @doc Returns `true' if `Node' has type
+%% `char' and represents `Value', otherwise `false'.
%%
%% @see char/1
@@ -1793,9 +1669,7 @@ is_char(Node, Value) ->
%% =====================================================================
-%% @spec char_value(syntaxTree()) -> char()
-%%
-%% @doc Returns the value represented by a <code>char</code> node.
+%% @doc Returns the value represented by a `char' node.
%%
%% @see char/1
@@ -1811,10 +1685,8 @@ char_value(Node) ->
%% =====================================================================
-%% @spec char_literal(syntaxTree()) -> string()
-%%
-%% @doc Returns the literal string represented by a <code>char</code>
-%% node. This includes the leading "<code>$</code>" character.
+%% @doc Returns the literal string represented by a `char'
+%% node. This includes the leading "`$'" character.
%%
%% @see char/1
@@ -1825,16 +1697,14 @@ char_literal(Node) ->
%% =====================================================================
-%% @spec string(Value::string()) -> syntaxTree()
-%%
%% @doc Creates an abstract string literal. The result represents
%% <code>"<em>Text</em>"</code> (including the surrounding
-%% double-quotes), where <code>Text</code> corresponds to the sequence
-%% of characters in <code>Value</code>, but not representing a
-%% <em>specific</em> string literal. E.g., the result of
-%% <code>string("x\ny")</code> represents any and all of
-%% <code>"x\ny"</code>, <code>"x\12y"</code>, <code>"x\012y"</code> and
-%% <code>"x\^Jy"</code>; cf. <code>char/1</code>.
+%% double-quotes), where `Text' corresponds to the sequence
+%% of characters in `Value', but not representing a
+%% <em>specific</em> string literal.
+%%
+%% For example, the result of `string("x\ny")' represents any and all of
+%% `"x\ny"', `"x\12y"', `"x\012y"' and `"x\^Jy"'; see {@link char/1}.
%%
%% @see string_value/1
%% @see string_literal/1
@@ -1861,11 +1731,8 @@ revert_string(Node) ->
%% =====================================================================
-%% @spec is_string(Node::syntaxTree(), Value::string()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if <code>Node</code> has type
-%% <code>string</code> and represents <code>Value</code>, otherwise
-%% <code>false</code>.
+%% @doc Returns `true' if `Node' has type
+%% `string' and represents `Value', otherwise `false'.
%%
%% @see string/1
@@ -1883,9 +1750,7 @@ is_string(Node, Value) ->
%% =====================================================================
-%% @spec string_value(syntaxTree()) -> string()
-%%
-%% @doc Returns the value represented by a <code>string</code> node.
+%% @doc Returns the value represented by a `string' node.
%%
%% @see string/1
@@ -1901,9 +1766,7 @@ string_value(Node) ->
%% =====================================================================
-%% @spec string_literal(syntaxTree()) -> string()
-%%
-%% @doc Returns the literal string represented by a <code>string</code>
+%% @doc Returns the literal string represented by a `string'
%% node. This includes surrounding double-quote characters.
%%
%% @see string/1
@@ -1915,11 +1778,8 @@ string_literal(Node) ->
%% =====================================================================
-%% @spec atom(Name) -> syntaxTree()
-%% Name = atom() | string()
-%%
%% @doc Creates an abstract atom literal. The print name of the atom is
-%% the character sequence represented by <code>Name</code>.
+%% the character sequence represented by `Name'.
%%
%% @see atom_value/1
%% @see atom_name/1
@@ -1948,11 +1808,8 @@ revert_atom(Node) ->
%% =====================================================================
-%% @spec is_atom(Node::syntaxTree(), Value::atom()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if <code>Node</code> has type
-%% <code>atom</code> and represents <code>Value</code>, otherwise
-%% <code>false</code>.
+%% @doc Returns `true' if `Node' has type
+%% `atom' and represents `Value', otherwise `false'.
%%
%% @see atom/1
@@ -1970,9 +1827,7 @@ is_atom(Node, Value) ->
%% =====================================================================
-%% @spec atom_value(syntaxTree()) -> atom()
-%%
-%% @doc Returns the value represented by an <code>atom</code> node.
+%% @doc Returns the value represented by an `atom' node.
%%
%% @see atom/1
@@ -1988,9 +1843,7 @@ atom_value(Node) ->
%% =====================================================================
-%% @spec atom_name(syntaxTree()) -> string()
-%%
-%% @doc Returns the printname of an <code>atom</code> node.
+%% @doc Returns the printname of an `atom' node.
%%
%% @see atom/1
@@ -2001,15 +1854,12 @@ atom_name(Node) ->
%% =====================================================================
-%% @spec atom_literal(syntaxTree()) -> string()
-%%
-%% @doc Returns the literal string represented by an <code>atom</code>
+%% @doc Returns the literal string represented by an `atom'
%% node. This includes surrounding single-quote characters if necessary.
%%
-%% <p>Note that e.g. the result of <code>atom("x\ny")</code> represents
-%% any and all of <code>'x\ny'</code>, <code>'x\12y'</code>,
-%% <code>'x\012y'</code> and <code>'x\^Jy\'</code>; cf.
-%% <code>string/1</code>.</p>
+%% Note that e.g. the result of `atom("x\ny")' represents
+%% any and all of `'x\ny'', `'x\12y'',
+%% `'x\012y'' and `'x\^Jy\''; see {@link string/1}.
%%
%% @see atom/1
%% @see string/1
@@ -2021,14 +1871,12 @@ atom_literal(Node) ->
%% =====================================================================
-%% @spec tuple(Elements::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Creates an abstract tuple. If <code>Elements</code> is
-%% <code>[X1, ..., Xn]</code>, the result represents
+%% @doc Creates an abstract tuple. If `Elements' is
+%% `[X1, ..., Xn]', the result represents
%% "<code>{<em>X1</em>, ..., <em>Xn</em>}</code>".
%%
-%% <p>Note: The Erlang language has distinct 1-tuples, i.e.,
-%% <code>{X}</code> is always distinct from <code>X</code> itself.</p>
+%% Note: The Erlang language has distinct 1-tuples, i.e.,
+%% `{X}' is always distinct from `X' itself.
%%
%% @see tuple_elements/1
%% @see tuple_size/1
@@ -2055,10 +1903,7 @@ revert_tuple(Node) ->
%% =====================================================================
-%% @spec tuple_elements(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of element subtrees of a <code>tuple</code>
-%% node.
+%% @doc Returns the list of element subtrees of a `tuple' node.
%%
%% @see tuple/1
@@ -2074,13 +1919,11 @@ tuple_elements(Node) ->
%% =====================================================================
-%% @spec tuple_size(syntaxTree()) -> integer()
-%%
-%% @doc Returns the number of elements of a <code>tuple</code> node.
+%% @doc Returns the number of elements of a `tuple' node.
%%
-%% <p>Note: this is equivalent to
-%% <code>length(tuple_elements(Node))</code>, but potentially more
-%% efficient.</p>
+%% Note: this is equivalent to
+%% `length(tuple_elements(Node))', but potentially more
+%% efficient.
%%
%% @see tuple/1
%% @see tuple_elements/1
@@ -2092,7 +1935,6 @@ tuple_size(Node) ->
%% =====================================================================
-%% @spec list(List) -> syntaxTree()
%% @equiv list(List, none)
-spec list([syntaxTree()]) -> syntaxTree().
@@ -2102,35 +1944,31 @@ list(List) ->
%% =====================================================================
-%% @spec list(List, Tail) -> syntaxTree()
-%% List = [syntaxTree()]
-%% Tail = none | syntaxTree()
-%%
%% @doc Constructs an abstract list skeleton. The result has type
-%% <code>list</code> or <code>nil</code>. If <code>List</code> is a
-%% nonempty list <code>[E1, ..., En]</code>, the result has type
-%% <code>list</code> and represents either "<code>[<em>E1</em>, ...,
-%% <em>En</em>]</code>", if <code>Tail</code> is <code>none</code>, or
+%% `list' or `nil'. If `List' is a
+%% nonempty list `[E1, ..., En]', the result has type
+%% `list' and represents either "<code>[<em>E1</em>, ...,
+%% <em>En</em>]</code>", if `Tail' is `none', or
%% otherwise "<code>[<em>E1</em>, ..., <em>En</em> |
-%% <em>Tail</em>]</code>". If <code>List</code> is the empty list,
-%% <code>Tail</code> <em>must</em> be <code>none</code>, and in that
-%% case the result has type <code>nil</code> and represents
-%% "<code>[]</code>" (cf. <code>nil/0</code>).
+%% <em>Tail</em>]</code>". If `List' is the empty list,
+%% `Tail' <em>must</em> be `none', and in that
+%% case the result has type `nil' and represents
+%% "`[]'" (see {@link nil/0}).
%%
-%% <p>The difference between lists as semantic objects (built up of
+%% The difference between lists as semantic objects (built up of
%% individual "cons" and "nil" terms) and the various syntactic forms
%% for denoting lists may be bewildering at first. This module provides
%% functions both for exact control of the syntactic representation as
%% well as for the simple composition and deconstruction in terms of
-%% cons and head/tail operations.</p>
+%% cons and head/tail operations.
%%
-%% <p>Note: in <code>list(Elements, none)</code>, the "nil" list
-%% terminator is implicit and has no associated information (cf.
-%% <code>get_attrs/1</code>), while in the seemingly equivalent
-%% <code>list(Elements, Tail)</code> when <code>Tail</code> has type
-%% <code>nil</code>, the list terminator subtree <code>Tail</code> may
+%% Note: in `list(Elements, none)', the "nil" list
+%% terminator is implicit and has no associated information (see
+%% {@link get_attrs/1}), while in the seemingly equivalent
+%% `list(Elements, Tail)' when `Tail' has type
+%% `nil', the list terminator subtree `Tail' may
%% have attached attributes such as position, comments, and annotations,
-%% which will be preserved in the result.</p>
+%% which will be preserved in the result.
%%
%% @see nil/0
%% @see list/1
@@ -2187,10 +2025,8 @@ revert_list(Node) ->
S, P).
%% =====================================================================
-%% @spec nil() -> syntaxTree()
-%%
%% @doc Creates an abstract empty list. The result represents
-%% "<code>[]</code>". The empty list is traditionally called "nil".
+%% "`[]'". The empty list is traditionally called "nil".
%%
%% @see list/2
%% @see is_list_skeleton/1
@@ -2213,13 +2049,11 @@ revert_nil(Node) ->
%% =====================================================================
-%% @spec list_prefix(Node::syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the prefix element subtrees of a <code>list</code> node.
-%% If <code>Node</code> represents "<code>[<em>E1</em>, ...,
+%% @doc Returns the prefix element subtrees of a `list' node.
+%% If `Node' represents "<code>[<em>E1</em>, ...,
%% <em>En</em>]</code>" or "<code>[<em>E1</em>, ..., <em>En</em> |
-%% <em>Tail</em>]</code>", the returned value is <code>[E1, ...,
-%% En]</code>.
+%% <em>Tail</em>]</code>", the returned value is `[E1, ...,
+%% En]'.
%%
%% @see list/2
@@ -2227,28 +2061,31 @@ revert_nil(Node) ->
list_prefix(Node) ->
case unwrap(Node) of
- {cons, _, Head, _} ->
- [Head];
+ {cons, _, Head, Tail} ->
+ [Head | cons_prefix(Tail)];
Node1 ->
(data(Node1))#list.prefix
end.
+%% collects sequences of conses; cf. cons_suffix/1 below
+cons_prefix({cons, _, Head, Tail}) ->
+ [Head | cons_prefix(Tail)];
+cons_prefix(_) ->
+ [].
+
%% =====================================================================
-%% @spec list_suffix(Node::syntaxTree()) -> none | syntaxTree()
-%%
-%% @doc Returns the suffix subtree of a <code>list</code> node, if one
-%% exists. If <code>Node</code> represents "<code>[<em>E1</em>, ...,
+%% @doc Returns the suffix subtree of a `list' node, if one
+%% exists. If `Node' represents "<code>[<em>E1</em>, ...,
%% <em>En</em> | <em>Tail</em>]</code>", the returned value is
-%% <code>Tail</code>, otherwise, i.e., if <code>Node</code> represents
-%% "<code>[<em>E1</em>, ..., <em>En</em>]</code>", <code>none</code> is
+%% `Tail', otherwise, i.e., if `Node' represents
+%% "<code>[<em>E1</em>, ..., <em>En</em>]</code>", `none' is
%% returned.
%%
-%% <p>Note that even if this function returns some <code>Tail</code>
-%% that is not <code>none</code>, the type of <code>Tail</code> can be
-%% <code>nil</code>, if the tail has been given explicitly, and the list
-%% skeleton has not been compacted (cf.
-%% <code>compact_list/1</code>).</p>
+%% Note that even if this function returns some `Tail'
+%% that is not `none', the type of `Tail' can be
+%% `nil', if the tail has been given explicitly, and the list
+%% skeleton has not been compacted (see {@link compact_list/1}).
%%
%% @see list/2
%% @see nil/0
@@ -2259,34 +2096,36 @@ list_prefix(Node) ->
list_suffix(Node) ->
case unwrap(Node) of
{cons, _, _, Tail} ->
- %% If there could be comments/annotations on the tail node,
- %% we should not return `none' even if it has type `nil'.
- case Tail of
+ case cons_suffix(Tail) of
{nil, _} ->
- none; % no interesting information is lost
- _ ->
- Tail
+ none;
+ Tail1 ->
+ Tail1
end;
Node1 ->
(data(Node1))#list.suffix
end.
+%% skips sequences of conses; cf. cons_prefix/1 above
+cons_suffix({cons, _, _, Tail}) ->
+ cons_suffix(Tail);
+cons_suffix(Tail) ->
+ Tail.
+
%% =====================================================================
-%% @spec cons(Head::syntaxTree(), Tail::syntaxTree()) -> syntaxTree()
-%%
%% @doc "Optimising" list skeleton cons operation. Creates an abstract
-%% list skeleton whose first element is <code>Head</code> and whose tail
-%% corresponds to <code>Tail</code>. This is similar to
-%% <code>list([Head], Tail)</code>, except that <code>Tail</code> may
-%% not be <code>none</code>, and that the result does not necessarily
+%% list skeleton whose first element is `Head' and whose tail
+%% corresponds to `Tail'. This is similar to
+%% `list([Head], Tail)', except that `Tail' may
+%% not be `none', and that the result does not necessarily
%% represent exactly "<code>[<em>Head</em> | <em>Tail</em>]</code>", but
-%% may depend on the <code>Tail</code> subtree. E.g., if
-%% <code>Tail</code> represents <code>[X, Y]</code>, the result may
+%% may depend on the `Tail' subtree. E.g., if
+%% `Tail' represents `[X, Y]', the result may
%% represent "<code>[<em>Head</em>, X, Y]</code>", rather than
%% "<code>[<em>Head</em> | [X, Y]]</code>". Annotations on
-%% <code>Tail</code> itself may be lost if <code>Tail</code> represents
-%% a list skeleton, but comments on <code>Tail</code> are propagated to
+%% `Tail' itself may be lost if `Tail' represents
+%% a list skeleton, but comments on `Tail' are propagated to
%% the result.
%%
%% @see list/2
@@ -2308,10 +2147,8 @@ cons(Head, Tail) ->
%% =====================================================================
-%% @spec list_head(Node::syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the head element subtree of a <code>list</code> node. If
-%% <code>Node</code> represents "<code>[<em>Head</em> ...]</code>", the
+%% @doc Returns the head element subtree of a `list' node. If
+%% `Node' represents "<code>[<em>Head</em> ...]</code>", the
%% result will represent "<code><em>Head</em></code>".
%%
%% @see list/2
@@ -2325,15 +2162,13 @@ list_head(Node) ->
%% =====================================================================
-%% @spec list_tail(Node::syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the tail of a <code>list</code> node. If
-%% <code>Node</code> represents a single-element list
+%% @doc Returns the tail of a `list' node. If
+%% `Node' represents a single-element list
%% "<code>[<em>E</em>]</code>", then the result has type
-%% <code>nil</code>, representing "<code>[]</code>". If
-%% <code>Node</code> represents "<code>[<em>E1</em>, <em>E2</em>
+%% `nil', representing "`[]'". If
+%% `Node' represents "<code>[<em>E1</em>, <em>E2</em>
%% ...]</code>", the result will represent "<code>[<em>E2</em>
-%% ...]</code>", and if <code>Node</code> represents
+%% ...]</code>", and if `Node' represents
%% "<code>[<em>Head</em> | <em>Tail</em>]</code>", the result will
%% represent "<code><em>Tail</em></code>".
%%
@@ -2358,10 +2193,8 @@ list_tail(Node) ->
%% =====================================================================
-%% @spec is_list_skeleton(syntaxTree()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if <code>Node</code> has type
-%% <code>list</code> or <code>nil</code>, otherwise <code>false</code>.
+%% @doc Returns `true' if `Node' has type
+%% `list' or `nil', otherwise `false'.
%%
%% @see list/2
%% @see nil/0
@@ -2377,24 +2210,22 @@ is_list_skeleton(Node) ->
%% =====================================================================
-%% @spec is_proper_list(Node::syntaxTree()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if <code>Node</code> represents a
-%% proper list, and <code>false</code> otherwise. A proper list is a
-%% list skeleton either on the form "<code>[]</code>" or
+%% @doc Returns `true' if `Node' represents a
+%% proper list, and `false' otherwise. A proper list is a
+%% list skeleton either on the form "`[]'" or
%% "<code>[<em>E1</em>, ..., <em>En</em>]</code>", or "<code>[... |
-%% <em>Tail</em>]</code>" where recursively <code>Tail</code> also
+%% <em>Tail</em>]</code>" where recursively `Tail' also
%% represents a proper list.
%%
-%% <p>Note: Since <code>Node</code> is a syntax tree, the actual
+%% Note: Since `Node' is a syntax tree, the actual
%% run-time values corresponding to its subtrees may often be partially
-%% or completely unknown. Thus, if <code>Node</code> represents e.g.
-%% "<code>[... | Ns]</code>" (where <code>Ns</code> is a variable), then
-%% the function will return <code>false</code>, because it is not known
-%% whether <code>Ns</code> will be bound to a list at run-time. If
-%% <code>Node</code> instead represents e.g. "<code>[1, 2, 3]</code>" or
-%% "<code>[A | []]</code>", then the function will return
-%% <code>true</code>.</p>
+%% or completely unknown. Thus, if `Node' represents e.g.
+%% "`[... | Ns]'" (where `Ns' is a variable), then
+%% the function will return `false', because it is not known
+%% whether `Ns' will be bound to a list at run-time. If
+%% `Node' instead represents e.g. "`[1, 2, 3]'" or
+%% "`[A | []]'", then the function will return
+%% `true'.
%%
%% @see list/2
@@ -2417,14 +2248,11 @@ is_proper_list(Node) ->
%% =====================================================================
-%% @spec list_elements(Node::syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of element subtrees of a list skeleton.
-%% <code>Node</code> must represent a proper list. E.g., if
-%% <code>Node</code> represents "<code>[<em>X1</em>, <em>X2</em> |
+%% `Node' must represent a proper list. E.g., if
+%% `Node' represents "<code>[<em>X1</em>, <em>X2</em> |
%% [<em>X3</em>, <em>X4</em> | []]</code>", then
-%% <code>list_elements(Node)</code> yields the list <code>[X1, X2, X3,
-%% X4]</code>.
+%% `list_elements(Node)' yields the list `[X1, X2, X3, X4]'.
%%
%% @see list/2
%% @see is_proper_list/1
@@ -2450,17 +2278,15 @@ list_elements(Node, As) ->
%% =====================================================================
-%% @spec list_length(Node::syntaxTree()) -> integer()
-%%
%% @doc Returns the number of element subtrees of a list skeleton.
-%% <code>Node</code> must represent a proper list. E.g., if
-%% <code>Node</code> represents "<code>[X1 | [X2, X3 | [X4, X5,
-%% X6]]]</code>", then <code>list_length(Node)</code> returns the
+%% `Node' must represent a proper list. E.g., if
+%% `Node' represents "`[X1 | [X2, X3 | [X4, X5,
+%% X6]]]'", then `list_length(Node)' returns the
%% integer 6.
%%
-%% <p>Note: this is equivalent to
-%% <code>length(list_elements(Node))</code>, but potentially more
-%% efficient.</p>
+%% Note: this is equivalent to
+%% `length(list_elements(Node))', but potentially more
+%% efficient.
%%
%% @see list/2
%% @see is_proper_list/1
@@ -2487,18 +2313,16 @@ list_length(Node, A) ->
%% =====================================================================
-%% @spec normalize_list(Node::syntaxTree()) -> syntaxTree()
-%%
%% @doc Expands an abstract list skeleton to its most explicit form. If
-%% <code>Node</code> represents "<code>[<em>E1</em>, ..., <em>En</em> |
+%% `Node' represents "<code>[<em>E1</em>, ..., <em>En</em> |
%% <em>Tail</em>]</code>", the result represents "<code>[<em>E1</em> |
%% ... [<em>En</em> | <em>Tail1</em>] ... ]</code>", where
-%% <code>Tail1</code> is the result of
-%% <code>normalize_list(Tail)</code>. If <code>Node</code> represents
+%% `Tail1' is the result of
+%% `normalize_list(Tail)'. If `Node' represents
%% "<code>[<em>E1</em>, ..., <em>En</em>]</code>", the result simply
%% represents "<code>[<em>E1</em> | ... [<em>En</em> | []] ...
-%% ]</code>". If <code>Node</code> does not represent a list skeleton,
-%% <code>Node</code> itself is returned.
+%% ]</code>". If `Node' does not represent a list skeleton,
+%% `Node' itself is returned.
%%
%% @see list/2
%% @see compact_list/1
@@ -2528,16 +2352,14 @@ normalize_list_1(Es, Tail) ->
%% =====================================================================
-%% @spec compact_list(Node::syntaxTree()) -> syntaxTree()
-%%
%% @doc Yields the most compact form for an abstract list skeleton. The
%% result either represents "<code>[<em>E1</em>, ..., <em>En</em> |
-%% <em>Tail</em>]</code>", where <code>Tail</code> is not a list
+%% <em>Tail</em>]</code>", where `Tail' is not a list
%% skeleton, or otherwise simply "<code>[<em>E1</em>, ...,
-%% <em>En</em>]</code>". Annotations on subtrees of <code>Node</code>
+%% <em>En</em>]</code>". Annotations on subtrees of `Node'
%% that represent list skeletons may be lost, but comments will be
-%% propagated to the result. Returns <code>Node</code> itself if
-%% <code>Node</code> does not represent a list skeleton.
+%% propagated to the result. Returns `Node' itself if
+%% `Node' does not represent a list skeleton.
%%
%% @see list/2
%% @see normalize_list/1
@@ -2575,10 +2397,8 @@ compact_list(Node) ->
%% =====================================================================
-%% @spec binary(Fields::[syntaxTree()]) -> syntaxTree()
-%%
%% @doc Creates an abstract binary-object template. If
-%% <code>Fields</code> is <code>[F1, ..., Fn]</code>, the result
+%% `Fields' is `[F1, ..., Fn]', the result
%% represents "<code>&lt;&lt;<em>F1</em>, ...,
%% <em>Fn</em>&gt;&gt;</code>".
%%
@@ -2611,10 +2431,7 @@ revert_binary(Node) ->
%% =====================================================================
-%% @spec binary_fields(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of field subtrees of a <code>binary</code>
-%% node.
+%% @doc Returns the list of field subtrees of a `binary' node.
%%
%% @see binary/1
%% @see binary_field/2
@@ -2631,7 +2448,6 @@ binary_fields(Node) ->
%% =====================================================================
-%% @spec binary_field(Body) -> syntaxTree()
%% @equiv binary_field(Body, [])
-spec binary_field(syntaxTree()) -> syntaxTree().
@@ -2641,15 +2457,11 @@ binary_field(Body) ->
%% =====================================================================
-%% @spec binary_field(Body::syntaxTree(), Size,
-%% Types::[syntaxTree()]) -> syntaxTree()
-%% Size = none | syntaxTree()
-%%
%% @doc Creates an abstract binary template field.
-%% If <code>Size</code> is <code>none</code>, this is equivalent to
-%% "<code>binary_field(Body, Types)</code>", otherwise it is
-%% equivalent to "<code>binary_field(size_qualifier(Body, Size),
-%% Types)</code>".
+%% If `Size' is `none', this is equivalent to
+%% "`binary_field(Body, Types)'", otherwise it is
+%% equivalent to "`binary_field(size_qualifier(Body, Size),
+%% Types)'".
%%
%% (This is a utility function.)
%%
@@ -2667,13 +2479,10 @@ binary_field(Body, Size, Types) ->
%% =====================================================================
-%% @spec binary_field(Body::syntaxTree(), Types::[syntaxTree()]) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract binary template field. If
-%% <code>Types</code> is the empty list, the result simply represents
-%% "<code><em>Body</em></code>", otherwise, if <code>Types</code> is
-%% <code>[T1, ..., Tn]</code>, the result represents
+%% `Types' is the empty list, the result simply represents
+%% "<code><em>Body</em></code>", otherwise, if `Types' is
+%% `[T1, ..., Tn]', the result represents
%% "<code><em>Body</em>/<em>T1</em>-...-<em>Tn</em></code>".
%%
%% @see binary/1
@@ -2727,9 +2536,7 @@ revert_binary_field(Node) ->
%% =====================================================================
-%% @spec binary_field_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>binary_field</code>.
+%% @doc Returns the body subtree of a `binary_field'.
%%
%% @see binary_field/2
@@ -2749,12 +2556,10 @@ binary_field_body(Node) ->
%% =====================================================================
-%% @spec binary_field_types(Node::syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of type-specifier subtrees of a
-%% <code>binary_field</code> node. If <code>Node</code> represents
+%% `binary_field' node. If `Node' represents
%% "<code>.../<em>T1</em>, ..., <em>Tn</em></code>", the result is
-%% <code>[T1, ..., Tn]</code>, otherwise the result is the empty list.
+%% `[T1, ..., Tn]', otherwise the result is the empty list.
%%
%% @see binary_field/2
@@ -2774,14 +2579,12 @@ binary_field_types(Node) ->
%% =====================================================================
-%% @spec binary_field_size(Node::syntaxTree()) -> none | syntaxTree()
-%%
%% @doc Returns the size specifier subtree of a
-%% <code>binary_field</code> node, if any. If <code>Node</code>
+%% `binary_field' node, if any. If `Node'
%% represents "<code><em>Body</em>:<em>Size</em></code>" or
%% "<code><em>Body</em>:<em>Size</em>/<em>T1</em>, ...,
-%% <em>Tn</em></code>", the result is <code>Size</code>, otherwise
-%% <code>none</code> is returned.
+%% <em>Tn</em></code>", the result is `Size', otherwise
+%% `none' is returned.
%%
%% (This is a utility function.)
%%
@@ -2810,9 +2613,6 @@ binary_field_size(Node) ->
%% =====================================================================
-%% @spec size_qualifier(Body::syntaxTree(), Size::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract size qualifier. The result represents
%% "<code><em>Body</em>:<em>Size</em></code>".
%%
@@ -2834,10 +2634,7 @@ size_qualifier(Body, Size) ->
%% =====================================================================
-%% @spec size_qualifier_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>size_qualifier</code>
-%% node.
+%% @doc Returns the body subtree of a `size_qualifier' node.
%%
%% @see size_qualifier/2
@@ -2848,10 +2645,8 @@ size_qualifier_body(Node) ->
%% =====================================================================
-%% @spec size_qualifier_argument(syntaxTree()) -> syntaxTree()
-%%
%% @doc Returns the argument subtree (the size) of a
-%% <code>size_qualifier</code> node.
+%% `size_qualifier' node.
%%
%% @see size_qualifier/2
@@ -2862,16 +2657,14 @@ size_qualifier_argument(Node) ->
%% =====================================================================
-%% @spec error_marker(Error::term()) -> syntaxTree()
-%%
%% @doc Creates an abstract error marker. The result represents an
%% occurrence of an error in the source code, with an associated Erlang
-%% I/O ErrorInfo structure given by <code>Error</code> (see module
+%% I/O ErrorInfo structure given by `Error' (see module
%% {@link //stdlib/io} for details). Error markers are regarded as source
%% code forms, but have no defined lexical form.
%%
-%% <p>Note: this is supported only for backwards compatibility with
-%% existing parsers and tools.</p>
+%% Note: this is supported only for backwards compatibility with
+%% existing parsers and tools.
%%
%% @see error_marker_info/1
%% @see warning_marker/1
@@ -2902,10 +2695,7 @@ revert_error_marker(Node) ->
%% =====================================================================
-%% @spec error_marker_info(syntaxTree()) -> term()
-%%
-%% @doc Returns the ErrorInfo structure of an <code>error_marker</code>
-%% node.
+%% @doc Returns the ErrorInfo structure of an `error_marker' node.
%%
%% @see error_marker/1
@@ -2921,16 +2711,14 @@ error_marker_info(Node) ->
%% =====================================================================
-%% @spec warning_marker(Error::term()) -> syntaxTree()
-%%
%% @doc Creates an abstract warning marker. The result represents an
%% occurrence of a possible problem in the source code, with an
-%% associated Erlang I/O ErrorInfo structure given by <code>Error</code>
+%% associated Erlang I/O ErrorInfo structure given by `Error'
%% (see module {@link //stdlib/io} for details). Warning markers are
%% regarded as source code forms, but have no defined lexical form.
%%
-%% <p>Note: this is supported only for backwards compatibility with
-%% existing parsers and tools.</p>
+%% Note: this is supported only for backwards compatibility with
+%% existing parsers and tools.
%%
%% @see warning_marker_info/1
%% @see error_marker/1
@@ -2961,10 +2749,7 @@ revert_warning_marker(Node) ->
%% =====================================================================
-%% @spec warning_marker_info(syntaxTree()) -> term()
-%%
-%% @doc Returns the ErrorInfo structure of a <code>warning_marker</code>
-%% node.
+%% @doc Returns the ErrorInfo structure of a `warning_marker' node.
%%
%% @see warning_marker/1
@@ -2980,16 +2765,14 @@ warning_marker_info(Node) ->
%% =====================================================================
-%% @spec eof_marker() -> syntaxTree()
-%%
%% @doc Creates an abstract end-of-file marker. This represents the
%% end of input when reading a sequence of source code forms. An
%% end-of-file marker is itself regarded as a source code form
%% (namely, the last in any sequence in which it occurs). It has no
%% defined lexical form.
%%
-%% <p>Note: this is retained only for backwards compatibility with
-%% existing parsers and tools.</p>
+%% Note: this is retained only for backwards compatibility with
+%% existing parsers and tools.
%%
%% @see error_marker/1
%% @see warning_marker/1
@@ -3013,7 +2796,6 @@ revert_eof_marker(Node) ->
%% =====================================================================
-%% @spec attribute(Name) -> syntaxTree()
%% @equiv attribute(Name, none)
-spec attribute(syntaxTree()) -> syntaxTree().
@@ -3023,23 +2805,20 @@ attribute(Name) ->
%% =====================================================================
-%% @spec attribute(Name::syntaxTree(), Arguments) -> syntaxTree()
-%% Arguments = none | [syntaxTree()]
-%%
%% @doc Creates an abstract program attribute. If
-%% <code>Arguments</code> is <code>[A1, ..., An]</code>, the result
+%% `Arguments' is `[A1, ..., An]', the result
%% represents "<code>-<em>Name</em>(<em>A1</em>, ...,
-%% <em>An</em>).</code>". Otherwise, if <code>Arguments</code> is
-%% <code>none</code>, the result represents
+%% <em>An</em>).</code>". Otherwise, if `Arguments' is
+%% `none', the result represents
%% "<code>-<em>Name</em>.</code>". The latter form makes it possible
%% to represent preprocessor directives such as
-%% "<code>-endif.</code>". Attributes are source code forms.
+%% "`-endif.'". Attributes are source code forms.
%%
-%% <p>Note: The preprocessor macro definition directive
+%% Note: The preprocessor macro definition directive
%% "<code>-define(<em>Name</em>, <em>Body</em>).</code>" has relatively
-%% few requirements on the syntactical form of <code>Body</code> (viewed
-%% as a sequence of tokens). The <code>text</code> node type can be used
-%% for a <code>Body</code> that is not a normal Erlang construct.</p>
+%% few requirements on the syntactical form of `Body' (viewed
+%% as a sequence of tokens). The `text' node type can be used
+%% for a `Body' that is not a normal Erlang construct.
%%
%% @see attribute/1
%% @see attribute_name/1
@@ -3233,9 +3012,7 @@ revert_module_name(A) ->
%% =====================================================================
-%% @spec attribute_name(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the name subtree of an <code>attribute</code> node.
+%% @doc Returns the name subtree of an `attribute' node.
%%
%% @see attribute/1
@@ -3251,15 +3028,12 @@ attribute_name(Node) ->
%% =====================================================================
-%% @spec attribute_arguments(Node::syntaxTree()) ->
-%% none | [syntaxTree()]
-%%
%% @doc Returns the list of argument subtrees of an
-%% <code>attribute</code> node, if any. If <code>Node</code>
+%% `attribute' node, if any. If `Node'
%% represents "<code>-<em>Name</em>.</code>", the result is
-%% <code>none</code>. Otherwise, if <code>Node</code> represents
+%% `none'. Otherwise, if `Node' represents
%% "<code>-<em>Name</em>(<em>E1</em>, ..., <em>En</em>).</code>",
-%% <code>[E1, ..., E1]</code> is returned.
+%% `[E1, ..., E1]' is returned.
%%
%% @see attribute/1
@@ -3326,9 +3100,6 @@ attribute_arguments(Node) ->
%% =====================================================================
-%% @spec arity_qualifier(Body::syntaxTree(), Arity::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract arity qualifier. The result represents
%% "<code><em>Body</em>/<em>Arity</em></code>".
%%
@@ -3350,10 +3121,7 @@ arity_qualifier(Body, Arity) ->
%% =====================================================================
-%% @spec arity_qualifier_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of an <code>arity_qualifier</code>
-%% node.
+%% @doc Returns the body subtree of an `arity_qualifier' node.
%%
%% @see arity_qualifier/2
@@ -3364,10 +3132,8 @@ arity_qualifier_body(Node) ->
%% =====================================================================
-%% @spec arity_qualifier_argument(syntaxTree()) -> syntaxTree()
-%%
%% @doc Returns the argument (the arity) subtree of an
-%% <code>arity_qualifier</code> node.
+%% `arity_qualifier' node.
%%
%% @see arity_qualifier/2
@@ -3378,9 +3144,6 @@ arity_qualifier_argument(Node) ->
%% =====================================================================
-%% @spec module_qualifier(Module::syntaxTree(), Body::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract module qualifier. The result represents
%% "<code><em>Module</em>:<em>Body</em></code>".
%%
@@ -3414,10 +3177,8 @@ revert_module_qualifier(Node) ->
%% =====================================================================
-%% @spec module_qualifier_argument(syntaxTree()) -> syntaxTree()
-%%
%% @doc Returns the argument (the module) subtree of a
-%% <code>module_qualifier</code> node.
+%% `module_qualifier' node.
%%
%% @see module_qualifier/2
@@ -3433,10 +3194,7 @@ module_qualifier_argument(Node) ->
%% =====================================================================
-%% @spec module_qualifier_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>module_qualifier</code>
-%% node.
+%% @doc Returns the body subtree of a `module_qualifier' node.
%%
%% @see module_qualifier/2
@@ -3452,11 +3210,9 @@ module_qualifier_body(Node) ->
%% =====================================================================
-%% @spec qualified_name(Segments::[syntaxTree()]) -> syntaxTree()
-%%
%% @doc Creates an abstract qualified name. The result represents
%% "<code><em>S1</em>.<em>S2</em>. ... .<em>Sn</em></code>", if
-%% <code>Segments</code> is <code>[S1, S2, ..., Sn]</code>.
+%% `Segments' is `[S1, S2, ..., Sn]'.
%%
%% @see qualified_name_segments/1
@@ -3484,10 +3240,8 @@ revert_qualified_name(Node) ->
%% =====================================================================
-%% @spec qualified_name_segments(syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of name segments of a
-%% <code>qualified_name</code> node.
+%% `qualified_name' node.
%%
%% @see qualified_name/1
@@ -3503,13 +3257,10 @@ qualified_name_segments(Node) ->
%% =====================================================================
-%% @spec function(Name::syntaxTree(), Clauses::[syntaxTree()]) ->
-%% syntaxTree()
-%%
-%% @doc Creates an abstract function definition. If <code>Clauses</code>
-%% is <code>[C1, ..., Cn]</code>, the result represents
+%% @doc Creates an abstract function definition. If `Clauses'
+%% is `[C1, ..., Cn]', the result represents
%% "<code><em>Name</em> <em>C1</em>; ...; <em>Name</em>
-%% <em>Cn</em>.</code>". More exactly, if each <code>Ci</code>
+%% <em>Cn</em>.</code>". More exactly, if each `Ci'
%% represents "<code>(<em>Pi1</em>, ..., <em>Pim</em>) <em>Gi</em> ->
%% <em>Bi</em></code>", then the result represents
%% "<code><em>Name</em>(<em>P11</em>, ..., <em>P1m</em>) <em>G1</em> ->
@@ -3523,13 +3274,12 @@ qualified_name_segments(Node) ->
%% @see is_form/1
%% @see rule/2
--record(function, {name, clauses}).
-%% XXX: This one is problematic because there is a tuple with the same
-%% tag and size that comes from 'erl_parse'
-%% -record(function, {name :: syntaxTree(), clauses :: [syntaxTree()]}).
+%% Don't use the name 'function' for this record, to avoid confusion with
+%% the tuples on the form {function,Name,Arity} used by erl_parse.
+-record(func, {name :: syntaxTree(), clauses :: [syntaxTree()]}).
%% type(Node) = function
-%% data(Node) = #function{name :: Name, clauses :: Clauses}
+%% data(Node) = #func{name :: Name, clauses :: Clauses}
%%
%% Name = syntaxTree()
%% Clauses = [syntaxTree()]
@@ -3556,7 +3306,7 @@ qualified_name_segments(Node) ->
-spec function(syntaxTree(), [syntaxTree()]) -> syntaxTree().
function(Name, Clauses) ->
- tree(function, #function{name = Name, clauses = Clauses}).
+ tree(function, #func{name = Name, clauses = Clauses}).
revert_function(Node) ->
Name = function_name(Node),
@@ -3572,9 +3322,7 @@ revert_function(Node) ->
%% =====================================================================
-%% @spec function_name(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the name subtree of a <code>function</code> node.
+%% @doc Returns the name subtree of a `function' node.
%%
%% @see function/2
@@ -3585,15 +3333,12 @@ function_name(Node) ->
{function, Pos, Name, _, _} ->
set_pos(atom(Name), Pos);
Node1 ->
- (data(Node1))#function.name
+ (data(Node1))#func.name
end.
%% =====================================================================
-%% @spec function_clauses(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of clause subtrees of a <code>function</code>
-%% node.
+%% @doc Returns the list of clause subtrees of a `function' node.
%%
%% @see function/2
@@ -3604,21 +3349,19 @@ function_clauses(Node) ->
{function, _, _, _, Clauses} ->
Clauses;
Node1 ->
- (data(Node1))#function.clauses
+ (data(Node1))#func.clauses
end.
%% =====================================================================
-%% @spec function_arity(Node::syntaxTree()) -> integer()
-%%
-%% @doc Returns the arity of a <code>function</code> node. The result
+%% @doc Returns the arity of a `function' node. The result
%% is the number of parameter patterns in the first clause of the
%% function; subsequent clauses are ignored.
%%
-%% <p>An exception is thrown if <code>function_clauses(Node)</code>
+%% An exception is thrown if `function_clauses(Node)'
%% returns an empty list, or if the first element of that list is not
-%% a syntax tree <code>C</code> of type <code>clause</code> such that
-%% <code>clause_patterns(C)</code> is a nonempty list.</p>
+%% a syntax tree `C' of type `clause' such that
+%% `clause_patterns(C)' is a nonempty list.
%%
%% @see function/2
%% @see function_clauses/1
@@ -3634,7 +3377,6 @@ function_arity(Node) ->
%% =====================================================================
-%% @spec clause(Guard, Body) -> syntaxTree()
%% @equiv clause([], Guard, Body)
-type guard() :: 'none' | syntaxTree() | [syntaxTree()] | [[syntaxTree()]].
@@ -3646,34 +3388,28 @@ clause(Guard, Body) ->
%% =====================================================================
-%% @spec clause(Patterns::[syntaxTree()], Guard,
-%% Body::[syntaxTree()]) -> syntaxTree()
-%% Guard = none | syntaxTree()
-%% | [syntaxTree()] | [[syntaxTree()]]
-%%
-%% @doc Creates an abstract clause. If <code>Patterns</code> is
-%% <code>[P1, ..., Pn]</code> and <code>Body</code> is <code>[B1, ...,
-%% Bm]</code>, then if <code>Guard</code> is <code>none</code>, the
+%% @doc Creates an abstract clause. If `Patterns' is
+%% `[P1, ..., Pn]' and `Body' is `[B1, ...,
+%% Bm]', then if `Guard' is `none', the
%% result represents "<code>(<em>P1</em>, ..., <em>Pn</em>) ->
%% <em>B1</em>, ..., <em>Bm</em></code>", otherwise, unless
-%% <code>Guard</code> is a list, the result represents
+%% `Guard' is a list, the result represents
%% "<code>(<em>P1</em>, ..., <em>Pn</em>) when <em>Guard</em> ->
%% <em>B1</em>, ..., <em>Bm</em></code>".
%%
-%% <p>For simplicity, the <code>Guard</code> argument may also be any
+%% For simplicity, the `Guard' argument may also be any
%% of the following:
%% <ul>
-%% <li>An empty list <code>[]</code>. This is equivalent to passing
-%% <code>none</code>.</li>
-%% <li>A nonempty list <code>[E1, ..., Ej]</code> of syntax trees.
-%% This is equivalent to passing <code>conjunction([E1, ...,
-%% Ej])</code>.</li>
-%% <li>A nonempty list of lists of syntax trees <code>[[E1_1, ...,
-%% E1_k1], ..., [Ej_1, ..., Ej_kj]]</code>, which is equivalent
-%% to passing <code>disjunction([conjunction([E1_1, ...,
-%% E1_k1]), ..., conjunction([Ej_1, ..., Ej_kj])])</code>.</li>
+%% <li>An empty list `[]'. This is equivalent to passing
+%% `none'.</li>
+%% <li>A nonempty list `[E1, ..., Ej]' of syntax trees.
+%% This is equivalent to passing `conjunction([E1, ...,
+%% Ej])'.</li>
+%% <li>A nonempty list of lists of syntax trees `[[E1_1, ...,
+%% E1_k1], ..., [Ej_1, ..., Ej_kj]]', which is equivalent
+%% to passing `disjunction([conjunction([E1_1, ...,
+%% E1_k1]), ..., conjunction([Ej_1, ..., Ej_kj])])'.</li>
%% </ul>
-%% </p>
%%
%% @see clause/2
%% @see clause_patterns/1
@@ -3789,10 +3525,7 @@ unfold_try_clause({clause, Pos, [{tuple, _, [C, V, _]}],
%% =====================================================================
-%% @spec clause_patterns(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of pattern subtrees of a <code>clause</code>
-%% node.
+%% @doc Returns the list of pattern subtrees of a `clause' node.
%%
%% @see clause/3
@@ -3808,13 +3541,11 @@ clause_patterns(Node) ->
%% =====================================================================
-%% @spec clause_guard(Node::syntaxTree()) -> none | syntaxTree()
-%%
-%% @doc Returns the guard subtree of a <code>clause</code> node, if
-%% any. If <code>Node</code> represents "<code>(<em>P1</em>, ...,
+%% @doc Returns the guard subtree of a `clause' node, if
+%% any. If `Node' represents "<code>(<em>P1</em>, ...,
%% <em>Pn</em>) when <em>Guard</em> -> <em>B1</em>, ...,
-%% <em>Bm</em></code>", <code>Guard</code> is returned. Otherwise, the
-%% result is <code>none</code>.
+%% <em>Bm</em></code>", `Guard' is returned. Otherwise, the
+%% result is `none'.
%%
%% @see clause/3
@@ -3836,10 +3567,7 @@ clause_guard(Node) ->
%% =====================================================================
-%% @spec clause_body(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Return the list of body subtrees of a <code>clause</code>
-%% node.
+%% @doc Return the list of body subtrees of a `clause' node.
%%
%% @see clause/3
@@ -3855,10 +3583,8 @@ clause_body(Node) ->
%% =====================================================================
-%% @spec disjunction(List::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Creates an abstract disjunction. If <code>List</code> is
-%% <code>[E1, ..., En]</code>, the result represents
+%% @doc Creates an abstract disjunction. If `List' is
+%% `[E1, ..., En]', the result represents
%% "<code><em>E1</em>; ...; <em>En</em></code>".
%%
%% @see disjunction_body/1
@@ -3874,10 +3600,8 @@ disjunction(Tests) ->
%% =====================================================================
-%% @spec disjunction_body(syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of body subtrees of a
-%% <code>disjunction</code> node.
+%% `disjunction' node.
%%
%% @see disjunction/1
@@ -3888,10 +3612,8 @@ disjunction_body(Node) ->
%% =====================================================================
-%% @spec conjunction(List::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Creates an abstract conjunction. If <code>List</code> is
-%% <code>[E1, ..., En]</code>, the result represents
+%% @doc Creates an abstract conjunction. If `List' is
+%% `[E1, ..., En]', the result represents
%% "<code><em>E1</em>, ..., <em>En</em></code>".
%%
%% @see conjunction_body/1
@@ -3907,10 +3629,8 @@ conjunction(Tests) ->
%% =====================================================================
-%% @spec conjunction_body(syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of body subtrees of a
-%% <code>conjunction</code> node.
+%% `conjunction' node.
%%
%% @see conjunction/1
@@ -3921,8 +3641,6 @@ conjunction_body(Node) ->
%% =====================================================================
-%% @spec catch_expr(Expr::syntaxTree()) -> syntaxTree()
-%%
%% @doc Creates an abstract catch-expression. The result represents
%% "<code>catch <em>Expr</em></code>".
%%
@@ -3949,9 +3667,7 @@ revert_catch_expr(Node) ->
%% =====================================================================
-%% @spec catch_expr_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>catch_expr</code> node.
+%% @doc Returns the body subtree of a `catch_expr' node.
%%
%% @see catch_expr/1
@@ -3967,9 +3683,6 @@ catch_expr_body(Node) ->
%% =====================================================================
-%% @spec match_expr(Pattern::syntaxTree(), Body::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract match-expression. The result represents
%% "<code><em>Pattern</em> = <em>Body</em></code>".
%%
@@ -4002,9 +3715,7 @@ revert_match_expr(Node) ->
%% =====================================================================
-%% @spec match_expr_pattern(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the pattern subtree of a <code>match_expr</code> node.
+%% @doc Returns the pattern subtree of a `match_expr' node.
%%
%% @see match_expr/2
@@ -4020,9 +3731,7 @@ match_expr_pattern(Node) ->
%% =====================================================================
-%% @spec match_expr_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>match_expr</code> node.
+%% @doc Returns the body subtree of a `match_expr' node.
%%
%% @see match_expr/2
@@ -4038,15 +3747,12 @@ match_expr_body(Node) ->
%% =====================================================================
-%% @spec operator(Name) -> syntaxTree()
-%% Name = atom() | string()
-%%
%% @doc Creates an abstract operator. The name of the operator is the
-%% character sequence represented by <code>Name</code>. This is
+%% character sequence represented by `Name'. This is
%% analogous to the print name of an atom, but an operator is never
%% written within single-quotes; e.g., the result of
-%% <code>operator('++')</code> represents "<code>++</code>" rather
-%% than "<code>'++'</code>".
+%% `operator('++')' represents "`++'" rather
+%% than "`'++''".
%%
%% @see operator_name/1
%% @see operator_literal/1
@@ -4064,9 +3770,7 @@ operator(Name) ->
%% =====================================================================
-%% @spec operator_name(syntaxTree()) -> atom()
-%%
-%% @doc Returns the name of an <code>operator</code> node. Note that
+%% @doc Returns the name of an `operator' node. Note that
%% the name is returned as an atom.
%%
%% @see operator/1
@@ -4078,11 +3782,8 @@ operator_name(Node) ->
%% =====================================================================
-%% @spec operator_literal(syntaxTree()) -> string()
-%%
%% @doc Returns the literal string represented by an
-%% <code>operator</code> node. This is simply the operator name as a
-%% string.
+%% `operator' node. This is simply the operator name as a string.
%%
%% @see operator/1
@@ -4093,9 +3794,6 @@ operator_literal(Node) ->
%% =====================================================================
-%% @spec infix_expr(Left::syntaxTree(), Operator::syntaxTree(),
-%% Right::syntaxTree()) -> syntaxTree()
-%%
%% @doc Creates an abstract infix operator expression. The result
%% represents "<code><em>Left</em> <em>Operator</em>
%% <em>Right</em></code>".
@@ -4144,10 +3842,8 @@ revert_infix_expr(Node) ->
%% =====================================================================
-%% @spec infix_expr_left(syntaxTree()) -> syntaxTree()
-%%
%% @doc Returns the left argument subtree of an
-%% <code>infix_expr</code> node.
+%% `infix_expr' node.
%%
%% @see infix_expr/3
@@ -4163,10 +3859,7 @@ infix_expr_left(Node) ->
%% =====================================================================
-%% @spec infix_expr_operator(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the operator subtree of an <code>infix_expr</code>
-%% node.
+%% @doc Returns the operator subtree of an `infix_expr' node.
%%
%% @see infix_expr/3
@@ -4182,10 +3875,8 @@ infix_expr_operator(Node) ->
%% =====================================================================
-%% @spec infix_expr_right(syntaxTree()) -> syntaxTree()
-%%
%% @doc Returns the right argument subtree of an
-%% <code>infix_expr</code> node.
+%% `infix_expr' node.
%%
%% @see infix_expr/3
@@ -4201,9 +3892,6 @@ infix_expr_right(Node) ->
%% =====================================================================
-%% @spec prefix_expr(Operator::syntaxTree(), Argument::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract prefix operator expression. The result
%% represents "<code><em>Operator</em> <em>Argument</em></code>".
%%
@@ -4247,10 +3935,7 @@ revert_prefix_expr(Node) ->
%% =====================================================================
-%% @spec prefix_expr_operator(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the operator subtree of a <code>prefix_expr</code>
-%% node.
+%% @doc Returns the operator subtree of a `prefix_expr' node.
%%
%% @see prefix_expr/2
@@ -4266,10 +3951,7 @@ prefix_expr_operator(Node) ->
%% =====================================================================
-%% @spec prefix_expr_argument(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the argument subtree of a <code>prefix_expr</code>
-%% node.
+%% @doc Returns the argument subtree of a `prefix_expr' node.
%%
%% @see prefix_expr/2
@@ -4285,7 +3967,6 @@ prefix_expr_argument(Node) ->
%% =====================================================================
-%% @spec record_field(Name) -> syntaxTree()
%% @equiv record_field(Name, none)
-spec record_field(syntaxTree()) -> syntaxTree().
@@ -4295,11 +3976,8 @@ record_field(Name) ->
%% =====================================================================
-%% @spec record_field(Name::syntaxTree(), Value) -> syntaxTree()
-%% Value = none | syntaxTree()
-%%
%% @doc Creates an abstract record field specification. If
-%% <code>Value</code> is <code>none</code>, the result represents
+%% `Value' is `none', the result represents
%% simply "<code><em>Name</em></code>", otherwise it represents
%% "<code><em>Name</em> = <em>Value</em></code>".
%%
@@ -4321,9 +3999,7 @@ record_field(Name, Value) ->
%% =====================================================================
-%% @spec record_field_name(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the name subtree of a <code>record_field</code> node.
+%% @doc Returns the name subtree of a `record_field' node.
%%
%% @see record_field/2
@@ -4334,13 +4010,11 @@ record_field_name(Node) ->
%% =====================================================================
-%% @spec record_field_value(syntaxTree()) -> none | syntaxTree()
-%%
-%% @doc Returns the value subtree of a <code>record_field</code> node,
-%% if any. If <code>Node</code> represents
-%% "<code><em>Name</em></code>", <code>none</code> is
-%% returned. Otherwise, if <code>Node</code> represents
-%% "<code><em>Name</em> = <em>Value</em></code>", <code>Value</code>
+%% @doc Returns the value subtree of a `record_field' node,
+%% if any. If `Node' represents
+%% "<code><em>Name</em></code>", `none' is
+%% returned. Otherwise, if `Node' represents
+%% "<code><em>Name</em> = <em>Value</em></code>", `Value'
%% is returned.
%%
%% @see record_field/2
@@ -4352,15 +4026,12 @@ record_field_value(Node) ->
%% =====================================================================
-%% @spec record_index_expr(Type::syntaxTree(), Field::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract record field index expression. The result
%% represents "<code>#<em>Type</em>.<em>Field</em></code>".
%%
-%% <p>(Note: the function name <code>record_index/2</code> is reserved
+%% (Note: the function name `record_index/2' is reserved
%% by the Erlang compiler, which is why that name could not be used
-%% for this constructor.)</p>
+%% for this constructor.)
%%
%% @see record_index_expr_type/1
%% @see record_index_expr_field/1
@@ -4399,10 +4070,7 @@ revert_record_index_expr(Node) ->
%% =====================================================================
-%% @spec record_index_expr_type(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the type subtree of a <code>record_index_expr</code>
-%% node.
+%% @doc Returns the type subtree of a `record_index_expr' node.
%%
%% @see record_index_expr/2
@@ -4418,10 +4086,7 @@ record_index_expr_type(Node) ->
%% =====================================================================
-%% @spec record_index_expr_field(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the field subtree of a <code>record_index_expr</code>
-%% node.
+%% @doc Returns the field subtree of a `record_index_expr' node.
%%
%% @see record_index_expr/2
@@ -4437,7 +4102,6 @@ record_index_expr_field(Node) ->
%% =====================================================================
-%% @spec record_access(Argument, Field) -> syntaxTree()
%% @equiv record_access(Argument, none, Field)
-spec record_access(syntaxTree(), syntaxTree()) -> syntaxTree().
@@ -4447,17 +4111,13 @@ record_access(Argument, Field) ->
%% =====================================================================
-%% @spec record_access(Argument::syntaxTree(), Type,
-%% Field::syntaxTree()) -> syntaxTree()
-%% Type = none | syntaxTree()
-%%
%% @doc Creates an abstract record field access expression. If
-%% <code>Type</code> is not <code>none</code>, the result represents
+%% `Type' is not `none', the result represents
%% "<code><em>Argument</em>#<em>Type</em>.<em>Field</em></code>".
%%
-%% <p>If <code>Type</code> is <code>none</code>, the result represents
+%% If `Type' is `none', the result represents
%% "<code><em>Argument</em>.<em>Field</em></code>". This is a special
-%% form only allowed within Mnemosyne queries.</p>
+%% form only allowed within Mnemosyne queries.
%%
%% @see record_access/2
%% @see record_access_argument/1
@@ -4512,10 +4172,7 @@ revert_record_access(Node) ->
%% =====================================================================
-%% @spec record_access_argument(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the argument subtree of a <code>record_access</code>
-%% node.
+%% @doc Returns the argument subtree of a `record_access' node.
%%
%% @see record_access/3
@@ -4533,14 +4190,12 @@ record_access_argument(Node) ->
%% =====================================================================
-%% @spec record_access_type(syntaxTree()) -> none | syntaxTree()
-%%
-%% @doc Returns the type subtree of a <code>record_access</code> node,
-%% if any. If <code>Node</code> represents
-%% "<code><em>Argument</em>.<em>Field</em></code>", <code>none</code>
-%% is returned, otherwise if <code>Node</code> represents
+%% @doc Returns the type subtree of a `record_access' node,
+%% if any. If `Node' represents
+%% "<code><em>Argument</em>.<em>Field</em></code>", `none'
+%% is returned, otherwise if `Node' represents
%% "<code><em>Argument</em>#<em>Type</em>.<em>Field</em></code>",
-%% <code>Type</code> is returned.
+%% `Type' is returned.
%%
%% @see record_access/3
@@ -4558,10 +4213,7 @@ record_access_type(Node) ->
%% =====================================================================
-%% @spec record_access_field(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the field subtree of a <code>record_access</code>
-%% node.
+%% @doc Returns the field subtree of a `record_access' node.
%%
%% @see record_access/3
@@ -4579,7 +4231,6 @@ record_access_field(Node) ->
%% =====================================================================
-%% @spec record_expr(Type, Fields) -> syntaxTree()
%% @equiv record_expr(none, Type, Fields)
-spec record_expr(syntaxTree(), [syntaxTree()]) -> syntaxTree().
@@ -4589,13 +4240,9 @@ record_expr(Type, Fields) ->
%% =====================================================================
-%% @spec record_expr(Argument, Type::syntaxTree(),
-%% Fields::[syntaxTree()]) -> syntaxTree()
-%% Argument = none | syntaxTree()
-%%
-%% @doc Creates an abstract record expression. If <code>Fields</code> is
-%% <code>[F1, ..., Fn]</code>, then if <code>Argument</code> is
-%% <code>none</code>, the result represents
+%% @doc Creates an abstract record expression. If `Fields' is
+%% `[F1, ..., Fn]', then if `Argument' is
+%% `none', the result represents
%% "<code>#<em>Type</em>{<em>F1</em>, ..., <em>Fn</em>}</code>",
%% otherwise it represents
%% "<code><em>Argument</em>#<em>Type</em>{<em>F1</em>, ...,
@@ -4661,14 +4308,12 @@ revert_record_expr(Node) ->
%% =====================================================================
-%% @spec record_expr_argument(syntaxTree()) -> none | syntaxTree()
-%%
-%% @doc Returns the argument subtree of a <code>record_expr</code> node,
-%% if any. If <code>Node</code> represents
-%% "<code>#<em>Type</em>{...}</code>", <code>none</code> is returned.
-%% Otherwise, if <code>Node</code> represents
+%% @doc Returns the argument subtree of a `record_expr' node,
+%% if any. If `Node' represents
+%% "<code>#<em>Type</em>{...}</code>", `none' is returned.
+%% Otherwise, if `Node' represents
%% "<code><em>Argument</em>#<em>Type</em>{...}</code>",
-%% <code>Argument</code> is returned.
+%% `Argument' is returned.
%%
%% @see record_expr/3
@@ -4686,9 +4331,7 @@ record_expr_argument(Node) ->
%% =====================================================================
-%% @spec record_expr_type(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the type subtree of a <code>record_expr</code> node.
+%% @doc Returns the type subtree of a `record_expr' node.
%%
%% @see record_expr/3
@@ -4706,10 +4349,8 @@ record_expr_type(Node) ->
%% =====================================================================
-%% @spec record_expr_fields(syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of field subtrees of a
-%% <code>record_expr</code> node.
+%% `record_expr' node.
%%
%% @see record_expr/3
@@ -4727,15 +4368,11 @@ record_expr_fields(Node) ->
%% =====================================================================
-%% @spec application(Module, Function::syntaxTree(),
-%% Arguments::[syntaxTree()]) -> syntaxTree()
-%% Module = none | syntaxTree()
-%%
%% @doc Creates an abstract function application expression. If
-%% <code>Module</code> is <code>none</code>, this is call is equivalent
-%% to <code>application(Function, Arguments)</code>, otherwise it is
-%% equivalent to <code>application(module_qualifier(Module, Function),
-%% Arguments)</code>.
+%% `Module' is `none', this is call is equivalent
+%% to `application(Function, Arguments)', otherwise it is
+%% equivalent to `application(module_qualifier(Module, Function),
+%% Arguments)'.
%%
%% (This is a utility function.)
%%
@@ -4752,11 +4389,8 @@ application(Module, Name, Arguments) ->
%% =====================================================================
-%% @spec application(Operator::syntaxTree(),
-%% Arguments::[syntaxTree()]) -> syntaxTree()
-%%
%% @doc Creates an abstract function application expression. If
-%% <code>Arguments</code> is <code>[A1, ..., An]</code>, the result
+%% `Arguments' is `[A1, ..., An]', the result
%% represents "<code><em>Operator</em>(<em>A1</em>, ...,
%% <em>An</em>)</code>".
%%
@@ -4794,14 +4428,11 @@ revert_application(Node) ->
%% =====================================================================
-%% @spec application_operator(syntaxTree()) -> syntaxTree()
+%% @doc Returns the operator subtree of an `application' node.
%%
-%% @doc Returns the operator subtree of an <code>application</code>
-%% node.
-%%
-%% <p>Note: if <code>Node</code> represents
+%% Note: if `Node' represents
%% "<code><em>M</em>:<em>F</em>(...)</code>", then the result is the
-%% subtree representing "<code><em>M</em>:<em>F</em></code>".</p>
+%% subtree representing "<code><em>M</em>:<em>F</em></code>".
%%
%% @see application/2
%% @see module_qualifier/2
@@ -4818,10 +4449,8 @@ application_operator(Node) ->
%% =====================================================================
-%% @spec application_arguments(syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of argument subtrees of an
-%% <code>application</code> node.
+%% `application' node.
%%
%% @see application/2
@@ -4837,11 +4466,8 @@ application_arguments(Node) ->
%% =====================================================================
-%% @spec list_comp(Template::syntaxTree(), Body::[syntaxTree()]) ->
-%% syntaxTree()
-%%
-%% @doc Creates an abstract list comprehension. If <code>Body</code> is
-%% <code>[E1, ..., En]</code>, the result represents
+%% @doc Creates an abstract list comprehension. If `Body' is
+%% `[E1, ..., En]', the result represents
%% "<code>[<em>Template</em> || <em>E1</em>, ..., <em>En</em>]</code>".
%%
%% @see list_comp_template/1
@@ -4876,9 +4502,7 @@ revert_list_comp(Node) ->
%% =====================================================================
-%% @spec list_comp_template(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the template subtree of a <code>list_comp</code> node.
+%% @doc Returns the template subtree of a `list_comp' node.
%%
%% @see list_comp/2
@@ -4894,10 +4518,7 @@ list_comp_template(Node) ->
%% =====================================================================
-%% @spec list_comp_body(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of body subtrees of a <code>list_comp</code>
-%% node.
+%% @doc Returns the list of body subtrees of a `list_comp' node.
%%
%% @see list_comp/2
@@ -4912,11 +4533,8 @@ list_comp_body(Node) ->
end.
%% =====================================================================
-%% @spec binary_comp(Template::syntaxTree(), Body::[syntaxTree()]) ->
-%% syntaxTree()
-%%
-%% @doc Creates an abstract binary comprehension. If <code>Body</code> is
-%% <code>[E1, ..., En]</code>, the result represents
+%% @doc Creates an abstract binary comprehension. If `Body' is
+%% `[E1, ..., En]', the result represents
%% "<code>&lt;&lt;<em>Template</em> || <em>E1</em>, ..., <em>En</em>&gt;&gt;</code>".
%%
%% @see binary_comp_template/1
@@ -4951,9 +4569,7 @@ revert_binary_comp(Node) ->
%% =====================================================================
-%% @spec binary_comp_template(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the template subtree of a <code>binary_comp</code> node.
+%% @doc Returns the template subtree of a `binary_comp' node.
%%
%% @see binary_comp/2
@@ -4969,10 +4585,7 @@ binary_comp_template(Node) ->
%% =====================================================================
-%% @spec binary_comp_body(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of body subtrees of a <code>binary_comp</code>
-%% node.
+%% @doc Returns the list of body subtrees of a `binary_comp' node.
%%
%% @see binary_comp/2
@@ -4988,8 +4601,6 @@ binary_comp_body(Node) ->
%% =====================================================================
-%% @spec query_expr(Body::syntaxTree()) -> syntaxTree()
-%%
%% @doc Creates an abstract Mnemosyne query expression. The result
%% represents "<code>query <em>Body</em> end</code>".
%%
@@ -5018,9 +4629,7 @@ revert_query_expr(Node) ->
%% =====================================================================
-%% @spec query_expr_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>query_expr</code> node.
+%% @doc Returns the body subtree of a `query_expr' node.
%%
%% @see query_expr/1
@@ -5036,13 +4645,10 @@ query_expr_body(Node) ->
%% =====================================================================
-%% @spec rule(Name::syntaxTree(), Clauses::[syntaxTree()]) ->
-%% syntaxTree()
-%%
-%% @doc Creates an abstract Mnemosyne rule. If <code>Clauses</code> is
-%% <code>[C1, ..., Cn]</code>, the results represents
+%% @doc Creates an abstract Mnemosyne rule. If `Clauses' is
+%% `[C1, ..., Cn]', the results represents
%% "<code><em>Name</em> <em>C1</em>; ...; <em>Name</em>
-%% <em>Cn</em>.</code>". More exactly, if each <code>Ci</code>
+%% <em>Cn</em>.</code>". More exactly, if each `Ci'
%% represents "<code>(<em>Pi1</em>, ..., <em>Pim</em>) <em>Gi</em> ->
%% <em>Bi</em></code>", then the result represents
%% "<code><em>Name</em>(<em>P11</em>, ..., <em>P1m</em>) <em>G1</em> :-
@@ -5097,9 +4703,7 @@ revert_rule(Node) ->
%% =====================================================================
-%% @spec rule_name(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the name subtree of a <code>rule</code> node.
+%% @doc Returns the name subtree of a `rule' node.
%%
%% @see rule/2
@@ -5114,9 +4718,7 @@ rule_name(Node) ->
end.
%% =====================================================================
-%% @spec rule_clauses(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of clause subtrees of a <code>rule</code> node.
+%% @doc Returns the list of clause subtrees of a `rule' node.
%%
%% @see rule/2
@@ -5131,16 +4733,14 @@ rule_clauses(Node) ->
end.
%% =====================================================================
-%% @spec rule_arity(Node::syntaxTree()) -> integer()
-%%
-%% @doc Returns the arity of a <code>rule</code> node. The result is the
+%% @doc Returns the arity of a `rule' node. The result is the
%% number of parameter patterns in the first clause of the rule;
%% subsequent clauses are ignored.
%%
-%% <p>An exception is thrown if <code>rule_clauses(Node)</code> returns
+%% An exception is thrown if `rule_clauses(Node)' returns
%% an empty list, or if the first element of that list is not a syntax
-%% tree <code>C</code> of type <code>clause</code> such that
-%% <code>clause_patterns(C)</code> is a nonempty list.</p>
+%% tree `C' of type `clause' such that
+%% `clause_patterns(C)' is a nonempty list.
%%
%% @see rule/2
%% @see rule_clauses/1
@@ -5156,9 +4756,6 @@ rule_arity(Node) ->
%% =====================================================================
-%% @spec generator(Pattern::syntaxTree(), Body::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract generator. The result represents
%% "<code><em>Pattern</em> &lt;- <em>Body</em></code>".
%%
@@ -5193,9 +4790,7 @@ revert_generator(Node) ->
%% =====================================================================
-%% @spec generator_pattern(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the pattern subtree of a <code>generator</code> node.
+%% @doc Returns the pattern subtree of a `generator' node.
%%
%% @see generator/2
@@ -5211,9 +4806,7 @@ generator_pattern(Node) ->
%% =====================================================================
-%% @spec generator_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>generator</code> node.
+%% @doc Returns the body subtree of a `generator' node.
%%
%% @see generator/2
@@ -5229,9 +4822,6 @@ generator_body(Node) ->
%% =====================================================================
-%% @spec binary_generator(Pattern::syntaxTree(), Body::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract binary_generator. The result represents
%% "<code><em>Pattern</em> &lt;- <em>Body</em></code>".
%%
@@ -5266,9 +4856,7 @@ revert_binary_generator(Node) ->
%% =====================================================================
-%% @spec binary_generator_pattern(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the pattern subtree of a <code>generator</code> node.
+%% @doc Returns the pattern subtree of a `generator' node.
%%
%% @see binary_generator/2
@@ -5284,9 +4872,7 @@ binary_generator_pattern(Node) ->
%% =====================================================================
-%% @spec binary_generator_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>generator</code> node.
+%% @doc Returns the body subtree of a `generator' node.
%%
%% @see binary_generator/2
@@ -5302,10 +4888,8 @@ binary_generator_body(Node) ->
%% =====================================================================
-%% @spec block_expr(Body::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Creates an abstract block expression. If <code>Body</code> is
-%% <code>[B1, ..., Bn]</code>, the result represents "<code>begin
+%% @doc Creates an abstract block expression. If `Body' is
+%% `[B1, ..., Bn]', the result represents "<code>begin
%% <em>B1</em>, ..., <em>Bn</em> end</code>".
%%
%% @see block_expr_body/1
@@ -5321,7 +4905,7 @@ binary_generator_body(Node) ->
%%
%% Body = [erl_parse()] \ []
--spec block_expr(Body::[syntaxTree()]) -> syntaxTree().
+-spec block_expr([syntaxTree()]) -> syntaxTree().
block_expr(Body) ->
tree(block_expr, Body).
@@ -5333,10 +4917,7 @@ revert_block_expr(Node) ->
%% =====================================================================
-%% @spec block_expr_body(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of body subtrees of a <code>block_expr</code>
-%% node.
+%% @doc Returns the list of body subtrees of a `block_expr' node.
%%
%% @see block_expr/1
@@ -5352,12 +4933,10 @@ block_expr_body(Node) ->
%% =====================================================================
-%% @spec if_expr(Clauses::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Creates an abstract if-expression. If <code>Clauses</code> is
-%% <code>[C1, ..., Cn]</code>, the result represents "<code>if
+%% @doc Creates an abstract if-expression. If `Clauses' is
+%% `[C1, ..., Cn]', the result represents "<code>if
%% <em>C1</em>; ...; <em>Cn</em> end</code>". More exactly, if each
-%% <code>Ci</code> represents "<code>() <em>Gi</em> ->
+%% `Ci' represents "<code>() <em>Gi</em> ->
%% <em>Bi</em></code>", then the result represents "<code>if
%% <em>G1</em> -> <em>B1</em>; ...; <em>Gn</em> -> <em>Bn</em>
%% end</code>".
@@ -5392,10 +4971,7 @@ revert_if_expr(Node) ->
%% =====================================================================
-%% @spec if_expr_clauses(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of clause subtrees of an <code>if_expr</code>
-%% node.
+%% @doc Returns the list of clause subtrees of an `if_expr' node.
%%
%% @see if_expr/1
@@ -5411,13 +4987,10 @@ if_expr_clauses(Node) ->
%% =====================================================================
-%% @spec case_expr(Argument::syntaxTree(), Clauses::[syntaxTree()]) ->
-%% syntaxTree()
-%%
-%% @doc Creates an abstract case-expression. If <code>Clauses</code> is
-%% <code>[C1, ..., Cn]</code>, the result represents "<code>case
+%% @doc Creates an abstract case-expression. If `Clauses' is
+%% `[C1, ..., Cn]', the result represents "<code>case
%% <em>Argument</em> of <em>C1</em>; ...; <em>Cn</em> end</code>". More
-%% exactly, if each <code>Ci</code> represents "<code>(<em>Pi</em>)
+%% exactly, if each `Ci' represents "<code>(<em>Pi</em>)
%% <em>Gi</em> -> <em>Bi</em></code>", then the result represents
%% "<code>case <em>Argument</em> of <em>P1</em> <em>G1</em> ->
%% <em>B1</em>; ...; <em>Pn</em> <em>Gn</em> -> <em>Bn</em> end</code>".
@@ -5461,9 +5034,7 @@ revert_case_expr(Node) ->
%% =====================================================================
-%% @spec case_expr_argument(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the argument subtree of a <code>case_expr</code> node.
+%% @doc Returns the argument subtree of a `case_expr' node.
%%
%% @see case_expr/2
@@ -5479,10 +5050,7 @@ case_expr_argument(Node) ->
%% =====================================================================
-%% @spec case_expr_clauses(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of clause subtrees of a <code>case_expr</code>
-%% node.
+%% @doc Returns the list of clause subtrees of a `case_expr' node.
%%
%% @see case_expr/2
@@ -5498,12 +5066,10 @@ case_expr_clauses(Node) ->
%% =====================================================================
-%% @spec cond_expr(Clauses::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Creates an abstract cond-expression. If <code>Clauses</code> is
-%% <code>[C1, ..., Cn]</code>, the result represents "<code>cond
+%% @doc Creates an abstract cond-expression. If `Clauses' is
+%% `[C1, ..., Cn]', the result represents "<code>cond
%% <em>C1</em>; ...; <em>Cn</em> end</code>". More exactly, if each
-%% <code>Ci</code> represents "<code>() <em>Ei</em> ->
+%% `Ci' represents "<code>() <em>Ei</em> ->
%% <em>Bi</em></code>", then the result represents "<code>cond
%% <em>E1</em> -> <em>B1</em>; ...; <em>En</em> -> <em>Bn</em>
%% end</code>".
@@ -5538,10 +5104,7 @@ revert_cond_expr(Node) ->
%% =====================================================================
-%% @spec cond_expr_clauses(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of clause subtrees of a <code>cond_expr</code>
-%% node.
+%% @doc Returns the list of clause subtrees of a `cond_expr' node.
%%
%% @see cond_expr/1
@@ -5557,7 +5120,6 @@ cond_expr_clauses(Node) ->
%% =====================================================================
-%% @spec receive_expr(Clauses) -> syntaxTree()
%% @equiv receive_expr(Clauses, none, [])
-spec receive_expr([syntaxTree()]) -> syntaxTree().
@@ -5567,25 +5129,21 @@ receive_expr(Clauses) ->
%% =====================================================================
-%% @spec receive_expr(Clauses::[syntaxTree()], Timeout,
-%% Action::[syntaxTree()]) -> syntaxTree()
-%% Timeout = none | syntaxTree()
-%%
-%% @doc Creates an abstract receive-expression. If <code>Timeout</code>
-%% is <code>none</code>, the result represents "<code>receive
-%% <em>C1</em>; ...; <em>Cn</em> end</code>" (the <code>Action</code>
-%% argument is ignored). Otherwise, if <code>Clauses</code> is
-%% <code>[C1, ..., Cn]</code> and <code>Action</code> is <code>[A1, ...,
-%% Am]</code>, the result represents "<code>receive <em>C1</em>; ...;
+%% @doc Creates an abstract receive-expression. If `Timeout'
+%% is `none', the result represents "<code>receive
+%% <em>C1</em>; ...; <em>Cn</em> end</code>" (the `Action'
+%% argument is ignored). Otherwise, if `Clauses' is
+%% `[C1, ..., Cn]' and `Action' is `[A1, ...,
+%% Am]', the result represents "<code>receive <em>C1</em>; ...;
%% <em>Cn</em> after <em>Timeout</em> -> <em>A1</em>, ..., <em>Am</em>
-%% end</code>". More exactly, if each <code>Ci</code> represents
+%% end</code>". More exactly, if each `Ci' represents
%% "<code>(<em>Pi</em>) <em>Gi</em> -> <em>Bi</em></code>", then the
%% result represents "<code>receive <em>P1</em> <em>G1</em> ->
%% <em>B1</em>; ...; <em>Pn</em> <em>Gn</em> -> <em>Bn</em> ...
%% end</code>".
%%
-%% <p>Note that in Erlang, a receive-expression must have at least one
-%% clause if no timeout part is specified.</p>
+%% Note that in Erlang, a receive-expression must have at least one
+%% clause if no timeout part is specified.
%%
%% @see receive_expr_clauses/1
%% @see receive_expr_timeout/1
@@ -5649,11 +5207,8 @@ revert_receive_expr(Node) ->
%% =====================================================================
-%% @spec receive_expr_clauses(syntaxTree()) -> [syntaxTree()]
-%% type(Node) = receive_expr
-%%
%% @doc Returns the list of clause subtrees of a
-%% <code>receive_expr</code> node.
+%% `receive_expr' node.
%%
%% @see receive_expr/3
@@ -5671,15 +5226,12 @@ receive_expr_clauses(Node) ->
%% =====================================================================
-%% @spec receive_expr_timeout(Node::syntaxTree()) -> Timeout
-%% Timeout = none | syntaxTree()
-%%
-%% @doc Returns the timeout subtree of a <code>receive_expr</code> node,
-%% if any. If <code>Node</code> represents "<code>receive <em>C1</em>;
-%% ...; <em>Cn</em> end</code>", <code>none</code> is returned.
-%% Otherwise, if <code>Node</code> represents "<code>receive
+%% @doc Returns the timeout subtree of a `receive_expr' node,
+%% if any. If `Node' represents "<code>receive <em>C1</em>;
+%% ...; <em>Cn</em> end</code>", `none' is returned.
+%% Otherwise, if `Node' represents "<code>receive
%% <em>C1</em>; ...; <em>Cn</em> after <em>Timeout</em> -> ... end</code>",
-%% <code>Timeout</code> is returned.
+%% `Timeout' is returned.
%%
%% @see receive_expr/3
@@ -5697,10 +5249,8 @@ receive_expr_timeout(Node) ->
%% =====================================================================
-%% @spec receive_expr_action(Node::syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of action body subtrees of a
-%% <code>receive_expr</code> node. If <code>Node</code> represents
+%% `receive_expr' node. If `Node' represents
%% "<code>receive <em>C1</em>; ...; <em>Cn</em> end</code>", this is the
%% empty list.
%%
@@ -5720,8 +5270,6 @@ receive_expr_action(Node) ->
%% =====================================================================
-%% @spec try_expr(Body::syntaxTree(), Handlers::[syntaxTree()]) ->
-%% syntaxTree()
%% @equiv try_expr(Body, [], Handlers)
-spec try_expr([syntaxTree()], [syntaxTree()]) -> syntaxTree().
@@ -5731,8 +5279,6 @@ try_expr(Body, Handlers) ->
%% =====================================================================
-%% @spec try_expr(Body::syntaxTree(), Clauses::[syntaxTree()],
-%% Handlers::[syntaxTree()]) -> syntaxTree()
%% @equiv try_expr(Body, Clauses, Handlers, [])
-spec try_expr([syntaxTree()], [syntaxTree()], [syntaxTree()]) -> syntaxTree().
@@ -5742,8 +5288,6 @@ try_expr(Body, Clauses, Handlers) ->
%% =====================================================================
-%% @spec try_after_expr(Body::syntaxTree(), After::[syntaxTree()]) ->
-%% syntaxTree()
%% @equiv try_expr(Body, [], [], After)
-spec try_after_expr([syntaxTree()], [syntaxTree()]) -> syntaxTree().
@@ -5753,30 +5297,26 @@ try_after_expr(Body, After) ->
%% =====================================================================
-%% @spec try_expr(Body::[syntaxTree()], Clauses::[syntaxTree()],
-%% Handlers::[syntaxTree()], After::[syntaxTree()]) ->
-%% syntaxTree()
-%%
-%% @doc Creates an abstract try-expression. If <code>Body</code> is
-%% <code>[B1, ..., Bn]</code>, <code>Clauses</code> is <code>[C1, ...,
-%% Cj]</code>, <code>Handlers</code> is <code>[H1, ..., Hk]</code>, and
-%% <code>After</code> is <code>[A1, ..., Am]</code>, the result
+%% @doc Creates an abstract try-expression. If `Body' is
+%% `[B1, ..., Bn]', `Clauses' is `[C1, ...,
+%% Cj]', `Handlers' is `[H1, ..., Hk]', and
+%% `After' is `[A1, ..., Am]', the result
%% represents "<code>try <em>B1</em>, ..., <em>Bn</em> of <em>C1</em>;
%% ...; <em>Cj</em> catch <em>H1</em>; ...; <em>Hk</em> after
%% <em>A1</em>, ..., <em>Am</em> end</code>". More exactly, if each
-%% <code>Ci</code> represents "<code>(<em>CPi</em>) <em>CGi</em> ->
-%% <em>CBi</em></code>", and each <code>Hi</code> represents
+%% `Ci' represents "<code>(<em>CPi</em>) <em>CGi</em> ->
+%% <em>CBi</em></code>", and each `Hi' represents
%% "<code>(<em>HPi</em>) <em>HGi</em> -> <em>HBi</em></code>", then the
%% result represents "<code>try <em>B1</em>, ..., <em>Bn</em> of
%% <em>CP1</em> <em>CG1</em> -> <em>CB1</em>; ...; <em>CPj</em>
%% <em>CGj</em> -> <em>CBj</em> catch <em>HP1</em> <em>HG1</em> ->
%% <em>HB1</em>; ...; <em>HPk</em> <em>HGk</em> -> <em>HBk</em> after
-%% <em>A1</em>, ..., <em>Am</em> end</code>"; cf.
-%% <code>case_expr/2</code>. If <code>Clauses</code> is the empty list,
-%% the <code>of ...</code> section is left out. If <code>After</code> is
-%% the empty list, the <code>after ...</code> section is left out. If
-%% <code>Handlers</code> is the empty list, and <code>After</code> is
-%% nonempty, the <code>catch ...</code> section is left out.
+%% <em>A1</em>, ..., <em>Am</em> end</code>"; see
+%% {@link case_expr/2}. If `Clauses' is the empty list,
+%% the `of ...' section is left out. If `After' is
+%% the empty list, the `after ...' section is left out. If
+%% `Handlers' is the empty list, and `After' is
+%% nonempty, the `catch ...' section is left out.
%%
%% @see try_expr_body/1
%% @see try_expr_clauses/1
@@ -5834,10 +5374,7 @@ revert_try_expr(Node) ->
%% =====================================================================
-%% @spec try_expr_body(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of body subtrees of a <code>try_expr</code>
-%% node.
+%% @doc Returns the list of body subtrees of a `try_expr' node.
%%
%% @see try_expr/4
@@ -5853,10 +5390,8 @@ try_expr_body(Node) ->
%% =====================================================================
-%% @spec try_expr_clauses(Node::syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of case-clause subtrees of a
-%% <code>try_expr</code> node. If <code>Node</code> represents
+%% `try_expr' node. If `Node' represents
%% "<code>try <em>Body</em> catch <em>H1</em>; ...; <em>Hn</em>
%% end</code>", the result is the empty list.
%%
@@ -5874,10 +5409,8 @@ try_expr_clauses(Node) ->
%% =====================================================================
-%% @spec try_expr_handlers(syntaxTree()) -> [syntaxTree()]
-%%
%% @doc Returns the list of handler-clause subtrees of a
-%% <code>try_expr</code> node.
+%% `try_expr' node.
%%
%% @see try_expr/4
@@ -5893,10 +5426,7 @@ try_expr_handlers(Node) ->
%% =====================================================================
-%% @spec try_expr_after(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of "after" subtrees of a <code>try_expr</code>
-%% node.
+%% @doc Returns the list of "after" subtrees of a `try_expr' node.
%%
%% @see try_expr/4
@@ -5912,9 +5442,6 @@ try_expr_after(Node) ->
%% =====================================================================
-%% @spec class_qualifier(Class::syntaxTree(), Body::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract class qualifier. The result represents
%% "<code><em>Class</em>:<em>Body</em></code>".
%%
@@ -5937,10 +5464,8 @@ class_qualifier(Class, Body) ->
%% =====================================================================
-%% @spec class_qualifier_argument(syntaxTree()) -> syntaxTree()
-%%
%% @doc Returns the argument (the class) subtree of a
-%% <code>class_qualifier</code> node.
+%% `class_qualifier' node.
%%
%% @see class_qualifier/2
@@ -5951,9 +5476,7 @@ class_qualifier_argument(Node) ->
%% =====================================================================
-%% @spec class_qualifier_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>class_qualifier</code> node.
+%% @doc Returns the body subtree of a `class_qualifier' node.
%%
%% @see class_qualifier/2
@@ -5964,13 +5487,10 @@ class_qualifier_body(Node) ->
%% =====================================================================
-%% @spec implicit_fun(Name::syntaxTree(), Arity::syntaxTree()) ->
-%% syntaxTree()
-%%
%% @doc Creates an abstract "implicit fun" expression. If
-%% <code>Arity</code> is <code>none</code>, this is equivalent to
-%% <code>implicit_fun(Name)</code>, otherwise it is equivalent to
-%% <code>implicit_fun(arity_qualifier(Name, Arity))</code>.
+%% `Arity' is `none', this is equivalent to
+%% `implicit_fun(Name)', otherwise it is equivalent to
+%% `implicit_fun(arity_qualifier(Name, Arity))'.
%%
%% (This is a utility function.)
%%
@@ -5986,14 +5506,11 @@ implicit_fun(Name, Arity) ->
%% =====================================================================
-%% @spec implicit_fun(Module::syntaxTree(), Name::syntaxTree(),
-%% Arity::syntaxTree()) -> syntaxTree()
-%%
%% @doc Creates an abstract module-qualified "implicit fun" expression.
-%% If <code>Module</code> is <code>none</code>, this is equivalent to
-%% <code>implicit_fun(Name, Arity)</code>, otherwise it is equivalent to
-%% <code>implicit_fun(module_qualifier(Module, arity_qualifier(Name,
-%% Arity))</code>.
+%% If `Module' is `none', this is equivalent to
+%% `implicit_fun(Name, Arity)', otherwise it is equivalent to
+%% `implicit_fun(module_qualifier(Module, arity_qualifier(Name,
+%% Arity))'.
%%
%% (This is a utility function.)
%%
@@ -6010,10 +5527,8 @@ implicit_fun(Module, Name, Arity) ->
%% =====================================================================
-%% @spec implicit_fun(Name::syntaxTree()) -> syntaxTree()
-%%
%% @doc Creates an abstract "implicit fun" expression. The result
-%% represents "<code>fun <em>Name</em></code>". <code>Name</code> should
+%% represents "<code>fun <em>Name</em></code>". `Name' should
%% represent either <code><em>F</em>/<em>A</em></code> or
%% <code><em>M</em>:<em>F</em>/<em>A</em></code>
%%
@@ -6072,15 +5587,13 @@ revert_implicit_fun(Node) ->
%% =====================================================================
-%% @spec implicit_fun_name(Node::syntaxTree()) -> syntaxTree()
+%% @doc Returns the name subtree of an `implicit_fun' node.
%%
-%% @doc Returns the name subtree of an <code>implicit_fun</code> node.
-%%
-%% <p>Note: if <code>Node</code> represents "<code>fun
+%% Note: if `Node' represents "<code>fun
%% <em>N</em>/<em>A</em></code>" or "<code>fun
%% <em>M</em>:<em>N</em>/<em>A</em></code>", then the result is the
%% subtree representing "<code><em>N</em>/<em>A</em></code>" or
-%% "<code><em>M</em>:<em>N</em>/<em>A</em></code>", respectively.</p>
+%% "<code><em>M</em>:<em>N</em>/<em>A</em></code>", respectively.
%%
%% @see implicit_fun/1
%% @see arity_qualifier/2
@@ -6110,12 +5623,10 @@ implicit_fun_name(Node) ->
%% =====================================================================
-%% @spec fun_expr(Clauses::[syntaxTree()]) -> syntaxTree()
-%%
-%% @doc Creates an abstract fun-expression. If <code>Clauses</code> is
-%% <code>[C1, ..., Cn]</code>, the result represents "<code>fun
+%% @doc Creates an abstract fun-expression. If `Clauses' is
+%% `[C1, ..., Cn]', the result represents "<code>fun
%% <em>C1</em>; ...; <em>Cn</em> end</code>". More exactly, if each
-%% <code>Ci</code> represents "<code>(<em>Pi1</em>, ..., <em>Pim</em>)
+%% `Ci' represents "<code>(<em>Pi1</em>, ..., <em>Pim</em>)
%% <em>Gi</em> -> <em>Bi</em></code>", then the result represents
%% "<code>fun (<em>P11</em>, ..., <em>P1m</em>) <em>G1</em> ->
%% <em>B1</em>; ...; (<em>Pn1</em>, ..., <em>Pnm</em>) <em>Gn</em> ->
@@ -6152,10 +5663,7 @@ revert_fun_expr(Node) ->
%% =====================================================================
-%% @spec fun_expr_clauses(syntaxTree()) -> [syntaxTree()]
-%%
-%% @doc Returns the list of clause subtrees of a <code>fun_expr</code>
-%% node.
+%% @doc Returns the list of clause subtrees of a `fun_expr' node.
%%
%% @see fun_expr/1
@@ -6171,16 +5679,14 @@ fun_expr_clauses(Node) ->
%% =====================================================================
-%% @spec fun_expr_arity(syntaxTree()) -> integer()
-%%
-%% @doc Returns the arity of a <code>fun_expr</code> node. The result is
+%% @doc Returns the arity of a `fun_expr' node. The result is
%% the number of parameter patterns in the first clause of the
%% fun-expression; subsequent clauses are ignored.
%%
-%% <p>An exception is thrown if <code>fun_expr_clauses(Node)</code>
+%% An exception is thrown if `fun_expr_clauses(Node)'
%% returns an empty list, or if the first element of that list is not a
-%% syntax tree <code>C</code> of type <code>clause</code> such that
-%% <code>clause_patterns(C)</code> is a nonempty list.</p>
+%% syntax tree `C' of type `clause' such that
+%% `clause_patterns(C)' is a nonempty list.
%%
%% @see fun_expr/1
%% @see fun_expr_clauses/1
@@ -6194,8 +5700,6 @@ fun_expr_arity(Node) ->
%% =====================================================================
-%% @spec parentheses(Body::syntaxTree()) -> syntaxTree()
-%%
%% @doc Creates an abstract parenthesised expression. The result
%% represents "<code>(<em>Body</em>)</code>", independently of the
%% context.
@@ -6215,9 +5719,7 @@ revert_parentheses(Node) ->
%% =====================================================================
-%% @spec parentheses_body(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the body subtree of a <code>parentheses</code> node.
+%% @doc Returns the body subtree of a `parentheses' node.
%%
%% @see parentheses/1
@@ -6228,7 +5730,6 @@ parentheses_body(Node) ->
%% =====================================================================
-%% @spec macro(Name) -> syntaxTree()
%% @equiv macro(Name, none)
-spec macro(syntaxTree()) -> syntaxTree().
@@ -6238,25 +5739,22 @@ macro(Name) ->
%% =====================================================================
-%% @spec macro(Name::syntaxTree(), Arguments) -> syntaxTree()
-%% Arguments = none | [syntaxTree()]
-%%
-%% @doc Creates an abstract macro application. If <code>Arguments</code>
-%% is <code>none</code>, the result represents
-%% "<code>?<em>Name</em></code>", otherwise, if <code>Arguments</code>
-%% is <code>[A1, ..., An]</code>, the result represents
+%% @doc Creates an abstract macro application. If `Arguments'
+%% is `none', the result represents
+%% "<code>?<em>Name</em></code>", otherwise, if `Arguments'
+%% is `[A1, ..., An]', the result represents
%% "<code>?<em>Name</em>(<em>A1</em>, ..., <em>An</em>)</code>".
%%
-%% <p>Notes: if <code>Arguments</code> is the empty list, the result
+%% Notes: if `Arguments' is the empty list, the result
%% will thus represent "<code>?<em>Name</em>()</code>", including a pair
-%% of matching parentheses.</p>
+%% of matching parentheses.
%%
-%% <p>The only syntactical limitation imposed by the preprocessor on the
+%% The only syntactical limitation imposed by the preprocessor on the
%% arguments to a macro application (viewed as sequences of tokens) is
%% that they must be balanced with respect to parentheses, brackets,
-%% <code>begin ... end</code>, <code>case ... end</code>, etc. The
-%% <code>text</code> node type can be used to represent arguments which
-%% are not regular Erlang constructs.</p>
+%% `begin ... end', `case ... end', etc. The
+%% `text' node type can be used to represent arguments which
+%% are not regular Erlang constructs.
%%
%% @see macro_name/1
%% @see macro_arguments/1
@@ -6278,9 +5776,7 @@ macro(Name, Arguments) ->
%% =====================================================================
-%% @spec macro_name(syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns the name subtree of a <code>macro</code> node.
+%% @doc Returns the name subtree of a `macro' node.
%%
%% @see macro/2
@@ -6291,14 +5787,12 @@ macro_name(Node) ->
%% =====================================================================
-%% @spec macro_arguments(Node::syntaxTree()) -> none | [syntaxTree()]
-%%
-%% @doc Returns the list of argument subtrees of a <code>macro</code>
-%% node, if any. If <code>Node</code> represents
-%% "<code>?<em>Name</em></code>", <code>none</code> is returned.
-%% Otherwise, if <code>Node</code> represents
+%% @doc Returns the list of argument subtrees of a `macro'
+%% node, if any. If `Node' represents
+%% "<code>?<em>Name</em></code>", `none' is returned.
+%% Otherwise, if `Node' represents
%% "<code>?<em>Name</em>(<em>A1</em>, ..., <em>An</em>)</code>",
-%% <code>[A1, ..., An]</code> is returned.
+%% `[A1, ..., An]' is returned.
%%
%% @see macro/2
@@ -6309,15 +5803,13 @@ macro_arguments(Node) ->
%% =====================================================================
-%% @spec abstract(Term::term()) -> syntaxTree()
-%%
%% @doc Returns the syntax tree corresponding to an Erlang term.
-%% <code>Term</code> must be a literal term, i.e., one that can be
+%% `Term' must be a literal term, i.e., one that can be
%% represented as a source code literal. Thus, it may not contain a
%% process identifier, port, reference, binary or function value as a
%% subterm. The function recognises printable strings, in order to get a
%% compact and readable representation. Evaluation fails with reason
-%% <code>badarg</code> if <code>Term</code> is not a literal term.
+%% `badarg' if `Term' is not a literal term.
%%
%% @see concrete/1
%% @see is_literal/1
@@ -6367,19 +5859,17 @@ abstract_tail(H, T) ->
%% =====================================================================
-%% @spec concrete(Node::syntaxTree()) -> term()
-%%
%% @doc Returns the Erlang term represented by a syntax tree. Evaluation
-%% fails with reason <code>badarg</code> if <code>Node</code> does not
+%% fails with reason `badarg' if `Node' does not
%% represent a literal term.
%%
-%% <p>Note: Currently, the set of syntax trees which have a concrete
+%% Note: Currently, the set of syntax trees which have a concrete
%% representation is larger than the set of trees which can be built
-%% using the function <code>abstract/1</code>. An abstract character
-%% will be concretised as an integer, while <code>abstract/1</code> does
+%% using the function {@link abstract/1}. An abstract character
+%% will be concretised as an integer, while {@link abstract/1} does
%% not at present yield an abstract character for any input. (Use the
-%% <code>char/1</code> function to explicitly create an abstract
-%% character.)</p>
+%% {@link char/1} function to explicitly create an abstract
+%% character.)
%%
%% @see abstract/1
%% @see is_literal/1
@@ -6422,7 +5912,7 @@ concrete(Node) ->
{value, concrete(F), []}
end, [], true),
B;
- _ ->
+ _ ->
erlang:error({badarg, Node})
end.
@@ -6433,12 +5923,10 @@ concrete_list([]) ->
%% =====================================================================
-%% @spec is_literal(Node::syntaxTree()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if <code>Node</code> represents a
-%% literal term, otherwise <code>false</code>. This function returns
-%% <code>true</code> if and only if the value of
-%% <code>concrete(Node)</code> is defined.
+%% @doc Returns `true' if `Node' represents a
+%% literal term, otherwise `false'. This function returns
+%% `true' if and only if the value of
+%% `concrete(Node)' is defined.
%%
%% @see abstract/1
%% @see concrete/1
@@ -6469,21 +5957,19 @@ is_literal(T) ->
%% =====================================================================
-%% @spec revert(Tree::syntaxTree()) -> syntaxTree()
-%%
-%% @doc Returns an <code>erl_parse</code>-compatible representation of a
-%% syntax tree, if possible. If <code>Tree</code> represents a
+%% @doc Returns an `erl_parse'-compatible representation of a
+%% syntax tree, if possible. If `Tree' represents a
%% well-formed Erlang program or expression, the conversion should work
-%% without problems. Typically, <code>is_tree/1</code> yields
-%% <code>true</code> if conversion failed (i.e., the result is still an
-%% abstract syntax tree), and <code>false</code> otherwise.
+%% without problems. Typically, {@link is_tree/1} yields
+%% `true' if conversion failed (i.e., the result is still an
+%% abstract syntax tree), and `false' otherwise.
%%
-%% <p>The <code>is_tree/1</code> test is not completely foolproof. For a
-%% few special node types (e.g. <code>arity_qualifier</code>), if such a
+%% The {@link is_tree/1} test is not completely foolproof. For a
+%% few special node types (e.g. `arity_qualifier'), if such a
%% node occurs in a context where it is not expected, it will be left
%% unchanged as a non-reverted subtree of the result. This can only
-%% happen if <code>Tree</code> does not actually represent legal Erlang
-%% code.</p>
+%% happen if `Tree' does not actually represent legal Erlang
+%% code.
%%
%% @see revert_forms/1
%% @see //stdlib/erl_parse
@@ -6615,16 +6101,12 @@ revert_root(Node) ->
%% =====================================================================
-%% @spec revert_forms(Forms) -> [erl_parse()]
-%%
-%% Forms = syntaxTree() | [syntaxTree()]
-%%
%% @doc Reverts a sequence of Erlang source code forms. The sequence can
-%% be given either as a <code>form_list</code> syntax tree (possibly
+%% be given either as a `form_list' syntax tree (possibly
%% nested), or as a list of "program form" syntax trees. If successful,
-%% the corresponding flat list of <code>erl_parse</code>-compatible
-%% syntax trees is returned (cf. <code>revert/1</code>). If some program
-%% form could not be reverted, <code>{error, Form}</code> is thrown.
+%% the corresponding flat list of `erl_parse'-compatible
+%% syntax trees is returned (see {@link revert/1}). If some program
+%% form could not be reverted, `{error, Form}' is thrown.
%% Standalone comments in the form sequence are discarded.
%%
%% @see revert/1
@@ -6633,10 +6115,10 @@ revert_root(Node) ->
-type forms() :: syntaxTree() | [syntaxTree()].
-%% -spec revert_forms(forms()) -> [erl_parse()].
+-spec revert_forms(forms()) -> [erl_parse()].
-revert_forms(L) when is_list(L) ->
- revert_forms(form_list(L));
+revert_forms(Forms) when is_list(Forms) ->
+ revert_forms(form_list(Forms));
revert_forms(T) ->
case type(T) of
form_list ->
@@ -6673,60 +6155,54 @@ revert_forms_1([]) ->
%% =====================================================================
-%% @spec subtrees(Node::syntaxTree()) -> [[syntaxTree()]]
-%%
%% @doc Returns the grouped list of all subtrees of a syntax tree. If
-%% <code>Node</code> is a leaf node (cf. <code>is_leaf/1</code>), this
+%% `Node' is a leaf node (see {@link is_leaf/1}), this
%% is the empty list, otherwise the result is always a nonempty list,
-%% containing the lists of subtrees of <code>Node</code>, in
+%% containing the lists of subtrees of `Node', in
%% left-to-right order as they occur in the printed program text, and
%% grouped by category. Often, each group contains only a single
%% subtree.
%%
-%% <p>Depending on the type of <code>Node</code>, the size of some
+%% Depending on the type of `Node', the size of some
%% groups may be variable (e.g., the group consisting of all the
%% elements of a tuple), while others always contain the same number of
%% elements - usually exactly one (e.g., the group containing the
%% argument expression of a case-expression). Note, however, that the
%% exact structure of the returned list (for a given node type) should
%% in general not be depended upon, since it might be subject to change
-%% without notice.</p>
+%% without notice.
%%
-%% <p>The function <code>subtrees/1</code> and the constructor functions
-%% <code>make_tree/2</code> and <code>update_tree/2</code> can be a
+%% The function {@link subtrees/1} and the constructor functions
+%% {@link make_tree/2} and {@link update_tree/2} can be a
%% great help if one wants to traverse a syntax tree, visiting all its
%% subtrees, but treat nodes of the tree in a uniform way in most or all
%% cases. Using these functions makes this simple, and also assures that
%% your code is not overly sensitive to extensions of the syntax tree
%% data type, because any node types not explicitly handled by your code
-%% can be left to a default case.</p>
+%% can be left to a default case.
%%
-%% <p>For example:
-%% <pre>
-%% postorder(F, Tree) ->
+%% For example:
+%% ```postorder(F, Tree) ->
%% F(case subtrees(Tree) of
%% [] -> Tree;
%% List -> update_tree(Tree,
%% [[postorder(F, Subtree)
%% || Subtree &lt;- Group]
%% || Group &lt;- List])
-%% end).
-%% </pre>
-%% maps the function <code>F</code> on <code>Tree</code> and all its
+%% end).'''
+%% maps the function `F' on `Tree' and all its
%% subtrees, doing a post-order traversal of the syntax tree. (Note the
-%% use of <code>update_tree/2</code> to preserve node attributes.) For a
+%% use of {@link update_tree/2} to preserve node attributes.) For a
%% simple function like:
-%% <pre>
-%% f(Node) ->
+%% ```f(Node) ->
%% case type(Node) of
%% atom -> atom("a_" ++ atom_name(Node));
%% _ -> Node
-%% end.
-%% </pre>
-%% the call <code>postorder(fun f/1, Tree)</code> will yield a new
-%% representation of <code>Tree</code> in which all atom names have been
+%% end.'''
+%% the call `postorder(fun f/1, Tree)' will yield a new
+%% representation of `Tree' in which all atom names have been
%% extended with the prefix "a_", but nothing else (including comments,
-%% annotations and line numbers) has been changed.</p>
+%% annotations and line numbers) has been changed.
%%
%% @see make_tree/2
%% @see type/1
@@ -6896,12 +6372,9 @@ subtrees(T) ->
%% =====================================================================
-%% @spec update_tree(Node::syntaxTree(), Groups::[[syntaxTree()]]) ->
-%% syntaxTree()
-%%
%% @doc Creates a syntax tree with the same type and attributes as the
-%% given tree. This is equivalent to <code>copy_attrs(Node,
-%% make_tree(type(Node), Groups))</code>.
+%% given tree. This is equivalent to `copy_attrs(Node,
+%% make_tree(type(Node), Groups))'.
%%
%% @see make_tree/2
%% @see copy_attrs/2
@@ -6914,23 +6387,20 @@ update_tree(Node, Groups) ->
%% =====================================================================
-%% @spec make_tree(Type::atom(), Groups::[[syntaxTree()]]) ->
-%% syntaxTree()
-%%
%% @doc Creates a syntax tree with the given type and subtrees.
-%% <code>Type</code> must be a node type name (cf. <code>type/1</code>)
-%% that does not denote a leaf node type (cf. <code>is_leaf/1</code>).
-%% <code>Groups</code> must be a <em>nonempty</em> list of groups of
+%% `Type' must be a node type name (see {@link type/1})
+%% that does not denote a leaf node type (see {@link is_leaf/1}).
+%% `Groups' must be a <em>nonempty</em> list of groups of
%% syntax trees, representing the subtrees of a node of the given type,
%% in left-to-right order as they would occur in the printed program
-%% text, grouped by category as done by <code>subtrees/1</code>.
+%% text, grouped by category as done by {@link subtrees/1}.
%%
-%% <p>The result of <code>copy_attrs(Node, make_tree(type(Node),
-%% subtrees(Node)))</code> (cf. <code>update_tree/2</code>) represents
-%% the same source code text as the original <code>Node</code>, assuming
-%% that <code>subtrees(Node)</code> yields a nonempty list. However, it
+%% The result of `copy_attrs(Node, make_tree(type(Node),
+%% subtrees(Node)))' (see {@link update_tree/2}) represents
+%% the same source code text as the original `Node', assuming
+%% that `subtrees(Node)' yields a nonempty list. However, it
%% does not necessarily have the same data representation as
-%% <code>Node</code>.</p>
+%% `Node'.
%%
%% @see update_tree/2
%% @see subtrees/1
@@ -6995,42 +6465,40 @@ make_tree(tuple, [E]) -> tuple(E).
%% =====================================================================
-%% @spec meta(Tree::syntaxTree()) -> syntaxTree()
-%%
%% @doc Creates a meta-representation of a syntax tree. The result
%% represents an Erlang expression "<code><em>MetaTree</em></code>"
%% which, if evaluated, will yield a new syntax tree representing the
-%% same source code text as <code>Tree</code> (although the actual data
+%% same source code text as `Tree' (although the actual data
%% representation may be different). The expression represented by
-%% <code>MetaTree</code> is <em>implementation independent</em> with
+%% `MetaTree' is <em>implementation independent</em> with
%% regard to the data structures used by the abstract syntax tree
-%% implementation. Comments attached to nodes of <code>Tree</code> will
+%% implementation. Comments attached to nodes of `Tree' will
%% be preserved, but other attributes are lost.
%%
-%% <p>Any node in <code>Tree</code> whose node type is
-%% <code>variable</code> (cf. <code>type/1</code>), and whose list of
-%% annotations (cf. <code>get_ann/1</code>) contains the atom
-%% <code>meta_var</code>, will remain unchanged in the resulting tree,
-%% except that exactly one occurrence of <code>meta_var</code> is
-%% removed from its annotation list.</p>
+%% Any node in `Tree' whose node type is
+%% `variable' (see {@link type/1}), and whose list of
+%% annotations (see {@link get_ann/1}) contains the atom
+%% `meta_var', will remain unchanged in the resulting tree,
+%% except that exactly one occurrence of `meta_var' is
+%% removed from its annotation list.
%%
-%% <p>The main use of the function <code>meta/1</code> is to transform a
-%% data structure <code>Tree</code>, which represents a piece of program
+%% The main use of the function `meta/1' is to transform a
+%% data structure `Tree', which represents a piece of program
%% code, into a form that is <em>representation independent when
-%% printed</em>. E.g., suppose <code>Tree</code> represents a variable
-%% named "V". Then (assuming a function <code>print/1</code> for
-%% printing syntax trees), evaluating <code>print(abstract(Tree))</code>
-%% - simply using <code>abstract/1</code> to map the actual data
+%% printed</em>. E.g., suppose `Tree' represents a variable
+%% named "V". Then (assuming a function `print/1' for
+%% printing syntax trees), evaluating `print(abstract(Tree))'
+%% - simply using {@link abstract/1} to map the actual data
%% structure onto a syntax tree representation - would output a string
-%% that might look something like "<code>{tree, variable, ..., "V",
-%% ...}</code>", which is obviously dependent on the implementation of
+%% that might look something like "`{tree, variable, ..., "V",
+%% ...}'", which is obviously dependent on the implementation of
%% the abstract syntax trees. This could e.g. be useful for caching a
%% syntax tree in a file. However, in some situations like in a program
%% generator generator (with two "generator"), it may be unacceptable.
-%% Using <code>print(meta(Tree))</code> instead would output a
+%% Using `print(meta(Tree))' instead would output a
%% <em>representation independent</em> syntax tree generating
%% expression; in the above case, something like
-%% "<code>erl_syntax:variable("V")</code>".</p>
+%% "`erl_syntax:variable("V")'".
%%
%% @see abstract/1
%% @see type/1
@@ -7161,60 +6629,56 @@ meta_call(F, As) ->
%% =====================================================================
-%% @spec tree(Type) -> syntaxTree()
%% @equiv tree(Type, [])
--spec tree(atom()) -> syntaxTree().
+-spec tree(atom()) -> #tree{}.
tree(Type) ->
tree(Type, []).
%% =====================================================================
-%% @spec tree(Type::atom(), Data::term()) -> syntaxTree()
-%%
%% @doc <em>For special purposes only</em>. Creates an abstract syntax
-%% tree node with type tag <code>Type</code> and associated data
-%% <code>Data</code>.
+%% tree node with type tag `Type' and associated data
+%% `Data'.
%%
-%% <p>This function and the related <code>is_tree/1</code> and
-%% <code>data/1</code> provide a uniform way to extend the set of
-%% <code>erl_parse</code> node types. The associated data is any term,
-%% whose format may depend on the type tag.</p>
+%% This function and the related {@link is_tree/1} and
+%% {@link data/1} provide a uniform way to extend the set of
+%% `erl_parse' node types. The associated data is any term,
+%% whose format may depend on the type tag.
%%
-%% <h4>Notes:</h4>
+%% === Notes: ===
%% <ul>
%% <li>Any nodes created outside of this module must have type tags
%% distinct from those currently defined by this module; see
-%% <code>type/1</code> for a complete list.</li>
+%% {@link type/1} for a complete list.</li>
%% <li>The type tag of a syntax tree node may also be used
-%% as a primary tag by the <code>erl_parse</code> representation;
+%% as a primary tag by the `erl_parse' representation;
%% in that case, the selector functions for that node type
%% <em>must</em> handle both the abstract syntax tree and the
-%% <code>erl_parse</code> form. The function <code>type(T)</code>
+%% `erl_parse' form. The function `type(T)'
%% should return the correct type tag regardless of the
-%% representation of <code>T</code>, so that the user sees no
-%% difference between <code>erl_syntax</code> and
-%% <code>erl_parse</code> nodes.</li>
+%% representation of `T', so that the user sees no
+%% difference between `erl_syntax' and
+%% `erl_parse' nodes.</li>
%% </ul>
+%%
%% @see is_tree/1
%% @see data/1
%% @see type/1
--spec tree(atom(), term()) -> syntaxTree().
+-spec tree(atom(), term()) -> #tree{}.
tree(Type, Data) ->
#tree{type = Type, data = Data}.
%% =====================================================================
-%% @spec is_tree(Tree::syntaxTree()) -> boolean()
-%%
-%% @doc <em>For special purposes only</em>. Returns <code>true</code> if
-%% <code>Tree</code> is an abstract syntax tree and <code>false</code>
+%% @doc <em>For special purposes only</em>. Returns `true' if
+%% `Tree' is an abstract syntax tree and `false'
%% otherwise.
%%
-%% <p><em>Note</em>: this function yields <code>false</code> for all
-%% "old-style" <code>erl_parse</code>-compatible "parse trees".</p>
+%% <em>Note</em>: this function yields `false' for all
+%% "old-style" `erl_parse'-compatible "parse trees".
%%
%% @see tree/2
@@ -7227,12 +6691,10 @@ is_tree(_) ->
%% =====================================================================
-%% @spec data(Tree::syntaxTree()) -> term()
-%%
%% @doc <em>For special purposes only</em>. Returns the associated data
%% of a syntax tree node. Evaluation fails with reason
-%% <code>badarg</code> if <code>is_tree(Node)</code> does not yield
-%% <code>true</code>.
+%% `badarg' if `is_tree(Node)' does not yield
+%% `true'.
%%
%% @see tree/2
@@ -7248,26 +6710,19 @@ data(T) -> erlang:error({badarg, T}).
%% =====================================================================
-%% @spec wrap(Node::erl_parse()) -> syntaxTree()
-%%
-%% @type erl_parse() = erl_parse:parse_tree(). The "parse tree"
-%% representation built by the Erlang standard library parser
-%% <code>erl_parse</code>. This is a subset of the
-%% <a href="#type-syntaxTree"><code>syntaxTree</code></a> type.
-%%
-%% @doc Creates a wrapper structure around an <code>erl_parse</code>
+%% @doc Creates a wrapper structure around an `erl_parse'
%% "parse tree".
%%
-%% <p>This function and the related <code>unwrap/1</code> and
-%% <code>is_wrapper/1</code> provide a uniform way to attach arbitrary
-%% information to an <code>erl_parse</code> tree. Some information about
+%% This function and the related {@link unwrap/1} and
+%% {@link is_wrapper/1} provide a uniform way to attach arbitrary
+%% information to an `erl_parse' tree. Some information about
%% the encapsuled tree may be cached in the wrapper, such as the node
%% type. All functions on syntax trees must behave so that the user sees
-%% no difference between wrapped and non-wrapped <code>erl_parse</code>
+%% no difference between wrapped and non-wrapped `erl_parse'
%% trees. <em>Attaching a wrapper onto another wrapper structure is an
-%% error</em>.</p>
+%% error</em>.
-%%-spec wrap(erl_parse:parse_tree()) -> syntaxTree().
+-spec wrap(erl_parse()) -> #wrapper{}.
wrap(Node) ->
%% We assume that Node is an old-school `erl_parse' tree.
@@ -7276,24 +6731,20 @@ wrap(Node) ->
%% =====================================================================
-%% @spec unwrap(Node::syntaxTree()) -> syntaxTree()
-%%
-%% @doc Removes any wrapper structure, if present. If <code>Node</code>
+%% @doc Removes any wrapper structure, if present. If `Node'
%% is a wrapper structure, this function returns the wrapped
-%% <code>erl_parse</code> tree; otherwise it returns <code>Node</code>
+%% `erl_parse' tree; otherwise it returns `Node'
%% itself.
--spec unwrap(syntaxTree()) -> syntaxTree().
+-spec unwrap(syntaxTree()) -> #tree{} | erl_parse().
unwrap(#wrapper{tree = Node}) -> Node;
unwrap(Node) -> Node. % This could also be a new-form node.
%% =====================================================================
-%% @spec is_wrapper(Term::term()) -> boolean()
-%%
-%% @doc Returns <code>true</code> if the argument is a wrapper
-%% structure, otherwise <code>false</code>.
+%% @doc Returns `true' if the argument is a wrapper
+%% structure, otherwise `false'.
-ifndef(NO_UNUSED).
-spec is_wrapper(term()) -> boolean().
diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl
index 97dfbfd7cd..36cd35f15d 100644
--- a/lib/syntax_tools/src/erl_syntax_lib.erl
+++ b/lib/syntax_tools/src/erl_syntax_lib.erl
@@ -14,10 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @copyright 1997-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl
index 09efc9c392..59cf6c0a92 100644
--- a/lib/syntax_tools/src/erl_tidy.erl
+++ b/lib/syntax_tools/src/erl_tidy.erl
@@ -14,10 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @copyright 1999-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl
index aa933eb54b..37e561cbbe 100644
--- a/lib/syntax_tools/src/igor.erl
+++ b/lib/syntax_tools/src/igor.erl
@@ -14,10 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @copyright 1998-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
diff --git a/lib/syntax_tools/src/prettypr.erl b/lib/syntax_tools/src/prettypr.erl
index c13fa30998..1b5ba6b05a 100644
--- a/lib/syntax_tools/src/prettypr.erl
+++ b/lib/syntax_tools/src/prettypr.erl
@@ -14,10 +14,8 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @copyright 2000-2006 Richard Carlsson
-%% @author Richard Carlsson <[email protected]>
+%% @author Richard Carlsson <[email protected]>
%% @end
%% =====================================================================
diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk
index 2b9a08e192..8f774c5d75 100644
--- a/lib/syntax_tools/vsn.mk
+++ b/lib/syntax_tools/vsn.mk
@@ -1 +1 @@
-SYNTAX_TOOLS_VSN = 1.6.8
+SYNTAX_TOOLS_VSN = 1.6.9
diff --git a/lib/test_server/doc/src/Makefile b/lib/test_server/doc/src/Makefile
index 3ce549f0e1..8c7fa99886 100644
--- a/lib/test_server/doc/src/Makefile
+++ b/lib/test_server/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2012. 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/lib/test_server/doc/src/notes.xml b/lib/test_server/doc/src/notes.xml
index 3701066e56..3b109193a0 100644
--- a/lib/test_server/doc/src/notes.xml
+++ b/lib/test_server/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,78 @@
<file>notes.xml</file>
</header>
+<section><title>Test_Server 3.5.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The documentation has been updated with the latest
+ changes for the test_server_ctrl:report/2 function.</p>
+ <p>
+ Own Id: OTP-10086 Aux Id: seq12066 </p>
+ </item>
+ <item>
+ <p>
+ The ct:get_status/0 function failed to report status if a
+ parallel test case group was running at the time of the
+ call. This has been fixed and the return value for the
+ function has been updated. Please see the ct reference
+ manual for details.</p>
+ <p>
+ Own Id: OTP-10172</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ It is now possible to sort the HTML tables by clicking on
+ the header elements. In order to reset a sorted table,
+ the browser window should simply be refreshed. This
+ feature requires that the browser supports javascript,
+ and has javascript execution enabled. If the 'ct_run
+ -basic_html' flag is used, no javascript code is included
+ in the generated HTML code.</p>
+ <p>
+ Own Id: OTP-9896 Aux Id: seq12034, OTP-9835 </p>
+ </item>
+ <item>
+ <p>
+ Verbosity levels for log printouts has been added. This
+ makes it possible to specify preferred verbosity for
+ different categories of log printouts, as well as general
+ printouts (such as standard IO), to allow control over
+ which strings get printed and which get ignored. New
+ versions of the Common Test logging functions, ct:log,
+ ct:pal and ct:print, have been introduced, with a new
+ Importance argument added. The Importance value is
+ compared to the verbosity level at runtime. More
+ information can be found in the chapter about Logging in
+ the Common Test User's Guide.</p>
+ <p>
+ Own Id: OTP-10067 Aux Id: seq12050 </p>
+ </item>
+ <item>
+ <p>
+ The Erlang/OTP test runner ts has been extended to allow
+ cross compilation of test suites. To cross compile the
+ test suites first follow the normal cross compilation
+ procedures and release the tests on the build host. Then
+ install ts using an xcomp specification file and compile
+ test suites using ts:compile_testcases/0. For more
+ details see $ERL_TOP/xcomp/README.md.</p>
+ <p>
+ Own Id: OTP-10074</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Test_Server 3.5.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/test_server/doc/src/test_server_ctrl.xml b/lib/test_server/doc/src/test_server_ctrl.xml
index 9028a67ecb..41bc0bcc75 100644
--- a/lib/test_server/doc/src/test_server_ctrl.xml
+++ b/lib/test_server/doc/src/test_server_ctrl.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -769,11 +769,13 @@ Optional, if not given the test server controller node
constantly updated. The following can be reported:
</p>
<p><c>What = tests_start, Data = {Name,NumCases}</c><br></br>
+ <c>What = loginfo, Data = [{topdir,TestRootDir},{rundir,CurrLogDir}]</c><br></br>
<c>What = tests_done, Data = {Ok,Failed,{UserSkipped,AutoSkipped}}</c><br></br>
- <c>What = tc_start, Data = {Mod,Func}</c><br></br>
+ <c>What = tc_start, Data = {{Mod,Func},TCLogFile}</c><br></br>
<c>What = tc_done, Data = {Mod,Func,Result}</c><br></br>
<c>What = tc_user_skip, Data = {Mod,Func,Comment}</c><br></br>
- <c>What = tc_auto_skip, Data = {Mod,Func,Comment}</c></p>
+ <c>What = tc_auto_skip, Data = {Mod,Func,Comment}</c><br></br>
+ <c>What = framework_error, Data = {{FWMod,FWFunc},Error}</c></p>
</desc>
</func>
<func>
diff --git a/lib/test_server/doc/src/ts.xml b/lib/test_server/doc/src/ts.xml
index f9b48d8372..4a2c536e96 100644
--- a/lib/test_server/doc/src/ts.xml
+++ b/lib/test_server/doc/src/ts.xml
@@ -85,8 +85,7 @@
<p><c>ts:install/1</c> or <c>ts:install/2</c> is used if the
target platform is different from the controller host, i.e. if
you run on "remote target" or if special options are required
- for your system. VxWorks is currently supported
- as remote target platform.
+ for your system.
</p>
<p>See the reference manual for detailed information about
<c>ts:install/0/1/2</c>.
@@ -249,9 +248,8 @@
<p>Installs and configures the Test Server Framework for
running test suites. If a remote host is to be used, the
<c>TargetSystem</c> argument must be given so that "cross
- installation" can be done. This should be used for testing on
- VxWorks. Installation is required for any of the
- functions in <c>ts</c> to work.
+ installation" can be done. Installation is required for
+ any of the functions in <c>ts</c> to work.
</p>
<p>Opts may be one or more of
</p>
@@ -500,29 +498,6 @@ This option is mandatory for remote targets
</desc>
</func>
<func>
- <name>index() -> ok | {error, Reason}</name>
- <fsummary>Updates local index page</fsummary>
- <type>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>This function updates the local index page. This can be
- useful if a previous test run was not completed and the index
- is incomplete.</p>
- </desc>
- </func>
- <func>
- <name>clean() -> ok</name>
- <name>clean(all) -> ok</name>
- <fsummary>Cleans up the log directories created when running tests. </fsummary>
- <desc>
- <p>This function cleans up log directories created when
- running test cases. <c>clean/0</c> cleans up all but the last
- run of each application. <c>clean/1</c> cleans up all test
- runs found.</p>
- </desc>
- </func>
- <func>
<name>estone() -> ok | {error, Reason}</name>
<name>estone(Opts) -> ok</name>
<fsummary>Runs the EStone test</fsummary>
diff --git a/lib/test_server/src/Makefile b/lib/test_server/src/Makefile
index a75855eaab..bb0b4e55b8 100644
--- a/lib/test_server/src/Makefile
+++ b/lib/test_server/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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
@@ -44,20 +44,18 @@ MODULES= test_server_ctrl \
test_server \
test_server_sup \
test_server_h \
- erl2html2 \
- vxworks_client
+ erl2html2
TS_MODULES= \
ts \
ts_run \
- ts_reports \
ts_lib \
ts_make \
ts_erl_config \
ts_autoconf_win32 \
- ts_autoconf_vxworks \
ts_install \
- ts_install_cth
+ ts_install_cth \
+ ts_benchmark
TARGET_MODULES= $(MODULES:%=$(EBIN)/%)
TS_TARGET_MODULES= $(TS_MODULES:%=$(EBIN)/%)
diff --git a/lib/test_server/src/configure.in b/lib/test_server/src/configure.in
index 77bc993ccd..785bab395c 100644
--- a/lib/test_server/src/configure.in
+++ b/lib/test_server/src/configure.in
@@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script for Erlang.
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1997-2011. All Rights Reserved.
+dnl Copyright Ericsson AB 1997-2012. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl
index 6e94e4861a..8beed9bd3e 100644
--- a/lib/test_server/src/test_server.erl
+++ b/lib/test_server/src/test_server.erl
@@ -35,6 +35,7 @@
-export([fail/0,fail/1,format/1,format/2,format/3]).
-export([capture_start/0,capture_stop/0,capture_get/0]).
-export([messages_get/0]).
+-export([permit_io/2]).
-export([hours/1,minutes/1,seconds/1,sleep/1,adjusted_sleep/1,timecall/3]).
-export([timetrap_scale_factor/0,timetrap/1,get_timetrap_info/0,
timetrap_cancel/1,timetrap_cancel/0]).
@@ -49,7 +50,7 @@
-export([run_on_shielded_node/2]).
-export([is_cover/0,is_debug/0,is_commercial/0]).
--export([break/1,continue/0]).
+-export([break/1,break/2,break/3,continue/0,continue/1]).
%%% DEBUGGER INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-export([purify_new_leaks/0, purify_format/2, purify_new_fds_inuse/0,
@@ -523,7 +524,7 @@ stick_all_sticky(Node,Sticky) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% run_test_case_apply(Mod,Func,Args,Name,RunInit,TimetrapData) ->
+%% run_test_case_apply(Mod,Func,Args,Name,RunInit,TimetrapData,RejectIoReqs) ->
%% {Time,Value,Loc,Opts,Comment} | {died,Reason,unknown,Comment}
%%
%% Time = float() (seconds)
@@ -558,8 +559,12 @@ stick_all_sticky(Node,Sticky) ->
%% ScaleTimetrap indicates if test_server should attemp to automatically
%% compensate timetraps for runtime delays introduced by e.g. tools like
%% cover.
+%%
+%% RejectIoReqs (bool) is information about whether printouts to stdout
+%% should be visible in the minor log file or not.
-run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,TimetrapData}) ->
+run_test_case_apply({CaseNum,Mod,Func,Args,Name,
+ RunInit,TimetrapData,RejectIoReqs}) ->
purify_format("Test case #~w ~w:~w/1", [CaseNum, Mod, Func]),
case os:getenv("TS_RUN_VALGRIND") of
false ->
@@ -570,17 +575,19 @@ run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,TimetrapData}) ->
end,
test_server_h:testcase({Mod,Func,1}),
ProcBef = erlang:system_info(process_count),
- Result = run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData),
+ Result = run_test_case_apply(Mod, Func, Args, Name, RunInit,
+ TimetrapData, RejectIoReqs),
ProcAft = erlang:system_info(process_count),
purify_new_leaks(),
DetFail = get(test_server_detected_fail),
{Result,DetFail,ProcBef,ProcAft}.
-run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) ->
+run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData, RejectIoReqs) ->
case get(test_server_job_dir) of
undefined ->
%% i'm a local target
- do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData);
+ do_run_test_case_apply(Mod, Func, Args, Name, RunInit,
+ TimetrapData, RejectIoReqs);
JobDir ->
%% i'm a remote target
case Args of
@@ -595,13 +602,14 @@ run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) ->
Config2 = lists:keyreplace(priv_dir, 1, Config1,
{priv_dir,TargetPrivDir}),
do_run_test_case_apply(Mod, Func, [Config2], Name, RunInit,
- TimetrapData);
+ TimetrapData, RejectIoReqs);
_other ->
do_run_test_case_apply(Mod, Func, Args, Name, RunInit,
- TimetrapData)
+ TimetrapData, RejectIoReqs)
end
end.
-do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) ->
+do_run_test_case_apply(Mod, Func, Args, Name, RunInit,
+ TimetrapData, RejectIoReqs) ->
{ok,Cwd} = file:get_cwd(),
Args2Print = case Args of
[Args1] when is_list(Args1) ->
@@ -628,7 +636,8 @@ do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) ->
end),
group_leader(OldGLeader, self()),
put(test_server_detected_fail, []),
- run_test_case_msgloop(Ref, Pid, false, false, "", undefined, starting).
+ run_test_case_msgloop(Ref, Pid, false, RejectIoReqs, false, "",
+ undefined, starting).
%% Ugly bug (pre R5A):
%% If this process (group leader of the test case) terminates before
@@ -639,7 +648,7 @@ do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) ->
%% A test case is known to have failed if it returns {'EXIT', _} tuple,
%% or sends a message {failed, File, Line} to it's group_leader
%%
-run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
+run_test_case_msgloop(Ref, Pid, CaptureStdout, RejectIoReqs, Terminate,
Comment, CurrConf, Status) ->
%% NOTE: Keep job_proxy_msgloop/0 up to date when changes
%% are made in this function!
@@ -655,7 +664,7 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
end,
receive
{test_case_initialized,Pid} ->
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,running);
Abort = {abort_current_testcase,_,_} when Status == starting ->
%% we're in init phase, must must postpone this operation
@@ -663,7 +672,7 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
%% gets killed)
self() ! Abort,
erlang:yield(),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
{abort_current_testcase,Reason,From} ->
Line = case is_process_alive(Pid) of
@@ -694,82 +703,92 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
Error1
end
end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
NewComment,CurrConf,Status);
+ {permit_io,FromPid} ->
+ put({permit_io,FromPid},true),
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
+ Comment,CurrConf,Status);
{io_request,From,ReplyAs,{put_chars,io_lib,Func,[Format,Args]}}
when is_list(Format) ->
Msg = (catch io_lib:Func(Format,Args)),
- run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs,
+ Msg,From,Func),
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
{io_request,From,ReplyAs,{put_chars,io_lib,Func,[Format,Args]}}
when is_atom(Format) ->
Msg = (catch io_lib:Func(Format,Args)),
- run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs,
+ Msg,From,Func),
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
{io_request,From,ReplyAs,{put_chars,Bytes}} ->
- run_test_case_msgloop_io(
- ReplyAs,CaptureStdout,Bytes,From,put_chars),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs,
+ Bytes,From,put_chars),
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
{io_request,From,ReplyAs,{put_chars,unicode,io_lib,Func,[Format,Args]}}
when is_list(Format) ->
Msg = unicode_to_latin1(catch io_lib:Func(Format,Args)),
- run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs,
+ Msg,From,Func),
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
{io_request,From,ReplyAs,{put_chars,latin1,io_lib,Func,[Format,Args]}}
when is_list(Format) ->
Msg = (catch io_lib:Func(Format,Args)),
- run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs,
+ Msg,From,Func),
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
{io_request,From,ReplyAs,{put_chars,unicode,io_lib,Func,[Format,Args]}}
when is_atom(Format) ->
Msg = unicode_to_latin1(catch io_lib:Func(Format,Args)),
- run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs,
+ Msg,From,Func),
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
{io_request,From,ReplyAs,{put_chars,latin1,io_lib,Func,[Format,Args]}}
when is_atom(Format) ->
Msg = (catch io_lib:Func(Format,Args)),
- run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs,
+ Msg,From,Func),
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
{io_request,From,ReplyAs,{put_chars,unicode,Bytes}} ->
- run_test_case_msgloop_io(
- ReplyAs,CaptureStdout,unicode_to_latin1(Bytes),From,put_chars),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs,
+ unicode_to_latin1(Bytes),From,put_chars),
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
{io_request,From,ReplyAs,{put_chars,latin1,Bytes}} ->
- run_test_case_msgloop_io(
- ReplyAs,CaptureStdout,Bytes,From,put_chars),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs,
+ Bytes,From,put_chars),
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
IoReq when element(1, IoReq) == io_request ->
%% something else, just pass it on
group_leader() ! IoReq,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
{structured_io,ClientPid,Msg} ->
output(Msg, ClientPid),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
{capture,NewCapture} ->
- run_test_case_msgloop(Ref,Pid,NewCapture,Terminate,
+ run_test_case_msgloop(Ref,Pid,NewCapture,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
{sync_apply,From,MFA} ->
sync_local_or_remote_apply(false,From,MFA),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
{sync_apply_proxy,Proxy,From,MFA} ->
sync_local_or_remote_apply(Proxy,From,MFA),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
{printout,Detail,Format,Args} ->
print(Detail,Format,Args),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
{comment,NewComment} ->
NewComment1 = test_server_ctrl:to_string(NewComment),
@@ -783,18 +802,20 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
Other ->
Other
end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate1,
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate1,
NewComment2,CurrConf,Status);
{read_comment,From} ->
From ! {self(),read_comment,Comment},
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
{set_curr_conf,From,NewCurrConf} ->
From ! {self(),set_curr_conf,ok},
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,NewCurrConf,Status);
{make_priv_dir,From} when CurrConf == undefined ->
- From ! {self(),make_priv_dir,{error,no_priv_dir_in_config}};
+ From ! {self(),make_priv_dir,{error,no_priv_dir_in_config}},
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
+ Comment,CurrConf,Status);
{make_priv_dir,From} ->
Result =
case proplists:get_value(priv_dir, element(2, CurrConf)) of
@@ -811,12 +832,12 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
end
end,
From ! {self(),make_priv_dir,Result},
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
Comment,CurrConf,Status);
{'EXIT',Pid,{Ref,Time,Value,Loc,Opts}} ->
RetVal = {Time/1000000,Value,mod_loc(Loc),Opts,Comment},
- run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal},
- Comment,undefined,Status);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
+ {true,RetVal},Comment,undefined,Status);
{'EXIT',Pid,Reason} ->
case Reason of
{timetrap_timeout,TVal,Loc} ->
@@ -827,7 +848,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
spawn_fw_call(FwMod,FwFunc,CurrConf,Pid,
{framework_error,{timetrap,TVal}},
unknown,self()),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,
+ run_test_case_msgloop(Ref,Pid,
+ CaptureStdout,RejectIoReqs,
Terminate,Comment,
undefined,Status);
Loc1 ->
@@ -860,7 +882,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
Loc1,self()),
undefined
end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,
+ run_test_case_msgloop(Ref,Pid,
+ CaptureStdout,RejectIoReqs,
Terminate,Comment,
NewCurrConf,Status)
end;
@@ -877,15 +900,16 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
{timetrap_timeout,TVal},
Loc1,self())
end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
+ Terminate,Comment,CurrConf,Status);
{testcase_aborted,ErrorMsg={user_timetrap_error,_},AbortLoc} ->
%% user timetrap function caused exit
%% during start of test case
{Mod,Func} = get_mf(mod_loc(AbortLoc)),
spawn_fw_call(Mod,Func,CurrConf,Pid,
ErrorMsg,unknown,self()),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,
+ run_test_case_msgloop(Ref,Pid,
+ CaptureStdout,RejectIoReqs,
Terminate,Comment,
undefined,Status);
{testcase_aborted,AbortReason,AbortLoc} ->
@@ -896,7 +920,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
spawn_fw_call(FwMod,FwFunc,CurrConf,Pid,
{framework_error,ErrorMsg},
unknown,self()),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,
+ run_test_case_msgloop(Ref,Pid,
+ CaptureStdout,RejectIoReqs,
Terminate,Comment,
undefined,Status);
Loc1 ->
@@ -928,7 +953,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
ErrorMsg,Loc1,self()),
undefined
end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,
+ run_test_case_msgloop(Ref,Pid,
+ CaptureStdout,RejectIoReqs,
Terminate,Comment,
NewCurrConf,Status)
end;
@@ -943,14 +969,14 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
spawn_fw_call(Mod,Func,CurrConf,Pid,
testcase_aborted_or_killed,
unknown,self()),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
+ Terminate,Comment,CurrConf,Status);
{fw_error,{FwMod,FwFunc,FwError}} ->
spawn_fw_call(FwMod,FwFunc,CurrConf,Pid,
{framework_error,FwError},
unknown,self()),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
+ Terminate,Comment,CurrConf,Status);
_Other ->
%% the testcase has terminated because of Reason (e.g. an exit
%% because a linked process failed)
@@ -960,8 +986,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
end,
spawn_fw_call(Mod,Func,CurrConf,Pid,
Reason,unknown,self()),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status)
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
+ Terminate,Comment,CurrConf,Status)
end;
{EndConfPid,{call_end_conf,Data,_Result}} ->
case CurrConf of
@@ -969,11 +995,11 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
{_Mod,_Func,TCPid,TCExitReason,Loc} = Data,
spawn_fw_call(Mod,Func,CurrConf,TCPid,
TCExitReason,Loc,self()),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,undefined,Status);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
+ Terminate,Comment,undefined,Status);
_ ->
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status)
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
+ Terminate,Comment,CurrConf,Status)
end;
{_FwCallPid,fw_notify_done,{T,Value,Loc,Opts,AddToComment}} ->
%% the framework has been notified, we're finished
@@ -993,8 +1019,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
end,
{T,Value,Loc,Opts,Comment1}
end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal},
- Comment,undefined,Status);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
+ {true,RetVal},Comment,undefined,Status);
{'EXIT',_FwCallPid,{fw_notify_done,Func,Error}} ->
%% a framework function failed
CB = os:getenv("TEST_SERVER_FRAMEWORK"),
@@ -1005,13 +1031,13 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
{list_to_atom(CB),Func}
end,
RetVal = {died,{framework_error,Loc,Error},Loc,"Framework error"},
- run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal},
- Comment,undefined,Status);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
+ {true,RetVal},Comment,undefined,Status);
{failed,File,Line} ->
put(test_server_detected_fail,
[{File, Line}| get(test_server_detected_fail)]),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
+ Terminate,Comment,CurrConf,Status);
{user_timetrap,Pid,_TrapTime,StartTime,E={user_timetrap_error,_},_} ->
case update_user_timetraps(Pid, StartTime) of
@@ -1020,8 +1046,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
ignore ->
ok
end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
+ Terminate,Comment,CurrConf,Status);
{user_timetrap,Pid,TrapTime,StartTime,ElapsedTime,Scale} ->
%% a user timetrap is triggered, ignore it if new
%% timetrap has been started since
@@ -1036,49 +1062,57 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate,
ignore ->
ok
end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
+ Terminate,Comment,CurrConf,Status);
{timetrap_cancel_one,Handle,_From} ->
timetrap_cancel_one(Handle, false),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
+ Terminate,Comment,CurrConf,Status);
{timetrap_cancel_all,TCPid,_From} ->
timetrap_cancel_all(TCPid, false),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
+ Terminate,Comment,CurrConf,Status);
{get_timetrap_info,TCPid,From} ->
Info = get_timetrap_info(TCPid, false),
From ! {self(),get_timetrap_info,Info},
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
+ Terminate,Comment,CurrConf,Status);
_Other when not is_tuple(_Other) ->
%% ignore anything not generated by test server
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
+ Terminate,Comment,CurrConf,Status);
_Other when element(1, _Other) /= 'EXIT',
element(1, _Other) /= started,
element(1, _Other) /= finished,
element(1, _Other) /= print ->
%% ignore anything not generated by test server
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
- Comment,CurrConf,Status)
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
+ Terminate,Comment,CurrConf,Status)
after Timeout ->
ReturnValue
end.
-run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func) ->
+run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs,
+ Msg,From,Func) ->
case Msg of
{'EXIT',_} ->
From ! {io_reply,ReplyAs,{error,Func}};
_ ->
From ! {io_reply,ReplyAs,ok}
end,
- if CaptureStdout /= false ->
- CaptureStdout ! {captured,Msg};
- true ->
+ Proceed = if RejectIoReqs -> get({permit_io,From});
+ true -> true
+ end,
+ if Proceed ->
+ if CaptureStdout /= false ->
+ CaptureStdout ! {captured,Msg};
+ true ->
+ ok
+ end,
+ output({minor,Msg},From);
+ true ->
ok
- end,
- output({minor,Msg},From).
+ end.
output(Msg,Sender) ->
local_or_remote_apply({test_server_ctrl,output,[Msg,Sender]}).
@@ -1097,7 +1131,8 @@ call_end_conf(Mod,Func,TCPid,TCExitReason,Loc,Conf,TVal) ->
{'EXIT',Why} ->
timer:sleep(1),
group_leader() ! {printout,12,
- "WARNING! ~p:end_per_testcase(~p, ~p)"
+ "WARNING! "
+ "~p:end_per_testcase(~p, ~p)"
" crashed!\n\tReason: ~p\n",
[Mod,Func,Conf,Why]};
_ ->
@@ -1213,13 +1248,18 @@ spawn_fw_call(FwMod,FwFunc,_,_Pid,{framework_error,FwError},_,SendTo) ->
end,
spawn_link(FwCall);
-spawn_fw_call(Mod,Func,_,Pid,Error,Loc,SendTo) ->
+spawn_fw_call(Mod,Func,CurrConf,Pid,Error,Loc,SendTo) ->
+ {Mod1,Func1} =
+ case {Mod,Func,CurrConf} of
+ {undefined,undefined,{{M,F},_}} -> {M,F};
+ _ -> {Mod,Func}
+ end,
FwCall =
fun() ->
%% set group leader so that printouts/comments
%% from the framework get printed in the logs
group_leader(SendTo, self()),
- case catch fw_error_notify(Mod,Func,[],
+ case catch fw_error_notify(Mod1,Func1,[],
Error,Loc) of
{'EXIT',FwErrorNotifyErr} ->
exit({fw_notify_done,error_notification,
@@ -1228,7 +1268,7 @@ spawn_fw_call(Mod,Func,_,Pid,Error,Loc,SendTo) ->
ok
end,
Conf = [{tc_status,{failed,timetrap_timeout}}],
- case catch do_end_tc_call(Mod,Func, Loc,
+ case catch do_end_tc_call(Mod1,Func1, Loc,
{Pid,Error,[Conf]},Error) of
{'EXIT',FwEndTCErr} ->
exit({fw_notify_done,end_tc,FwEndTCErr});
@@ -1959,6 +1999,13 @@ messages_get() ->
test_server_sup:messages_get([]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% permit_io(GroupLeader, FromPid) -> ok
+%%
+%% Make sure proceeding IO from FromPid won't get rejected
+permit_io(GroupLeader, FromPid) ->
+ GroupLeader ! {permit_io,FromPid}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% sleep(Time) -> ok
%% Time = integer() | float() | infinity
%%
@@ -2061,31 +2108,40 @@ fail() ->
%% Break a test case so part of the test can be done manually.
%% Use continue/0 to continue.
break(Comment) ->
- case erase(test_server_timetraps) of
- undefined -> ok;
- List -> lists:foreach(fun({Ref,_,_}) ->
- timetrap_cancel(Ref)
- end, List)
- end,
+ break(?MODULE, Comment).
+
+break(CBM, Comment) ->
+ break(CBM, '', Comment).
+
+break(CBM, TestCase, Comment) ->
+ timetrap_cancel(),
+ {TCName,CntArg,PName} =
+ if TestCase == '' ->
+ {"", "", test_server_break_process};
+ true ->
+ Str = atom_to_list(TestCase),
+ {[32 | Str], Str,
+ list_to_atom("test_server_break_process_" ++ Str)}
+ end,
io:format(user,
"\n\n\n--- SEMIAUTOMATIC TESTING ---"
- "\nThe test case executes on process ~w"
+ "\nThe test case~s executes on process ~w"
"\n\n\n~s"
"\n\n\n-----------------------------\n\n"
- "Continue with --> test_server:continue().\n",
- [self(),Comment]),
- case whereis(test_server_break_process) of
+ "Continue with --> ~w:continue(~s).\n",
+ [TCName,self(),Comment,CBM,CntArg]),
+ case whereis(PName) of
undefined ->
- spawn_break_process(self());
+ spawn_break_process(self(), PName);
OldBreakProcess ->
OldBreakProcess ! cancel,
- spawn_break_process(self())
+ spawn_break_process(self(), PName)
end,
receive continue -> ok end.
-spawn_break_process(Pid) ->
+spawn_break_process(Pid, PName) ->
spawn(fun() ->
- register(test_server_break_process,self()),
+ register(PName, self()),
receive
continue -> continue(Pid);
cancel -> ok
@@ -2094,13 +2150,19 @@ spawn_break_process(Pid) ->
continue() ->
case whereis(test_server_break_process) of
- undefined ->
- ok;
- BreakProcess ->
- BreakProcess ! continue
+ undefined -> ok;
+ BreakProcess -> BreakProcess ! continue
end.
-continue(Pid) ->
+continue(TestCase) when is_atom(TestCase) ->
+ PName = list_to_atom("test_server_break_process_" ++
+ atom_to_list(TestCase)),
+ case whereis(PName) of
+ undefined -> ok;
+ BreakProcess -> BreakProcess ! continue
+ end;
+
+continue(Pid) when is_pid(Pid) ->
Pid ! continue.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2118,14 +2180,10 @@ timetrap_scale_factor() ->
{false,true} -> 2 * F0;
{false,false} -> F0
end,
- F2 = case has_superfluous_schedulers() of
+ F = case has_superfluous_schedulers() of
true -> 3*F1;
false -> F1
end,
- F = case test_server_sup:get_os_family() of
- vxworks -> 5 * F2;
- _ -> F2
- end,
case test_server:is_cover() of
true -> 10 * F;
false -> F
diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl
index 5ed296d215..88d86285d5 100644
--- a/lib/test_server/src/test_server_ctrl.erl
+++ b/lib/test_server/src/test_server_ctrl.erl
@@ -162,7 +162,7 @@
-export([jobs/0, run_test/1, wait_finish/0, idle_notify/1,
abort_current_testcase/1, abort/0]).
-export([start_get_totals/1, stop_get_totals/0]).
--export([get_levels/0, set_levels/3]).
+-export([reject_io_reqs/1, get_levels/0, set_levels/3]).
-export([multiply_timetraps/1, scale_timetraps/1, get_timetrap_parameters/0]).
-export([create_priv_dir/1]).
-export([cover/2, cover/3, cover/7,
@@ -218,8 +218,9 @@
-define(auto_skip_color, "#FFA64D").
-define(user_skip_color, "#FF8000").
+-define(sortable_table_name, "SortableTable").
--record(state,{jobs=[],levels={1,19,10},
+-record(state,{jobs=[], levels={1,19,10}, reject_io_reqs=false,
multiply_timetraps=1, scale_timetraps=true,
create_priv_dir=auto_per_run, finish=false,
target_info, trc=false, cover=false, wait_for_node=[],
@@ -429,14 +430,6 @@ run_test(CommandLine) ->
testcase_callback(TCCB),
add_job(Name, {command_line,SpecList}),
- %% adding of jobs involves file i/o which may take long time
- %% when running a nfs mounted file system (VxWorks).
- case controller_call(get_target_info) of
- #target_info{os_family=vxworks} ->
- receive after 30000 -> ready_to_wait end;
- _ ->
- wait_now
- end,
wait_finish().
%% Converted CoverFile to a string unless it is 'none'
@@ -498,6 +491,9 @@ get_levels() ->
set_levels(Show, Major, Minor) ->
controller_call({set_levels,Show,Major,Minor}).
+reject_io_reqs(Bool) ->
+ controller_call({reject_io_reqs,Bool}).
+
multiply_timetraps(N) ->
controller_call({multiply_timetraps,N}).
@@ -815,6 +811,7 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) ->
[SpecName,{State#state.multiply_timetraps,
State#state.scale_timetraps}],
LogDir, Name, State#state.levels,
+ State#state.reject_io_reqs,
State#state.create_priv_dir,
State#state.testcase_callback, ExtraTools1),
NewJobs = [{Name,Pid}|State#state.jobs],
@@ -825,6 +822,7 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) ->
[SpecList,{State#state.multiply_timetraps,
State#state.scale_timetraps}],
LogDir, Name, State#state.levels,
+ State#state.reject_io_reqs,
State#state.create_priv_dir,
State#state.testcase_callback, ExtraTools1),
NewJobs = [{Name,Pid}|State#state.jobs],
@@ -843,6 +841,7 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) ->
{State#state.multiply_timetraps,
State#state.scale_timetraps}],
LogDir, Name, State#state.levels,
+ State#state.reject_io_reqs,
State#state.create_priv_dir,
State#state.testcase_callback, ExtraTools1),
NewJobs = [{Name,Pid}|State#state.jobs],
@@ -975,6 +974,15 @@ handle_call({set_levels,Show,Major,Minor}, _From, State) ->
{reply,ok,State#state{levels={Show,Major,Minor}}};
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% handle_call({reject_io_reqs,Bool}, _, State) -> ok
+%% Bool = bool()
+%%
+%% May be used to switch off stdout printouts to the minor log file
+
+handle_call({reject_io_reqs,Bool}, _From, State) ->
+ {reply,ok,State#state{reject_io_reqs=Bool}};
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% handle_call({multiply_timetraps,N}, _, State) -> ok
%% N = integer() | infinity
%%
@@ -1340,14 +1348,15 @@ kill_all_jobs([]) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% spawn_tester(Mod, Func, Args, Dir, Name, Levels, CreatePrivDir,
-%% TestCaseCallback, ExtraTools) -> Pid
+%% spawn_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs,
+%% CreatePrivDir, TestCaseCallback, ExtraTools) -> Pid
%% Mod = atom()
%% Func = atom()
%% Args = [term(),...]
%% Dir = string()
%% Name = string()
%% Levels = {integer(),integer(),integer()}
+%% RejectIoReqs = bool()
%% CreatePrivDir = auto_per_run | manual_per_tc | auto_per_tc
%% TestCaseCallback = {CBMod,CBFunc} | undefined
%% ExtraTools = [ExtraTool,...]
@@ -1359,14 +1368,14 @@ kill_all_jobs([]) ->
%% When the named function is done executing, a summary of the results
%% is printed to the log files.
-spawn_tester(Mod, Func, Args, Dir, Name, Levels,
+spawn_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs,
CreatePrivDir, TCCallback, ExtraTools) ->
spawn_link(
- fun() -> init_tester(Mod, Func, Args, Dir, Name, Levels,
+ fun() -> init_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs,
CreatePrivDir, TCCallback, ExtraTools)
end).
-init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev},
+init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev}, RejectIoReqs,
CreatePrivDir, TCCallback, ExtraTools) ->
process_flag(trap_exit, true),
put(test_server_name, Name),
@@ -1378,6 +1387,7 @@ init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev},
put(test_server_summary_level, SumLev),
put(test_server_major_level, MajLev),
put(test_server_minor_level, MinLev),
+ put(test_server_reject_io_reqs, RejectIoReqs),
put(test_server_create_priv_dir, CreatePrivDir),
put(test_server_random_seed, proplists:get_value(random_seed, ExtraTools)),
put(test_server_testcase_callback, TCCallback),
@@ -1400,13 +1410,14 @@ init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev},
StartedExtraTools = start_extra_tools(ExtraTools),
{TimeMy,Result} = ts_tc(Mod, Func, Args),
put(test_server_common_io_handler, undefined),
- stop_extra_tools(StartedExtraTools),
+ catch stop_extra_tools(StartedExtraTools),
case Result of
{'EXIT',test_suites_done} ->
print(25, "DONE, normal exit", []);
{'EXIT',_Pid,Reason} ->
print(1, "EXIT, reason ~p", [Reason]);
{'EXIT',Reason} ->
+ report_severe_error(Reason),
print(1, "EXIT, reason ~p", [Reason]);
_Other ->
print(25, "DONE", [])
@@ -1424,10 +1435,15 @@ init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev},
end,
OkN = get(test_server_ok),
FailedN = get(test_server_failed),
- print(html,"<tr><td></td><td><b>TOTAL</b></td><td></td><td></td><td></td>"
- "<td>~.3fs</td><td><b>~s</b></td><td>~p Ok, ~p Failed~s of ~p</td></tr>\n",
+ print(html,"\n</tbody>\n<tfoot>\n"
+ "<tr><td></td><td><b>TOTAL</b></td><td></td><td></td><td></td>"
+ "<td>~.3fs</td><td><b>~s</b></td><td>~p Ok, ~p Failed~s of ~p</td></tr>\n"
+ "</tfoot>\n",
[Time,SuccessStr,OkN,FailedN,SkipStr,OkN+FailedN+SkippedN]).
+report_severe_error(Reason) ->
+ test_server_sup:framework_call(report, [severe_error,Reason]).
+
%% timer:tc/3
ts_tc(M, F, A) ->
Before = ?now,
@@ -1486,7 +1502,7 @@ stop_extra_tools([], _) ->
%% Reads the named test suite specification file, and executes it.
%%
%% This function is meant to be called by a process created by
-%% spawn_tester/7, which sets up some necessary dictionary values.
+%% spawn_tester/10, which sets up some necessary dictionary values.
do_spec(SpecName, TimetrapSpec) when is_list(SpecName) ->
case file:consult(SpecName) of
@@ -1535,7 +1551,7 @@ do_spec(SpecName, TimetrapSpec) when is_list(SpecName) ->
%% should not be used. Use a configuration test case instead.
%%
%% This function is meant to be called by a process created by
-%% spawn_tester/7, which sets up some necessary dictionary values.
+%% spawn_tester/10, which sets up some necessary dictionary values.
do_spec_list(TermList0, TimetrapSpec) ->
Nodes = [],
@@ -1702,7 +1718,7 @@ add_mod(Mod, Mods) ->
%% configuration information into the log files.
%%
%% This function is meant to be called by a process created by
-%% spawn_tester/7, which sets up some necessary dictionary values.
+%% spawn_tester/10, which sets up some necessary dictionary values.
do_test_cases(TopCases, SkipCases,
Config, MultiplyTimetrap) when is_integer(MultiplyTimetrap);
MultiplyTimetrap == infinity ->
@@ -1741,7 +1757,8 @@ do_test_cases(TopCases, SkipCases,
test_server_sup:framework_call(report, [tests_start,{Test,N}]),
{Header,Footer} =
case test_server_sup:framework_call(get_html_wrapper,
- [TestDescr,true,TestDir], "") of
+ [TestDescr,true,TestDir,
+ {[],[2,3,4,7,8],[1,6]}], "") of
Empty when (Empty == "") ; (element(2,Empty) == "") ->
put(basic_html, true),
{["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
@@ -1803,14 +1820,15 @@ do_test_cases(TopCases, SkipCases,
[?suitelog_name,?coverlog_name]),
print(html,
"<p>~s</p>\n" ++
- xhtml("<table bgcolor=\"white\" border=\"3\" cellpadding=\"5\">",
- "<table>") ++
- "<tr><th>Num</th><th>Module</th><th>Group</th>" ++
- "<th>Case</th><th>Log</th><th>Time</th><th>Result</th>" ++
- "<th>Comment</th></tr>\n",
- [print_if_known(N, {"<i>Executing <b>~p</b> test cases...</i>\n",[N]},
+ xhtml("<table bgcolor=\"white\" border=\"3\" cellpadding=\"5\">",
+ ["<table id=\"",?sortable_table_name,"\">\n",
+ "<thead>\n"]) ++
+ "<tr><th>Num</th><th>Module</th><th>Group</th>" ++
+ "<th>Case</th><th>Log</th><th>Time</th><th>Result</th>" ++
+ "<th>Comment</th></tr>\n</thead>\n<tbody>\n",
+ [print_if_known(N, {"<i>Executing <b>~p</b> test cases...</i>" ++
+ xhtml("\n<br>\n", "\n<br />\n"),[N]},
{"",[]})]),
- print(html, xhtml("<br>", "<br />")),
print(major, "=cases ~p", [get(test_server_cases)]),
print(major, "=user ~s", [TI#target_info.username]),
@@ -1859,7 +1877,7 @@ start_log_file() ->
{error, eexist} ->
ok;
MkDirError ->
- exit({cant_create_log_dir,{MkDirError,Dir}})
+ log_file_error(MkDirError, Dir)
end,
TestDir = timestamp_filename_get(filename:join(Dir, "run.")),
TestDir1 =
@@ -1874,10 +1892,10 @@ start_log_file() ->
ok ->
TestDirX;
MkDirError2 ->
- exit({cant_create_log_dir,{MkDirError2,TestDirX}})
+ log_file_error(MkDirError2, TestDirX)
end;
MkDirError2 ->
- exit({cant_create_log_dir,{MkDirError2,TestDir}})
+ log_file_error(MkDirError2, TestDir)
end,
ok = file:write_file(filename:join(Dir, ?last_file), TestDir1 ++ "\n"),
ok = file:write_file(?last_file, TestDir1 ++ "\n"),
@@ -1904,6 +1922,9 @@ start_log_file() ->
test_server_sup:framework_call(report, [loginfo,LogInfo]),
{ok,TestDir1}.
+log_file_error(Error, Dir) ->
+ exit({cannot_create_log_dir,{Error,lists:flatten(Dir)}}).
+
make_html_link(LinkName, Target, Explanation) ->
%% if possible use a relative reference to Target.
TargetL = filename:split(Target),
@@ -1964,7 +1985,8 @@ start_minor_log_file1(Mod, Func, LogDir, AbsName) ->
{Header,Footer} =
case test_server_sup:framework_call(get_html_wrapper,
[TestDescr,false,
- filename:dirname(AbsName)], "") of
+ filename:dirname(AbsName),
+ undefined], "") of
Empty when (Empty == "") ; (element(2,Empty) == "") ->
put(basic_html, true),
{["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
@@ -2094,7 +2116,7 @@ html_possibly_convert(Src, SrcInfo, Dest) ->
Header =
case test_server_sup:framework_call(get_html_wrapper,
["Module "++Src,false,
- OutDir], "") of
+ OutDir,undefined], "") of
Empty when (Empty == "") ; (element(2,Empty) == "") ->
["<!DOCTYPE HTML PUBLIC",
"\"-//W3C//DTD HTML 3.2 Final//EN\">\n",
@@ -3809,10 +3831,12 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
MinorBase,MinorBase]),
do_if_parallel(Main, ok, fun erlang:yield/0),
+
+ RejectIoReqs = get(test_server_reject_io_reqs),
%% run the test case
{Result,DetectedFail,ProcsBefore,ProcsAfter} =
run_test_case_apply(Num, Mod, Func, [UpdatedArgs], get_name(Mode),
- RunInit, Where, TimetrapData),
+ RunInit, Where, TimetrapData, RejectIoReqs),
{Time,RetVal,Loc,Opts,Comment} =
case Result of
Normal={_Time,_RetVal,_Loc,_Opts,_Comment} -> Normal;
@@ -4431,7 +4455,7 @@ do_format_exception(Reason={Error,Stack}) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit,
-%% Where, TimetrapData) ->
+%% Where, TimetrapData, RejectIoReqs) ->
%% {{Time,RetVal,Loc,Opts,Comment},DetectedFail,ProcessesBefore,ProcessesAfter} |
%% {{died,Reason,unknown,Comment},DetectedFail,ProcessesBefore,ProcessesAfter}
%% Name = atom()
@@ -4450,19 +4474,21 @@ do_format_exception(Reason={Error,Stack}) ->
%% sent over socket to target, and test_server runs the case and sends the
%% result back over the socket. Else test_server runs the case directly on host.
-run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, host, TimetrapData) ->
+run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, host,
+ TimetrapData, RejectIoReqs) ->
test_server:run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,
- TimetrapData});
-run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, target, TimetrapData) ->
+ TimetrapData,RejectIoReqs});
+run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, target,
+ TimetrapData, RejectIoReqs) ->
case get(test_server_ctrl_job_sock) of
undefined ->
%% local target
test_server:run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,
- TimetrapData});
+ TimetrapData,RejectIoReqs});
JobSock ->
%% remote target
request(JobSock, {test_case,{CaseNum,Mod,Func,Args,Name,RunInit,
- TimetrapData}}),
+ TimetrapData,RejectIoReqs}}),
read_job_sock_loop(JobSock)
end.
@@ -4660,21 +4686,16 @@ output_to_fd(stdout, Msg, Sender) ->
io:format("Testing ~s: ~s\n", [Name, lists:flatten(Msg)]);
output_to_fd(undefined, _Msg, _Sender) ->
ok;
-output_to_fd(Fd, [$=|Msg], internal) ->
- io:put_chars(Fd, [$=]),
- io:put_chars(Fd, Msg),
- io:put_chars(Fd, "\n");
+output_to_fd(Fd, Msg=[$=|_], internal) ->
+ io:put_chars(Fd, [Msg,"\n"]);
output_to_fd(Fd, Msg, internal) ->
- io:put_chars(Fd, [$=,$=,$=,$ ]),
- io:put_chars(Fd, Msg),
- io:put_chars(Fd, "\n");
+ io:put_chars(Fd, [$=,$=,$=,$ , Msg, "\n"]);
output_to_fd(Fd, Msg, _Sender) ->
- io:put_chars(Fd, Msg),
case get(test_server_log_nl) of
- false -> ok;
- _ -> io:put_chars(Fd, "\n")
+ false -> io:put_chars(Fd, Msg);
+ _ -> io:put_chars(Fd, [Msg,"\n"])
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -5808,11 +5829,11 @@ write_default_cross_coverlog(TestDir) ->
{ok,CrossCoverLog} =
file:open(filename:join(TestDir,?cross_coverlog_name), [write]),
write_coverlog_header(CrossCoverLog),
- io:fwrite(CrossCoverLog,
- ["No cross cover modules exist for this application,",
- xhtml("<br>","<br />"),
- "or cross cover analysis is not completed.\n"
- "</body></html>\n"], []),
+ io:put_chars(CrossCoverLog,
+ ["No cross cover modules exist for this application,",
+ xhtml("<br>","<br />"),
+ "or cross cover analysis is not completed.\n"
+ "</body></html>\n"]),
file:close(CrossCoverLog).
write_cover_result_table(CoverLog,Coverage) ->
diff --git a/lib/test_server/src/test_server_internal.hrl b/lib/test_server/src/test_server_internal.hrl
index c9c52854e3..b58b42805e 100644
--- a/lib/test_server/src/test_server_internal.hrl
+++ b/lib/test_server/src/test_server_internal.hrl
@@ -25,7 +25,7 @@
%% test_server_ctrl:contact_main_target/2
%% Once initiated, this information will never change!!
-record(target_info, {where, % local | Socket
- os_family, % atom(); win32 | unix | vxworks | ose
+ os_family, % atom(); win32 | unix
os_type, % result of os:type()
host, % string(); the name of the target machine
version, % string()
diff --git a/lib/test_server/src/test_server_node.erl b/lib/test_server/src/test_server_node.erl
index 6358efa764..17c02dfbe5 100644
--- a/lib/test_server/src/test_server_node.erl
+++ b/lib/test_server/src/test_server_node.erl
@@ -35,7 +35,6 @@
-include("test_server_internal.hrl").
-record(slave_info, {name,socket,client}).
--define(VXWORKS_ACCEPT_TIMEOUT,?ACCEPT_TIMEOUT).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% %%%
@@ -72,14 +71,6 @@ start_remote_main_target(Parameters) ->
lists:foreach(fun(T) -> maybe_reboot_target({TargetType,T}) end,
[list_to_atom(TargetHost)|SlaveTargets]),
- % Must give the targets a chance to reboot...
- case TargetType of
- vxworks ->
- receive after 15000 -> ok end;
- _ ->
- ok
- end,
-
Cmd0 = get_main_target_start_command(TargetType,TargetHost,Naming,
MasterNode,MasterCookie),
Cmd =
@@ -462,9 +453,6 @@ start_node_peer(SlaveName, OptList, From, TI) ->
%%
%% Slave nodes are started on a remote host if
%% - the option remote is given when calling test_server:start_node/3
-%% or
-%% - the target type is vxworks, since only one erlang node
-%% can be started on each vxworks host.
%%
start_node_slave(SlaveName, OptList, From, TI) ->
SuppliedArgs = start_node_get_option_value(args, OptList, []),
@@ -787,19 +775,6 @@ kill_node(SI,TI) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Platform specific code
-start_target(vxworks,TargetHost,Cmd) ->
- case vxworks_client:open(TargetHost) of
- {ok,P} ->
- case vxworks_client:send_data(P,Cmd,"start_erl called") of
- {ok,_} ->
- {ok,{vxworks,P},?VXWORKS_ACCEPT_TIMEOUT};
- Error ->
- Error
- end;
- Error ->
- Error
- end;
-
start_target(unix,TargetHost,Cmd0) ->
Cmd =
case test_server_sup:hoststr() of
@@ -809,19 +784,9 @@ start_target(unix,TargetHost,Cmd0) ->
open_port({spawn, Cmd}, [stream]),
{ok,undefined,?ACCEPT_TIMEOUT}.
-maybe_reboot_target({vxworks,P}) when is_pid(P) ->
- %% Reboot the vxworks card.
- %% Client is also closed after this, even if reboot fails
- vxworks_client:send_data_wait_for_close(P,"q");
-maybe_reboot_target({vxworks,T}) when is_atom(T) ->
- %% Reboot the vxworks card.
- %% Client is also closed after this, even if reboot fails
- vxworks_client:reboot(T);
maybe_reboot_target(_) ->
{error, cannot_reboot_target}.
-close_target_client({vxworks,P}) ->
- vxworks_client:close(P);
close_target_client(undefined) ->
ok.
@@ -830,11 +795,6 @@ close_target_client(undefined) ->
%%
%% Command for starting main target
%%
-get_main_target_start_command(vxworks,_TargetHost,Naming,
- _MasterNode,_MasterCookie) ->
- "e" ++ Naming ++ " test_server -boot start_sasl"
- " -sasl errlog_type error"
- " -s test_server start " ++ test_server_sup:hoststr();
get_main_target_start_command(unix,_TargetHost,Naming,
_MasterNode,_MasterCookie) ->
Prog = pick_erl_program(default),
@@ -845,9 +805,6 @@ get_main_target_start_command(unix,_TargetHost,Naming,
%%
%% Command for starting slave nodes
%%
-get_slave_node_start_command(vxworks, _Prog, _MasterNode) ->
- "e";
- %"e-noinput -master " ++ MasterNode;
get_slave_node_start_command(unix, Prog, MasterNode) ->
cast_to_list(Prog) ++ " -detached -master " ++ MasterNode.
diff --git a/lib/test_server/src/test_server_sup.erl b/lib/test_server/src/test_server_sup.erl
index 9d111ff769..4a27c1ebae 100644
--- a/lib/test_server/src/test_server_sup.erl
+++ b/lib/test_server/src/test_server_sup.erl
@@ -473,10 +473,8 @@ getenv_any([]) -> "".
%%
%% Returns the OS family
get_os_family() ->
- case os:type() of
- {OsFamily,_OsName} -> OsFamily;
- OsFamily -> OsFamily
- end.
+ {OsFamily,_OsName} = os:type(),
+ OsFamily.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/test_server/src/ts.erl b/lib/test_server/src/ts.erl
index 4899f38d2b..db16b6ecd2 100644
--- a/lib/test_server/src/ts.erl
+++ b/lib/test_server/src/ts.erl
@@ -25,9 +25,9 @@
-module(ts).
-export([run/0, run/1, run/2, run/3, run/4,
- clean/0, clean/1,
tests/0, tests/1,
- install/0, install/1, index/0,
+ install/0, install/1,
+ bench/0, bench/1, bench/2, benchmarks/0,
estone/0, estone/1,
cross_cover_analyse/1,
compile_testcases/0, compile_testcases/1,
@@ -40,20 +40,14 @@
%%% the modules:
%%%
%%% +-- ts_install --+------ ts_autoconf_win32
-%%% | |
-%%% | +------ ts_autoconf_vxworks
%%% |
%%% ts ---+ +------ ts_erl_config
%%% | | ts_lib
-%%% | +------ ts_make
-%%% | |
-%%% +-- ts_run -----+
-%%% | ts_filelib
-%%% +------ ts_make_erl
-%%% |
-%%% +------ ts_reports (indirectly)
-%%%
-%%%
+%%% +-- ts_run -----+------ ts_make
+%%% | | ts_filelib
+%%% | +------ ts_make_erl
+%%% |
+%%% +-- ts_benchmark
%%%
%%% The modules ts_lib and ts_filelib contains utilities used by
%%% the other modules.
@@ -63,8 +57,7 @@
%%% ts Frontend to the test server framework. Contains all
%%% interface functions.
%%% ts_install Installs the test suite. On Unix, `autoconf' is
-%%% is used; on Windows, ts_autoconf_win32 is used,
-%%% on VxWorks, ts_autoconf_vxworks is used.
+%%% is used; on Windows, ts_autoconf_win32 is used.
%%% The result is written to the file `variables'.
%%% ts_run Supervises running of the tests.
%%% ts_autconf_win32 An `autoconf' for Windows.
@@ -77,10 +70,9 @@
%%% and other platforms.
%%% ts_make_erl A corrected version of the standar Erlang module
%%% make (used for rebuilding test suites).
-%%% ts_reports Generates index pages in HTML, providing a summary
-%%% of the tests run.
%%% ts_lib Miscellanous utility functions, each used by several
%%% other modules.
+%%% ts_benchmark Supervises otp benchmarks and collects results.
%%%----------------------------------------------------------------------
-include_lib("kernel/include/file.hrl").
@@ -128,7 +120,7 @@ help(installed) ->
" ts:run(Spec, Mod) - Run a single test suite.\n",
" ts:run(Spec, Mod, Case)\n",
" - Run a single test case.\n",
- " All above run functions can have the additional Options argument\n",
+ " All above run functions can have an additional Options argument\n",
" which is a list of options.\n",
"\n",
"Run options supported:\n",
@@ -158,13 +150,10 @@ help(installed) ->
" {ctp | ctpl, Mod, Func}\n",
" {ctp | ctpl, Mod, Func, Arity}\n",
"\n",
- "Support functions\n",
+ "Support functions:\n",
" ts:tests() - Shows all available families of tests.\n",
" ts:tests(Spec) - Shows all available test modules in Spec,\n",
" i.e. ../Spec_test/*_SUITE.erl\n",
- " ts:index() - Updates local index page.\n",
- " ts:clean() - Cleans up all but the last tests run.\n",
- " ts:clean(all) - Cleans up all test runs found.\n",
" ts:estone() - Run estone_SUITE in kernel application with\n"
" no run options\n",
" ts:estone(Opts) - Run estone_SUITE in kernel application with\n"
@@ -179,6 +168,13 @@ help(installed) ->
" - Compile all testcases for usage in a cross ~n"
" compile environment."
" \n"
+ "Benchmark functions:\n"
+ " ts:benchmarks() - Get all available families of benchmarks\n"
+ " ts:bench() - Runs all benchmarks\n"
+ " ts:bench(Spec) - Runs all benchmarks in the given spec file.\n"
+ " The spec file is actually ../*_test/Spec_bench.spec\n\n"
+ " ts:bench can take the same Options argument as ts:run.\n"
+ "\n"
"Installation (already done):\n"
],
show_help([H,?install_help]).
@@ -193,33 +189,6 @@ install() ->
install(Options) when is_list(Options) ->
ts_install:install(install_local,Options).
-%% Updates the local index page.
-
-index() ->
- check_and_run(fun(_Vars) -> ts_reports:make_index(), ok end).
-
-%%
-%% clean(all)
-%% Deletes all logfiles.
-%%
-clean(all) ->
- delete_files(filelib:wildcard("*" ++ ?logdir_ext)).
-
-%% clean/0
-%%
-%% Cleans up run logfiles, all but the last run.
-clean() ->
- clean1(filelib:wildcard("*" ++ ?logdir_ext)).
-
-clean1([Dir|Dirs]) ->
- List0 = filelib:wildcard(filename:join(Dir, "run.*")),
- case lists:reverse(lists:sort(List0)) of
- [] -> ok;
- [_Last|Rest] -> delete_files(Rest)
- end,
- clean1(Dirs);
-clean1([]) -> ok.
-
%% run/0
%% Runs all specs found by ts:tests(), if any, or returns
%% {error, no_tests_available}. (batch)
@@ -491,6 +460,25 @@ tests(Spec) ->
{ok, Cwd} = file:get_cwd(),
ts_lib:suites(Cwd, atom_to_list(Spec)).
+%% Benchmark related functions
+
+bench() ->
+ bench([]).
+
+bench(Opts) when is_list(Opts) ->
+ bench(benchmarks(),Opts);
+bench(Spec) ->
+ bench([Spec],[]).
+
+bench(Spec, Opts) when is_atom(Spec) ->
+ bench([Spec],Opts);
+bench(Specs, Opts) ->
+ check_and_run(fun(Vars) -> ts_benchmark:run(Specs, Opts, Vars) end).
+
+benchmarks() ->
+ ts_benchmark:benchmarks().
+
+
%%
%% estone/0, estone/1
@@ -552,32 +540,6 @@ run_test(File, Args, Options) ->
run_test(File, Args, Options, Vars) ->
ts_run:run(File, Args, Options, Vars).
-
-delete_files([]) -> ok;
-delete_files([Item|Rest]) ->
- case file:delete(Item) of
- ok ->
- delete_files(Rest);
- {error,eperm} ->
- file:change_mode(Item, 8#777),
- delete_files(filelib:wildcard(filename:join(Item, "*"))),
- file:del_dir(Item),
- ok;
- {error,eacces} ->
- %% We'll see about that!
- file:change_mode(Item, 8#777),
- case file:delete(Item) of
- ok -> ok;
- {error,_} ->
- erlang:yield(),
- file:change_mode(Item, 8#777),
- file:delete(Item),
- ok
- end;
- {error,_} -> ok
- end,
- delete_files(Rest).
-
%% This module provides some convenient shortcuts to running
%% the test server from within a started Erlang shell.
diff --git a/lib/test_server/src/ts.hrl b/lib/test_server/src/ts.hrl
index db804d23a1..5e829f3575 100644
--- a/lib/test_server/src/ts.hrl
+++ b/lib/test_server/src/ts.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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/lib/test_server/src/ts_autoconf_vxworks.erl b/lib/test_server/src/ts_autoconf_vxworks.erl
deleted file mode 100644
index f4535cd89a..0000000000
--- a/lib/test_server/src/ts_autoconf_vxworks.erl
+++ /dev/null
@@ -1,191 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. 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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%% Purpose : Autoconf for cross environments.
-
--module(ts_autoconf_vxworks).
--export([configure/1]).
-%%% Supported cross platforms:
--define(PLATFORMS, ["vxworks_cpu32", "vxworks_ppc860", "vxworks_ppc603",
- "vxworks_sparc", "vxworks_ppc750", "vxworks_simso"]).
--include("ts.hrl").
-
-%% takes an argument {Target_arch, Target_host} (e.g. {vxworks_ppc860, thorin}).
-configure({Target_arch, Target_host}) ->
- case variables({Target_arch, Target_host}) of
- {ok, Vars} ->
- ts_lib:subst_file("conf_vars.in", "conf_vars", Vars);
- Error ->
- Error
- end.
-
-variables(Cross_spec) ->
- run_tests(Cross_spec, tests(), []).
-
-run_tests(Cross_spec, [{Prompt, Tester}|Rest], Vars) ->
- io:format("checking ~s... ", [Prompt]),
- case catch Tester(Cross_spec, Vars) of
- {'EXIT', Reason} ->
- io:format("FAILED~nExit status: ~p~n", [Reason]),
- {error, auto_conf_failed};
- {Result, NewVars} ->
- io:format("~s~n", [lists:concat([Result])]),
- run_tests(Cross_spec, Rest, NewVars)
- end;
-run_tests(_Cross_spec, [], Vars) ->
- {ok, Vars}.
-
-
-%%% The tests.
-
-tests() ->
- [{"supported target architecture", fun target_architecture/2},
- {"cross target host to run tests on", fun target_host/2},
- {"CPU type", fun cpu/2},
- {"for cross-compiling gcc", fun find_gcc/2},
- {"for cross-linker", fun find_ld/2},
- {"for object extension", fun find_obj/2},
- {"for shared libraries extension", fun find_dll/2},
- {"for executables extension", fun find_exe/2},
- {"for make", fun find_make/2}].
-
-target_architecture({Architecture, _Target_host}, Vars) ->
- case lists:member(Architecture, ?PLATFORMS) of
- true ->
- {Architecture, [{host_os, os_type(Architecture)}, {host, Architecture}|Vars]};
- false ->
- {"unsupported_platform", Vars}
- end.
-
-target_host({_Architecture, Target_host}, Vars) ->
- {Target_host, [{target_host, Target_host} | Vars]}.
-
-cpu({Arch, _Target_host}, Vars) ->
- Cpu = processor(Arch),
- {Cpu, [{host_cpu, Cpu}|Vars]}.
-
-find_gcc({Arch, _Target_host}, Vars) ->
- Gcc = "cc" ++ gnu_suffix(Arch),
- case os:find_executable(Gcc) of
- false ->
- {no, Vars};
- Path when is_list(Path) ->
- Cflags = cflags(Arch),
- {Path, [{'CC', Gcc},
- {'CFLAGS', Cflags},
- {'EI_CFLAGS', Cflags},
- {'ERTS_CFLAGS', Cflags},
- {'DEFS', ""},
- {'ERTS_LIBS', ""},
- {'LIBS', ""},
- {'SHLIB_CFLAGS', Cflags},
- {test_c_compiler, "{gnuc, undefined}"} | Vars]}
- end.
-
-find_ld({Arch, _Target_host}, Vars) ->
- Linker = "ld" ++ gnu_suffix(Arch),
- case os:find_executable(Linker) of
- false ->
- {no, Vars};
- Path when is_list(Path) ->
- {Path, [{'LD', Linker},
- {'CROSSLDFLAGS', ldflags(Arch)},
- {'SHLIB_EXTRACT_ALL', ""},
- {'SHLIB_LD', Linker},
- {'SHLIB_LDFLAGS', ""},
- {'SHLIB_LDLIBS', ""} | Vars]}
- end.
-
-find_obj({Arch, _Target_host}, Vars) ->
- Obj = obj_ext(Arch),
- {Obj, [{obj, Obj}|Vars]}.
-
-find_dll({Arch, _Target_host}, Vars) ->
- Dll = dll_ext(Arch),
- {Dll, [{'SHLIB_SUFFIX', Dll}|Vars]}.
-
-find_exe({Arch, _Target_host}, Vars) ->
- Exe = exe_ext(Arch),
- {Exe, [{exe, Exe}|Vars]}.
-
-find_make(_, Vars) ->
- {"make", [{make_command, "make"} | Vars]}.
-
-%%% some utility functions
-gnu_suffix(Arch) ->
- {_, _, _, _, Suffix, _Cpu, _Cflags, _} = cross_data(Arch),
- Suffix.
-
-processor(Arch) ->
- {_, _, _, _, _Suffix, Cpu, _Cflags, _} = cross_data(Arch),
- Cpu.
-
-cflags(Arch) ->
- {_, _, _, _, _Suffix, _Cpu, Cflags, _} = cross_data(Arch),
- Cflags.
-
-ldflags(Arch) ->
- {_, _, _, _, _Suffix, _Cpu, _Cflags, Ldflags} = cross_data(Arch),
- Ldflags.
-
-os_type(Arch) ->
- {Os_type, _, _, _, _, _, _, _} = cross_data(Arch),
- Os_type.
-
-obj_ext(Arch) ->
- {_, _, Obj, _, _, _, _, _} = cross_data(Arch),
- Obj.
-
-dll_ext(Arch) ->
- {_, _, _, Dll, _, _, _, _} = cross_data(Arch),
- Dll.
-
-exe_ext(Arch) ->
- {_, Exe, _, _, _, _, _, _} = cross_data(Arch),
- Exe.
-
-cross_data(Arch) ->
- case Arch of
- "vxworks_cpu32" ->
- {"VxWorks", "", ".o", ".eld", "68k", "cpu32",
- "-DCPU=CPU32 -DVXWORKS -I$(WIND_BASE)/target/h -mnobitfield -fno-builtin -nostdinc -fvolatile -msoft-float",
- "-r -d"};
- "vxworks_ppc860" ->
- {"VxWorks", "", ".o", ".eld", "ppc", "ppc860",
- "-DCPU=PPC860 -DVXWORKS -I$(WIND_BASE)/target/h -mcpu=860 -fno-builtin -fno-for-scope -msoft-float -D_GNU_TOOL -nostdinc",
- "-r -d"};
- "vxworks_ppc603" ->
- {"VxWorks", "", ".o", ".eld", "ppc", "ppc603",
- "-DCPU=PPC603 -DVXWORKS -I$(WIND_BASE)/target/h -fno-builtin -fno-for-scope -D_GNU_TOOL -nostdinc",
- "-r -d"};
- "vxworks_sparc" ->
- %%% The Sparc Architecture is included for private use (i.e. not Tornado 1.0.1 compatible).
- {"VxWorks", "", ".o", ".eld", "sparc", "sparc",
- "-DCPU=SPARC -DVXWORKS -I/home/gandalf/bsproj/BS.2/UOS/vw/5.2/h -fno-builtin -nostdinc",
- "-r -d"};
- "vxworks_ppc750" ->
- {"VxWorks", "", ".o", ".eld", "ppc", "ppc604",
- "-DCPU=PPC604 -DVXWORKS -DTOOL_FAMILY=gnu -DTOOL=gnu -I$(WIND_BASE)/target/h -fno-builtin -fno-for-scope -D_GNU_TOOL",
- "-r -d"};
- "vxworks_simso" ->
- {"VxWorks", "", ".o", ".eld", "simso", "simso",
- "-DCPU=SIMSPARCSOLARIS -DVXWORKS -DTOOL_FAMILY=gnu -DTOOL=gnu -I$(WIND_BASE)/target/h -I$(WIND_GCC_INCLUDE) -fno-builtin -fno-for-scope -D_GNU_TOOL",
- "-r -d"}
-
- end.
diff --git a/lib/test_server/src/ts_autoconf_win32.erl b/lib/test_server/src/ts_autoconf_win32.erl
index 258040b39e..4a5c5c7603 100644
--- a/lib/test_server/src/ts_autoconf_win32.erl
+++ b/lib/test_server/src/ts_autoconf_win32.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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/lib/test_server/src/ts_benchmark.erl b/lib/test_server/src/ts_benchmark.erl
new file mode 100644
index 0000000000..516d22fd2d
--- /dev/null
+++ b/lib/test_server/src/ts_benchmark.erl
@@ -0,0 +1,91 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012-2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(ts_benchmark).
+
+-include_lib("common_test/include/ct_event.hrl").
+-include_lib("kernel/include/file.hrl").
+-include("ts.hrl").
+
+-export([benchmarks/0,
+ run/3]).
+
+%% gen_event callbacks
+-export([init/1, handle_event/2]).
+
+benchmarks() ->
+ {ok, Cwd} = file:get_cwd(),
+ Benches = filelib:wildcard(
+ filename:join([Cwd,"..","*_test","*_bench.spec"])),
+ [begin
+ Base = filename:basename(N),
+ list_to_atom(string:substr(Base,1,string:rstr(Base,"_")-1))
+ end || N <- Benches].
+
+run(Specs, Opts, Vars) ->
+ {ok, Cwd} = file:get_cwd(),
+ {{YY,MM,DD},{HH,Mi,SS}} = calendar:local_time(),
+ BName = lists:concat([YY,"_",MM,"_",DD,"T",HH,"_",Mi,"_",SS]),
+ BDir = filename:join([Cwd,BName]),
+ file:make_dir(BDir),
+ [ts_run:run(atom_to_list(Spec),
+ [{spec, [atom_to_list(Spec)++"_bench.spec"]}],
+ [{event_handler, {ts_benchmark, [Spec,BDir]}}|Opts],Vars)
+ || Spec <- Specs],
+ file:delete(filename:join(Cwd,"latest_benchmark")),
+ {ok,D} = file:open(filename:join(Cwd,"latest_benchmark"),[write]),
+ io:format(D,BDir,[]),
+ file:close(D).
+
+
+%%%===================================================================
+%%% gen_event callbacks
+%%%===================================================================
+
+-record(state, { spec, suite, tc, stats_dir}).
+
+init([Spec,Dir]) ->
+ {ok, #state{ spec = Spec, stats_dir = Dir }}.
+
+handle_event(#event{name = tc_start, data = {Suite,Tc}}, State) ->
+ {ok,State#state{ suite = Suite, tc = Tc}};
+handle_event(#event{name = benchmark_data, data = Data}, State) ->
+ Spec = proplists:get_value(application, Data, State#state.spec),
+ Suite = proplists:get_value(suite, Data, State#state.suite),
+ Tc = proplists:get_value(name, Data, State#state.tc),
+ Value = proplists:get_value(value, Data),
+ {ok, D} = file:open(filename:join(
+ [State#state.stats_dir,
+ lists:concat([e(Spec),"-",e(Suite),"-",
+ e(Tc),".ebench"])]),
+ [append]),
+ io:format(D, "~p~n",[Value]),
+ file:close(D),
+ {ok, State};
+handle_event(_Event, State) ->
+ {ok, State}.
+
+
+e(Atom) when is_atom(Atom) ->
+ Atom;
+e(Str) when is_list(Str) ->
+ lists:map(fun($/) ->
+ $\\;
+ (C) ->
+ C
+ end,Str).
diff --git a/lib/test_server/src/ts_erl_config.erl b/lib/test_server/src/ts_erl_config.erl
index 45d88016a4..73abe86e11 100644
--- a/lib/test_server/src/ts_erl_config.erl
+++ b/lib/test_server/src/ts_erl_config.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -160,7 +160,6 @@ system_include(Root, Vars) ->
SysDir =
case ts_lib:var(os, Vars) of
"Windows" ++ _T -> "sys/win32";
- "VxWorks" -> "sys.vxworks";
_ -> "sys/unix"
end,
" -I" ++ quote(filename:nativename(filename:join([Root, "erts", "emulator", SysDir]))).
@@ -176,9 +175,6 @@ erl_interface(Vars,OsType) ->
{installed, _Root} ->
{filename:join(Dir, "lib"),
filename:join(Dir, "src")};
- {srctree, _Root, _Target} when OsType =:= vxworks ->
- {filename:join(Dir, "lib"),
- filename:join([Dir, "src"])};
{srctree, _Root, Target} ->
{filename:join([Dir, "obj", Target]),
filename:join([Dir, "src", Target])}
@@ -218,7 +214,7 @@ erl_interface(Vars,OsType) ->
{unix,_} ->
"-lpthread";
_ ->
- "" % VxWorks
+ ""
end,
[{erl_interface_libpath, quote(filename:nativename(LibPath))},
{erl_interface_sock_libs, sock_libraries(OsType)},
@@ -318,16 +314,12 @@ get_var(Key, Vars) ->
sock_libraries({win32, _}) ->
"ws2_32.lib";
sock_libraries({unix, _}) ->
- ""; % Included in general libraries if needed.
-sock_libraries(vxworks) ->
- "".
+ "". % Included in general libraries if needed.
link_library(LibName,{win32, _}) ->
LibName ++ ".lib";
link_library(LibName,{unix, _}) ->
"lib" ++ LibName ++ ".a";
-link_library(LibName,vxworks) ->
- "lib" ++ LibName ++ ".a";
link_library(_LibName,_Other) ->
exit({link_library, not_supported}).
diff --git a/lib/test_server/src/ts_install.erl b/lib/test_server/src/ts_install.erl
index 99ccfbc9bc..ba8952f10f 100644
--- a/lib/test_server/src/ts_install.erl
+++ b/lib/test_server/src/ts_install.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -55,8 +55,7 @@ build_install(TargetSystem, Options) ->
end.
os_type({unix,_}=OsType) -> OsType;
-os_type({win32,_}=OsType) -> OsType;
-os_type(_Other) -> vxworks.
+os_type({win32,_}=OsType) -> OsType.
target_install(CrossVars) ->
io:format("Cross installation detected, skipping configure and data_dir make~n"),
@@ -76,7 +75,6 @@ target_install(CrossVars) ->
%% Autoconf for various platforms.
%% unix uses the configure script
%% win32 uses ts_autoconf_win32
-%% VxWorks uses ts_autoconf_vxworks.
autoconf(TargetSystem, XComp) ->
case autoconf1(TargetSystem, XComp) of
@@ -90,8 +88,6 @@ autoconf1({win32, _},[{cross,"no"}]) ->
ts_autoconf_win32:configure();
autoconf1({unix, _},XCompFile) ->
unix_autoconf(XCompFile);
-autoconf1(Other,[{cross,"no"}]) ->
- ts_autoconf_vxworks:configure(Other);
autoconf1(_,_) ->
io:format("cross compilation not supported for that this platform~n"),
throw(cross_installation_failed).
diff --git a/lib/test_server/src/ts_install_cth.erl b/lib/test_server/src/ts_install_cth.erl
index 67f2df0cae..9b6e10e7e2 100644
--- a/lib/test_server/src/ts_install_cth.erl
+++ b/lib/test_server/src/ts_install_cth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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/lib/test_server/src/ts_lib.erl b/lib/test_server/src/ts_lib.erl
index d521d2beda..d9a699ca9f 100644
--- a/lib/test_server/src/ts_lib.erl
+++ b/lib/test_server/src/ts_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -25,9 +25,8 @@
-compile({no_auto_import,[error/1]}).
-export([error/1, var/2, erlang_type/0,
erlang_type/1,
- initial_capital/1, interesting_logs/1,
- specs/1, suites/2, last_test/1,
- force_write_file/2, force_delete/1,
+ initial_capital/1,
+ specs/1, suites/2,
subst_file/3, subst/2, print_data/1,
make_non_erlang/2,
maybe_atom_to_list/1, progress/4
@@ -91,25 +90,18 @@ initial_capital([C|Rest]) when $a =< C, C =< $z ->
initial_capital(String) ->
String.
-%% Returns a list of the "interesting logs" in a directory,
-%% i.e. those that correspond to spec files.
-
-interesting_logs(Dir) ->
- Logs = filelib:wildcard(filename:join(Dir, [$*|?logdir_ext])),
- Interesting =
- case specs(Dir) of
- [] ->
- Logs;
- Specs0 ->
- Specs = ordsets:from_list(Specs0),
- [L || L <- Logs, ordsets:is_element(filename_to_atom(L), Specs)]
- end,
- sort_tests(Interesting).
-
specs(Dir) ->
Specs = filelib:wildcard(filename:join([filename:dirname(Dir),
- "*_test", "*.{dyn,}spec"])),
- sort_tests([filename_to_atom(Name) || Name <- Specs]).
+ "*_test", "*.{dyn,}spec"])),
+ % Filter away all spec which end with _bench.spec
+ NoBench = fun(SpecName) ->
+ case lists:reverse(SpecName) of
+ "ceps.hcneb_"++_ -> false;
+ _ -> true
+ end
+ end,
+
+ sort_tests([filename_to_atom(Name) || Name <- Specs, NoBench(Name)]).
suites(Dir, Spec) ->
Glob=filename:join([filename:dirname(Dir), Spec++"_test",
@@ -157,42 +149,6 @@ suite_order(mnesia) -> 44;
suite_order(system) -> 999; %% IMPORTANT: system SHOULD always be last!
suite_order(_) -> 200.
-last_test(Dir) ->
- last_test(filelib:wildcard(filename:join(Dir, "run.[1-2]*")), false).
-
-last_test([Run|Rest], false) ->
- last_test(Rest, Run);
-last_test([Run|Rest], Latest) when Run > Latest ->
- last_test(Rest, Run);
-last_test([_|Rest], Latest) ->
- last_test(Rest, Latest);
-last_test([], Latest) ->
- Latest.
-
-%% Do the utmost to ensure that the file is written, by deleting or
-%% renaming an old file with the same name.
-
-force_write_file(Name, Contents) ->
- force_delete(Name),
- file:write_file(Name, Contents).
-
-force_delete(Name) ->
- case file:delete(Name) of
- {error, eacces} ->
- force_rename(Name, Name ++ ".old.", 0);
- Other ->
- Other
- end.
-
-force_rename(From, To, Number) ->
- Dest = [To|integer_to_list(Number)],
- case file:read_file_info(Dest) of
- {ok, _} ->
- force_rename(From, To, Number+1);
- {error, _} ->
- file:rename(From, Dest)
- end.
-
%% Substitute all occurrences of @var@ in the In file, using
%% the list of variables in Vars, producing the output file Out.
%% Returns: ok | {error, Reason}
diff --git a/lib/test_server/src/ts_make.erl b/lib/test_server/src/ts_make.erl
index 1d8ef230c7..f3266f5836 100644
--- a/lib/test_server/src/ts_make.erl
+++ b/lib/test_server/src/ts_make.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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/lib/test_server/src/ts_reports.erl b/lib/test_server/src/ts_reports.erl
deleted file mode 100644
index f981a77ae4..0000000000
--- a/lib/test_server/src/ts_reports.erl
+++ /dev/null
@@ -1,545 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2010. 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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%% Purpose : Produces reports in HTML from the outcome of test suite runs.
-
--module(ts_reports).
-
--export([make_index/0, make_master_index/2, make_progress_index/2]).
--export([count_cases/1, year/0, current_time/0]).
-
--include_lib("kernel/include/file.hrl").
--include("ts.hrl").
-
--compile({no_auto_import,[error/1]}).
-
--import(filename, [basename/1, rootname/1]).
--import(ts_lib, [error/1]).
-
-
-%% Make master index page which points out index pages for all platforms.
-
-make_master_index(Dir, Vars) ->
- IndexName = filename:join(Dir, "index.html"),
- {ok, Index0} = make_master_index1(directories(Dir), master_header(Vars)),
- Index = [Index0|master_footer()],
- io:put_chars("Updating " ++ IndexName ++ "... "),
- ok = ts_lib:force_write_file(IndexName, Index),
- io:put_chars("done\n").
-
-make_master_index1([Dir|Rest], Result) ->
- NewResult =
- case catch read_variables(Dir) of
- {'EXIT',{{bad_installation,Reason},_}} ->
- io:put_chars("Failed to read " ++ filename:join(Dir,?variables)++
- ": " ++ Reason ++ " - Ignoring this directory\n"),
- Result;
- Vars ->
- Platform = ts_lib:var(platform_label, Vars),
- case make_index(Dir, Vars, false) of
- {ok, Summary} ->
- make_master_index(Platform, Dir, Summary, Result);
- {error, _} ->
- Result
- end
- end,
- make_master_index1(Rest, NewResult);
-make_master_index1([], Result) ->
- {ok, Result}.
-
-make_progress_index(Dir, Vars) ->
- IndexName = filename:join(Dir, "index.html"),
- io:put_chars("Updating " ++ IndexName ++ "... "),
- Index0=progress_header(Vars),
- ts_lib:force_delete(IndexName),
- Dirs=find_progress_runs(Dir),
- Index1=[Index0|make_progress_links(Dirs, [])],
- IndexF=[Index1|progress_footer()],
- ok = ts_lib:force_write_file(IndexName, IndexF),
- io:put_chars("done\n").
-
-find_progress_runs(Dir) ->
- case file:list_dir(Dir) of
- {ok, Dirs0} ->
- Dirs1= [filename:join(Dir,X) || X <- Dirs0,
- filelib:is_dir(filename:join(Dir,X))],
- lists:sort(Dirs1);
- _ ->
- []
- end.
-
-name_from_vars(Dir, Platform) ->
- VarFile=filename:join([Dir, Platform, "variables"]),
- case file:consult(VarFile) of
- {ok, Vars} ->
- ts_lib:var(platform_id, Vars);
- _Other ->
- Platform
- end.
-
-make_progress_links([], Acc) ->
- Acc;
-make_progress_links([RDir|Rest], Acc) ->
- Dir=filename:basename(RDir),
- Platforms=[filename:basename(X) ||
- X <- find_progress_runs(RDir)],
- PlatformLinks=["<A HREF=\""++filename:join([Dir,X,"index.html"])
- ++"\">"++name_from_vars(RDir, X)++"</A><BR>" ||
- X <- Platforms],
- LinkName=Dir++"/index.html",
- Link =
- [
- "<TR valign=top>\n",
- "<TD><A HREF=\"", LinkName, "\">", Dir, "</A></TD>", "\n",
- "<TD>", PlatformLinks, "</TD>", "\n"
- ],
- make_progress_links(Rest, [Link|Acc]).
-
-read_variables(Dir) ->
- case file:consult(filename:join(Dir, ?variables)) of
- {ok, Vars} -> Vars;
- {error, Reason} ->
- erlang:error({bad_installation,file:format_error(Reason)}, [Dir])
- end.
-
-make_master_index(Platform, Dirname, {Succ, Fail, UserSkip,AutoSkip}, Result) ->
- Link = filename:join(filename:basename(Dirname), "index.html"),
- FailStr =
- if Fail > 0 ->
- ["<FONT color=\"red\">",
- integer_to_list(Fail),"</FONT>"];
- true ->
- integer_to_list(Fail)
- end,
- AutoSkipStr =
- if AutoSkip > 0 ->
- ["<FONT color=\"brown\">",
- integer_to_list(AutoSkip),"</FONT>"];
- true -> integer_to_list(AutoSkip)
- end,
- [Result,
- "<TR valign=top>\n",
- "<TD><A HREF=\"", Link, "\">", Platform, "</A></TD>", "\n",
- make_row(integer_to_list(Succ), false),
- make_row(FailStr, false),
- make_row(integer_to_list(UserSkip), false),
- make_row(AutoSkipStr, false),
- "</TR>\n"].
-
-%% Make index page which points out individual test suites for a single platform.
-
-make_index() ->
- {ok, Pwd} = file:get_cwd(),
- Vars = read_variables(Pwd),
- make_index(Pwd, Vars, true).
-
-make_index(Dir, Vars, IncludeLast) ->
- IndexName = filename:absname("index.html", Dir),
- io:put_chars("Updating " ++ IndexName ++ "... "),
- case catch make_index1(Dir, IndexName, Vars, IncludeLast) of
- {'EXIT', Reason} ->
- io:put_chars("CRASHED!\n"),
- io:format("~p~n", [Reason]),
- {error, Reason};
- {error, Reason} ->
- io:put_chars("FAILED\n"),
- io:format("~p~n", [Reason]),
- {error, Reason};
- {ok, Summary} ->
- io:put_chars("done\n"),
- {ok, Summary};
- Err ->
- io:format("Unknown internal error. Please report.\n(Err: ~p, ID: 1)",
- [Err]),
- {error, Err}
- end.
-
-make_index1(Dir, IndexName, Vars, IncludeLast) ->
- Logs0 = ts_lib:interesting_logs(Dir),
- Logs =
- case IncludeLast of
- true -> add_last_name(Logs0);
- false -> Logs0
- end,
- {ok, {Index0, Summary}} = make_index(Logs, header(Vars), 0, 0, 0, 0, 0),
- Index = [Index0|footer()],
- case ts_lib:force_write_file(IndexName, Index) of
- ok ->
- {ok, Summary};
- {error, Reason} ->
- error({index_write_error, Reason})
- end.
-
-make_index([Name|Rest], Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt) ->
- case ts_lib:last_test(Name) of
- false ->
- %% Silently skip.
- make_index(Rest, Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt);
- Last ->
- case count_cases(Last) of
- {Succ, Fail, USkip, ASkip} ->
- Cov =
- case file:read_file(filename:join(Last,?cover_total)) of
- {ok,Bin} ->
- TotCoverage = binary_to_term(Bin),
- io_lib:format("~w %",[TotCoverage]);
- _error ->
- ""
- end,
- Link = filename:join(basename(Name), basename(Last)),
- JustTheName = rootname(basename(Name)),
- NotBuilt = not_built(JustTheName),
- NewResult = [Result, make_index1(JustTheName,
- Link, Succ, Fail, USkip, ASkip,
- NotBuilt, Cov, false)],
- make_index(Rest, NewResult, TotSucc+Succ, TotFail+Fail,
- UserSkip+USkip, AutoSkip+ASkip, TotNotBuilt+NotBuilt);
- error ->
- make_index(Rest, Result, TotSucc, TotFail, UserSkip, AutoSkip,
- TotNotBuilt)
- end
- end;
-make_index([], Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt) ->
- {ok, {[Result|make_index1("Total", no_link,
- TotSucc, TotFail, UserSkip, AutoSkip,
- TotNotBuilt, "", true)],
- {TotSucc, TotFail, UserSkip, AutoSkip}}}.
-
-make_index1(SuiteName, Link, Success, Fail, UserSkip, AutoSkip, NotBuilt, Coverage, Bold) ->
- Name = test_suite_name(SuiteName),
- FailStr =
- if Fail > 0 ->
- ["<FONT color=\"red\">",
- integer_to_list(Fail),"</FONT>"];
- true ->
- integer_to_list(Fail)
- end,
- AutoSkipStr =
- if AutoSkip > 0 ->
- ["<FONT color=\"brown\">",
- integer_to_list(AutoSkip),"</FONT>"];
- true -> integer_to_list(AutoSkip)
- end,
- ["<TR valign=top>\n",
- "<TD>",
- case Link of
- no_link ->
- ["<B>", Name|"</B>"];
- _Other ->
- CrashDumpName = SuiteName ++ "_erl_crash.dump",
- CrashDumpLink =
- case filelib:is_file(CrashDumpName) of
- true ->
- ["&nbsp;<A HREF=\"", CrashDumpName,
- "\">(CrashDump)</A>"];
- false ->
- ""
- end,
- LogFile = filename:join(Link, ?suitelog_name ++ ".html"),
- ["<A HREF=\"", LogFile, "\">", Name, "</A>\n", CrashDumpLink,
- "</TD>\n"]
- end,
- make_row(integer_to_list(Success), Bold),
- make_row(FailStr, Bold),
- make_row(integer_to_list(UserSkip), Bold),
- make_row(AutoSkipStr, Bold),
- make_row(integer_to_list(NotBuilt), Bold),
- make_row(Coverage, Bold),
- "</TR>\n"].
-
-make_row(Row, true) ->
- ["<TD ALIGN=right><B>", Row|"</B></TD>"];
-make_row(Row, false) ->
- ["<TD ALIGN=right>", Row|"</TD>"].
-
-not_built(BaseName) ->
- Dir = filename:join("..", BaseName++"_test"),
- Erl = length(filelib:wildcard(filename:join(Dir,"*_SUITE.erl"))),
- Beam = length(filelib:wildcard(filename:join(Dir,"*_SUITE.beam"))),
- Erl-Beam.
-
-
-%% Add the log file directory for the very last test run (according to
-%% last_name).
-
-add_last_name(Logs) ->
- case file:read_file("last_name") of
- {ok, Bin} ->
- Name = filename:dirname(lib:nonl(binary_to_list(Bin))),
- case lists:member(Name, Logs) of
- true -> Logs;
- false -> [Name|Logs]
- end;
- _ ->
- Logs
- end.
-
-term_to_text(Term) ->
- lists:flatten(io_lib:format("~p.\n", [Term])).
-
-test_suite_name(Name) ->
- ts_lib:initial_capital(Name) ++ " suite".
-
-directories(Dir) ->
- {ok, Files} = file:list_dir(Dir),
- [filename:join(Dir, X) || X <- Files,
- filelib:is_dir(filename:join(Dir, X))].
-
-
-%%% Headers and footers.
-
-header(Vars) ->
- Platform = ts_lib:var(platform_id, Vars),
- ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"
- "<!-- autogenerated by '"++atom_to_list(?MODULE)++"'. -->\n"
- "<HTML>\n",
- "<HEAD>\n",
- "<TITLE>Test Results for ", Platform, "</TITLE>\n",
- "</HEAD>\n",
-
- body_tag(),
-
- "<!-- ---- DOCUMENT TITLE ---- -->\n",
-
- "<CENTER>\n",
- "<H1>Test Results for ", Platform, "</H1>\n",
- "</CENTER>\n",
-
- "<!-- ---- CONTENT ---- -->\n",
- "<CENTER>\n",
-
- "<TABLE border=3 cellpadding=5>\n",
- "<th><B>Family</B></th>\n",
- "<th>Successful</th>\n",
- "<th>Failed</th>\n",
- "<th>User Skipped</th>\n"
- "<th>Auto Skipped</th>\n"
- "<th>Missing Suites</th>\n"
- "<th>Coverage</th>\n"
- "\n"].
-
-footer() ->
- ["</TABLE>\n"
- "</CENTER>\n"
- "<P><CENTER>\n"
- "<HR>\n"
- "<P><FONT SIZE=-1>\n"
- "Copyright &copy; ", year(),
- " <A HREF=\"http://erlang.ericsson.se\">Open Telecom Platform</A><BR>\n"
- "Updated: <!date>", current_time(), "<!/date><BR>\n"
- "</FONT>\n"
- "</CENTER>\n"
- "</body>\n"
- "</HTML>\n"].
-
-progress_header(Vars) ->
- Release = ts_lib:var(erl_release, Vars),
- ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"
- "<!-- autogenerated by '"++atom_to_list(?MODULE)++"'. -->\n"
- "<HTML>\n",
- "<HEAD>\n",
- "<TITLE>", Release, " Progress Test Results</TITLE>\n",
- "</HEAD>\n",
-
- body_tag(),
-
- "<!-- ---- DOCUMENT TITLE ---- -->\n",
-
- "<CENTER>\n",
- "<H1>", Release, " Progress Test Results</H1>\n",
- "<TABLE border=3 cellpadding=5>\n",
- "<th><b>Test Run</b></th><th>Platforms</th>\n"].
-
-progress_footer() ->
- ["</TABLE>\n",
- "</CENTER>\n",
- "<P><CENTER>\n",
- "<HR>\n",
- "<P><FONT SIZE=-1>\n",
- "Copyright &copy; ", year(),
- " <A HREF=\"http://erlang.ericsson.se\">Open Telecom Platform</A><BR>\n",
- "Updated: <!date>", current_time(), "<!/date><BR>\n",
- "</FONT>\n",
- "</CENTER>\n",
- "</body>\n",
- "</HTML>\n"].
-
-master_header(Vars) ->
- Release = ts_lib:var(erl_release, Vars),
- Vsn = erlang:system_info(version),
- ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"
- "<!-- autogenerated by '"++atom_to_list(?MODULE)++"'. -->\n"
- "<HTML>\n",
- "<HEAD>\n",
- "<TITLE>", Release, " Test Results (", Vsn, ")</TITLE>\n",
- "</HEAD>\n",
-
- body_tag(),
-
- "<!-- ---- DOCUMENT TITLE ---- -->\n",
-
- "<CENTER>\n",
- "<H1>", Release, " Test Results (", Vsn, ")</H1>\n",
- "</CENTER>\n",
-
- "<!-- ---- CONTENT ---- -->\n",
-
- "<CENTER>\n",
-
- "<TABLE border=3 cellpadding=5>\n",
- "<th><b>Platform</b></th>\n",
- "<th>Successful</th>\n",
- "<th>Failed</th>\n",
- "<th>User Skipped</th>\n"
- "<th>Auto Skipped</th>\n"
- "\n"].
-
-master_footer() ->
- ["</TABLE>\n",
- "</CENTER>\n",
- "<P><CENTER>\n",
- "<HR>\n",
- "<P><FONT SIZE=-1>\n",
- "Copyright &copy; ", year(),
- " <A HREF=\"http://erlang.ericsson.se\">Open Telecom Platform</A><BR>\n",
- "Updated: <!date>", current_time(), "<!/date><BR>\n",
- "</FONT>\n",
- "</CENTER>\n",
- "</body>\n",
- "</HTML>\n"].
-
-body_tag() ->
- "<body bgcolor=\"#FFFFFF\" text=\"#000000\" link=\"#0000FF\""
- "vlink=\"#800080\" alink=\"#FF0000\">".
-
-year() ->
- {Y, _, _} = date(),
- integer_to_list(Y).
-
-current_time() ->
- {{Y, Mon, D}, {H, Min, S}} = calendar:local_time(),
- Weekday = weekday(calendar:day_of_the_week(Y, Mon, D)),
- lists:flatten(io_lib:format("~s ~s ~p ~2.2.0w:~2.2.0w:~2.2.0w ~w",
- [Weekday, month(Mon), D, H, Min, S, Y])).
-
-weekday(1) -> "Mon";
-weekday(2) -> "Tue";
-weekday(3) -> "Wed";
-weekday(4) -> "Thu";
-weekday(5) -> "Fri";
-weekday(6) -> "Sat";
-weekday(7) -> "Sun".
-
-month(1) -> "Jan";
-month(2) -> "Feb";
-month(3) -> "Mar";
-month(4) -> "Apr";
-month(5) -> "May";
-month(6) -> "Jun";
-month(7) -> "Jul";
-month(8) -> "Aug";
-month(9) -> "Sep";
-month(10) -> "Oct";
-month(11) -> "Nov";
-month(12) -> "Dec".
-
-%% Count test cases in the given directory (a directory of the type
-%% run.1997-08-04_09.58.52).
-
-count_cases(Dir) ->
- SumFile = filename:join(Dir, ?run_summary),
- case read_summary(SumFile, [summary]) of
- {ok, [{Succ,Fail,Skip}]} ->
- {Succ,Fail,Skip,0};
- {ok, [Summary]} ->
- Summary;
- {error, _} ->
- LogFile = filename:join(Dir, ?suitelog_name),
- case file:read_file(LogFile) of
- {ok, Bin} ->
- Summary = count_cases1(binary_to_list(Bin), {0, 0, 0, 0}),
- write_summary(SumFile, Summary),
- Summary;
- {error, _Reason} ->
- io:format("\nFailed to read ~p (skipped)\n", [LogFile]),
- error
- end
- end.
-
-write_summary(Name, Summary) ->
- File = [term_to_text({summary, Summary})],
- ts_lib:force_write_file(Name, File).
-
-% XXX: This function doesn't do what the writer expect. It can't handle
-% the case if there are several different keys and I had to add a special
-% case for the empty file. The caller also expect just one tuple as
-% a result so this function is written way to general for no reason.
-% But it works sort of. /kgb
-
-read_summary(Name, Keys) ->
- case file:consult(Name) of
- {ok, []} ->
- {error, "Empty summary file"};
- {ok, Terms} ->
- {ok, lists:map(fun(Key) -> {value, {_, Value}} =
- lists:keysearch(Key, 1, Terms),
- Value end,
- Keys)};
- {error, Reason} ->
- {error, Reason}
- end.
-
-count_cases1("=failed" ++ Rest, {Success, _Fail, UserSkip,AutoSkip}) ->
- {NextLine, Count} = get_number(Rest),
- count_cases1(NextLine, {Success, Count, UserSkip,AutoSkip});
-count_cases1("=successful" ++ Rest, {_Success, Fail, UserSkip,AutoSkip}) ->
- {NextLine, Count} = get_number(Rest),
- count_cases1(NextLine, {Count, Fail, UserSkip,AutoSkip});
-count_cases1("=skipped" ++ Rest, {Success, Fail, _UserSkip,AutoSkip}) ->
- {NextLine, Count} = get_number(Rest),
- count_cases1(NextLine, {Success, Fail, Count,AutoSkip});
-count_cases1("=user_skipped" ++ Rest, {Success, Fail, _UserSkip,AutoSkip}) ->
- {NextLine, Count} = get_number(Rest),
- count_cases1(NextLine, {Success, Fail, Count,AutoSkip});
-count_cases1("=auto_skipped" ++ Rest, {Success, Fail, UserSkip,_AutoSkip}) ->
- {NextLine, Count} = get_number(Rest),
- count_cases1(NextLine, {Success, Fail, UserSkip,Count});
-count_cases1([], Counters) ->
- Counters;
-count_cases1(Other, Counters) ->
- count_cases1(skip_to_nl(Other), Counters).
-
-get_number([$\s|Rest]) ->
- get_number(Rest);
-get_number([Digit|Rest]) when $0 =< Digit, Digit =< $9 ->
- get_number(Rest, Digit-$0).
-
-get_number([Digit|Rest], Acc) when $0 =< Digit, Digit =< $9 ->
- get_number(Rest, Acc*10+Digit-$0);
-get_number([$\n|Rest], Acc) ->
- {Rest, Acc};
-get_number([_|Rest], Acc) ->
- get_number(Rest, Acc).
-
-skip_to_nl([$\n|Rest]) ->
- Rest;
-skip_to_nl([_|Rest]) ->
- skip_to_nl(Rest);
-skip_to_nl([]) ->
- [].
diff --git a/lib/test_server/src/ts_run.erl b/lib/test_server/src/ts_run.erl
index a61028e4bc..f4d5b3e3b1 100644
--- a/lib/test_server/src/ts_run.erl
+++ b/lib/test_server/src/ts_run.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -21,7 +21,7 @@
-module(ts_run).
--export([run/4]).
+-export([run/4,ct_run_test/2]).
-define(DEFAULT_MAKE_TIMETRAP_MINUTES, 60).
-define(DEFAULT_UNMAKE_TIMETRAP_MINUTES, 15).
@@ -87,6 +87,24 @@ execute([Hook|Rest], Vars0, Spec0, St0) ->
execute([], Vars, Spec, St) ->
{ok, Vars, Spec, St}.
+%% Wrapper to run tests using ct:run_test/1 and handle any errors.
+
+ct_run_test(Dir, CommonTestArgs) ->
+ try
+ ok = file:set_cwd(Dir),
+ case ct:run_test(CommonTestArgs) of
+ {_,_,_} ->
+ ok;
+ {error,Error} ->
+ io:format("ERROR: ~P\n", [Error,20]);
+ Other ->
+ io:format("~P\n", [Other,20])
+ end
+ catch
+ _:Crash ->
+ io:format("CRASH: ~P\n", [Crash,20])
+ end.
+
%%
%% Deletes File from Files when File is on the form .../<SUITE>_data/<file>
%% when all of <SUITE> has been skipped in Spec, i.e. there
@@ -157,7 +175,6 @@ get_config_files() ->
[TSConfig | case os:type() of
{unix,_} -> ["ts.unix.config"];
{win32,_} -> ["ts.win32.config"];
- vxworks -> ["ts.vxworks.config"];
_ -> []
end].
@@ -231,8 +248,7 @@ make_command(Vars, Spec, State) ->
" -boot start_sasl -sasl errlog_type error",
" -pz \"",Cwd,"\"",
" -ct_test_vars ",TestVars,
- " -eval \"file:set_cwd(\\\"",TestDir,"\\\")\" "
- " -eval \"ct:run_test(",
+ " -eval \"ts_run:ct_run_test(\\\"",TestDir,"\\\", ",
backslashify(lists:flatten(State#state.test_server_args)),")\""
" ",
ExtraArgs],
@@ -329,14 +345,13 @@ start_xterm(Command) ->
path_separator() ->
case os:type() of
{win32, _} -> ";";
- {unix, _} -> ":";
- vxworks -> ":"
+ {unix, _} -> ":"
end.
-make_common_test_args(Args0, Options, _Vars) ->
+make_common_test_args(Args0, Options0, _Vars) ->
Trace =
- case lists:keysearch(trace,1,Options) of
+ case lists:keysearch(trace,1,Options0) of
{value,{trace,TI}} when is_tuple(TI); is_tuple(hd(TI)) ->
ok = file:write_file(?tracefile,io_lib:format("~p.~n",[TI])),
[{ct_trace,?tracefile}];
@@ -348,7 +363,7 @@ make_common_test_args(Args0, Options, _Vars) ->
[]
end,
Cover =
- case lists:keysearch(cover,1,Options) of
+ case lists:keysearch(cover,1,Options0) of
{value,{cover, App, none, _Analyse}} ->
io:format("No cover file found for ~p~n",[App]),
[];
@@ -358,7 +373,7 @@ make_common_test_args(Args0, Options, _Vars) ->
[]
end,
- Logdir = case lists:keysearch(logdir, 1, Options) of
+ Logdir = case lists:keysearch(logdir, 1, Options0) of
{value,{logdir, _}} ->
[];
false ->
@@ -373,15 +388,16 @@ make_common_test_args(Args0, Options, _Vars) ->
{scale_timetraps, true}]
end,
- ConfigPath = case {os:getenv("TEST_CONFIG_PATH"),
- lists:keysearch(config, 1, Options)} of
- {false,{value, {config, Path}}} ->
- Path;
- {false,false} ->
- "../test_server";
- {Path,_} ->
- Path
- end,
+ {ConfigPath,
+ Options} = case {os:getenv("TEST_CONFIG_PATH"),
+ lists:keysearch(config, 1, Options0)} of
+ {_,{value, {config, Path}}} ->
+ {Path,lists:keydelete(config, 1, Options0)};
+ {false,false} ->
+ {"../test_server",Options0};
+ {Path,_} ->
+ {Path,Options0}
+ end,
ConfigFiles = [{config,[filename:join(ConfigPath,File)
|| File <- get_config_files()]}],
io_lib:format("~100000p",[Args0++Trace++Cover++Logdir++
diff --git a/lib/test_server/src/ts_selftest.erl b/lib/test_server/src/ts_selftest.erl
deleted file mode 100644
index 655aa4bab3..0000000000
--- a/lib/test_server/src/ts_selftest.erl
+++ /dev/null
@@ -1,120 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. 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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(ts_selftest).
--export([selftest/0]).
-
-selftest() ->
- case node() of
- nonode@nohost ->
- io:format("Sorry, you have to start this node distributed.~n"),
- exit({error, node_not_distributed});
- _ ->
- ok
- end,
- case catch ts:tests(test_server) of
- {'EXIT', _} ->
- io:format("Test Server self test not availiable.");
- Other ->
- selftest1()
- end.
-
-selftest1() ->
- % Batch starts
- io:format("Selftest #1: Whole spec, batch mode:~n"),
- io:format("------------------------------------~n"),
- ts:run(test_server, [batch]),
- ok=check_result(1, "test_server.logs", 2),
-
- io:format("Selftest #2: One module, batch mode:~n"),
- io:format("------------------------------------~n"),
- ts:run(test_server, test_server_SUITE, [batch]),
- ok=check_result(2, "test_server_SUITE.logs", 2),
-
- io:format("Selftest #3: One testcase, batch mode:~n"),
- io:format("--------------------------------------~n"),
- ts:run(test_server, test_server_SUITE, msgs, [batch]),
- ok=check_result(3, "test_server_SUITE.logs", 0),
-
- % Interactive starts
- io:format("Selftest #4: Whole spec, interactive mode:~n"),
- io:format("------------------------------------------~n"),
- ts:run(test_server),
- kill_test_server(),
- ok=check_result(4, "test_server.logs", 2),
-
- io:format("Selftest #5: One module, interactive mode:~n"),
- io:format("------------------------------------------~n"),
- ts:run(test_server, test_server_SUITE),
- kill_test_server(),
- ok=check_result(5, "test_server_SUITE.logs", 2),
-
- io:format("Selftest #6: One testcase, interactive mode:~n"),
- io:format("--------------------------------------------~n"),
- ts:run(test_server, test_server_SUITE, msgs),
- kill_test_server(),
- ok=check_result(6, "test_server_SUITE.logs", 0),
-
- ok.
-
-check_result(Test, TDir, ExpSkip) ->
- Dir=ts_lib:last_test(TDir),
- {Total, Failed, Skipped}=ts_reports:count_cases(Dir),
- io:format("Selftest #~p:",[Test]),
- case {Total, Failed, Skipped} of
- {_, 0, ExpSkip} -> % 2 test cases should be skipped.
- io:format("All ok.~n~n"),
- ok;
- {_, _, _} ->
- io:format("Not completely successful.~n~n"),
- error
- end.
-
-
-%% Wait for test server to get started.
-kill_test_server() ->
- Node=list_to_atom("test_server@"++atom_to_list(hostname())),
- net_adm:ping(Node),
- case whereis(test_server_ctrl) of
- undefined ->
- kill_test_server();
- Pid ->
- kill_test_server(0, Pid)
- end.
-
-%% Wait for test server to finish.
-kill_test_server(30, Pid) ->
- exit(self(), test_server_is_dead);
-kill_test_server(Num, Pid) ->
- case whereis(test_server_ctrl) of
- undefined ->
- slave:stop(node(Pid));
- Pid ->
- receive
- after
- 1000 ->
- kill_test_server(Num+1, Pid)
- end
- end.
-
-
-hostname() ->
- list_to_atom(from($@, atom_to_list(node()))).
-from(H, [H | T]) -> T;
-from(H, [_ | T]) -> from(H, T);
-from(H, []) -> [].
diff --git a/lib/test_server/src/vxworks_client.erl b/lib/test_server/src/vxworks_client.erl
deleted file mode 100644
index ca65eca02a..0000000000
--- a/lib/test_server/src/vxworks_client.erl
+++ /dev/null
@@ -1,243 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2002-2009. 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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(vxworks_client).
-
--export([open/1, close/1, send_data/2, send_data/3, send_data_wait_for_close/2, reboot/1]).
--export([init/2]).
-
--include("ts.hrl").
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%% This is a client talking to a test server daemon on a VxWorks card.
-%%%
-%%% User interface:
-%%%
-%%% open/1
-%%% Start a client and establish the connection with the test server daemon
-%%%
-%%% send_data/2
-%%% Send data/command to the test server daemon, don't wait for any return
-%%%
-%%% send_data/3
-%%% Send data/command to the test server daemon and wait for the given
-%%% return value.
-%%%
-%%% send_data_wait_for_close/2
-%%% Send data/command to the test server daemon and wait for the daemon to
-%%% close the connection.
-%%%
-%%% close/1
-%%% Close the client.
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-%%
-%% User interface
-%%
-
-reboot(Target) ->
- {ok, {_,_,_,_,_,[Addr|_]}} = inet:gethostbyname(Target),
- Fun = fun({ok, Socket}) ->
- gen_tcp:send(Socket, "q\n"),
- receive
- {tcp_closed, Socket} ->
- gen_tcp:close(Socket),
- {ok, socket_closed}
- after 5000 ->
- exit({timeout, tryagain})
- end
- end,
- io:format("Stopping (rebooting) ~p ",[Target]),
- case fun_target(Addr, Fun) of
- {ok, socket_closed} ->
- ok;
- _Else ->
- io:format("No contact with ts daemon - exiting ...~n"),
- exit({stop, no_ts_daemon_contact})
- end.
-
-
-%% open(Target) -> {ok,Client} | {error, Reason}
-open(Target) ->
- {ok, {_,_,_,_,_,[Addr|_]}} = inet:gethostbyname(Target),
- Fun = fun({ok, Socket}) ->
- P = spawn(?MODULE,init,[Target,Socket]),
- inet_tcp:controlling_process(Socket,P),
- {ok,P}
- end,
- case fun_target(Addr,Fun) of
- {ok, Pid} ->
- {ok, Pid};
- {error,Reason} ->
- {error, Reason}
- end.
-
-%% send_data(Client,Data) -> ok
-send_data(Pid,Data) ->
- Pid ! {send_data,Data++"\n"},
- ok.
-
-%% send_data(Client,Data,ExpectedReturn) -> {ok,ExpectedReturn} | {error,Reason}
-send_data(Pid,Data,Return) ->
- Pid ! {send_data,Data++"\n",Return,self()},
- receive {Pid,Result} -> Result end.
-
-%% send_data_wait_for_close(Client,Data) -> ok | {error,Reason}
-send_data_wait_for_close(Pid,Data) ->
- send_data(Pid,Data,tcp_closed).
-
-%% close(Client) -> ok
-close(Pid) ->
- Pid ! close,
- ok.
-
-
-%%
-%% Internal
-%%
-
-init(Target,Socket) ->
- process_flag(trap_exit,true),
- loop(Target,Socket).
-
-loop(Target,Socket) ->
- receive
- {send_data,Data} ->
- %% io:format("vx client sending: ~p~n", [Data]),
- gen_tcp:send(Socket, Data),
- loop(Socket,Target);
- {send_data,Data,tcp_closed,From} ->
- %% io:format("vx client sending: ~p~n", [Data]),
- gen_tcp:send(Socket, Data),
- receive
- {tcp_closed, Socket} ->
- From ! {self(),ok}
- after 5000 ->
- From ! {self(),{error,timeout}}
- end,
- closed(Socket,normal);
- {send_data,Data,Return,From} ->
- %% io:format("vx client sending: ~p~n", [Data]),
- gen_tcp:send(Socket, Data),
- case receive_line(Socket,[],Return,200) of
- {tcp_closed, Socket} ->
- From ! {self(),{error,{socket_closed,Target}}},
- closed(Socket,{socket_closed,Target});
- {tcp,Socket,_Rest} ->
- From ! {self(),{ok,Data}},
- got_data(Target,Socket,Data);
- error ->
- From ! {self(),{error,{catatonic,Target}}}
- end;
- close ->
- closed(Socket,normal);
- {tcp_closed, Socket} ->
- closed(Socket,{socket_closed,Target});
- {tcp,Socket,Data} ->
- got_data(Target,Socket,Data)
- end.
-
-
-
-closed(Socket,Reason) ->
- gen_tcp:close(Socket),
- exit(Reason).
-
-got_data(Target,Socket,Data) ->
- if is_atom(Target) ->
- io:format("~w: ~s",[Target,uncr(Data)]);
- true ->
- io:format("~s: ~s",[Target,uncr(Data)])
- end,
- loop(Target,Socket).
-
-uncr([]) ->
- [];
-uncr([$\r | T]) ->
- uncr(T);
-uncr([H | T]) ->
- [H | uncr(T)].
-
-strip_line(Line) ->
- RPos = string:rchr(Line, $\n),
- string:substr(Line,RPos+1).
-
-maybe_done_receive(Socket,Ack,Match,C) ->
- case string:str(Ack,Match) of
- 0 ->
- receive_line(Socket,strip_line(Ack),Match,C);
- _ ->
- {tcp,Socket,strip_line(Ack)}
- end.
-
-
-receive_line(_Socket,_Ack,_Match,0) ->
- error;
-receive_line(Socket,Ack,Match,Counter) ->
- receive
- {tcp_closed, Socket} ->
- {tcp_closed, Socket};
- {tcp,Socket,Data} ->
- NewAck = Ack ++ Data,
- case {string:str(NewAck,"\r") > 0,
- string:str(NewAck,"\n") > 0} of
- {true,_} ->
- maybe_done_receive(Socket,NewAck,Match,Counter-1);
- {_,true} ->
- maybe_done_receive(Socket,NewAck,Match,Counter-1);
- _ ->
- receive_line(Socket,NewAck,Match,Counter)
- end
- after 20000 ->
- error
- end.
-
-
-%% Misc functions
-fun_target(Addr, Fun) ->
- io:format("["),
- fun_target(Addr, Fun, 60). %Vx-cards need plenty of time.
-
-fun_target(_Addr, _Fun, 0) ->
- io:format(" no contact with ts daemon]~n"),
- {error,failed_to_connect};
-fun_target(Addr, Fun, Tries_left) ->
- receive after 1 -> ok end,
- case do_connect(Addr, Fun) of
- {ok, Value} ->
- io:format(" ok]~n"),
- {ok, Value};
- _Error -> % typical {error, econnrefused}
- io:format("."),
- receive after 10000 -> ok end,
- fun_target(Addr, Fun, Tries_left-1)
- end.
-
-do_connect(Addr, Fun) ->
- case gen_tcp:connect(Addr, ?TS_PORT, [{reuseaddr, true}], 60000) of
- {ok, Socket} ->
- Fun({ok, Socket});
- Error ->
- Error
- end.
-
-
-
diff --git a/lib/test_server/test/Makefile b/lib/test_server/test/Makefile
index b2ac95afaa..afe5aff196 100644
--- a/lib/test_server/test/Makefile
+++ b/lib/test_server/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/test_server/test/test_server.cover b/lib/test_server/test/test_server.cover
index 5c59bab494..c16212567e 100644
--- a/lib/test_server/test/test_server.cover
+++ b/lib/test_server/test/test_server.cover
@@ -11,12 +11,11 @@
test_server_node,
test_server_sup,
ts,
- ts_autoconf_vxworks,
ts_autoconf_win32,
ts_erl_config,
ts_install,
ts_lib,
ts_make,
- ts_run,
- vxworks_client]}.
+ ts_run
+ ]}.
diff --git a/lib/test_server/vsn.mk b/lib/test_server/vsn.mk
index a1f4559083..aecf595f3f 100644
--- a/lib/test_server/vsn.mk
+++ b/lib/test_server/vsn.mk
@@ -1 +1 @@
-TEST_SERVER_VSN = 3.5.1
+TEST_SERVER_VSN = 3.5.2
diff --git a/lib/toolbar/doc/src/Makefile b/lib/toolbar/doc/src/Makefile
index af907859d7..2239421556 100644
--- a/lib/toolbar/doc/src/Makefile
+++ b/lib/toolbar/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/toolbar/doc/src/notes.xml b/lib/toolbar/doc/src/notes.xml
index b901e6c8ff..f3ed41bd82 100644
--- a/lib/toolbar/doc/src/notes.xml
+++ b/lib/toolbar/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/toolbar/src/Makefile b/lib/toolbar/src/Makefile
index 91ce305e26..102970a59a 100644
--- a/lib/toolbar/src/Makefile
+++ b/lib/toolbar/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/tools/doc/src/Makefile b/lib/tools/doc/src/Makefile
index a76ce98ad6..e3a2d64041 100644
--- a/lib/tools/doc/src/Makefile
+++ b/lib/tools/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/tools/doc/src/eprof.xml b/lib/tools/doc/src/eprof.xml
index 1c5e38109b..82eb8dd284 100644
--- a/lib/tools/doc/src/eprof.xml
+++ b/lib/tools/doc/src/eprof.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml
index f4da132102..5d7bd464ce 100644
--- a/lib/tools/doc/src/notes.xml
+++ b/lib/tools/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,69 @@
</header>
<p>This document describes the changes made to the Tools application.</p>
+<section><title>Tools 2.6.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The last tuple fun call has been removed from fprof.</p>
+ <p>
+ Own Id: OTP-10091 Aux Id: seq12067 </p>
+ </item>
+ <item>
+ <p>
+ Fix indentation of record fields in Emacs (Thanks to
+ Tomas Abrahamsson)</p>
+ <p>
+ Own Id: OTP-10120</p>
+ </item>
+ <item>
+ <p>
+ Documentation fixes (Thanks to Ricardo Catalinas Jim�nez
+ )</p>
+ <p>
+ Own Id: OTP-10121</p>
+ </item>
+ <item>
+ <p>
+ Remove Erlang-specific compilation error regexp in
+ erlang.el</p>
+ <p>
+ Own Id: OTP-10168</p>
+ </item>
+ <item>
+ <p>
+ Fix highlighting of atoms ending with a dollar sign</p>
+ <p>
+ Like this: 'atom$'. In that example, the last single
+ quote should be recognised as ending the atom. This needs
+ a font-lock workaround similar to the one for strings.
+ Thanks to Magnus Henoch</p>
+ <p>
+ Own Id: OTP-10178</p>
+ </item>
+ <item>
+ <p> Xref now accepts filenames with character codes
+ greater than 126. (Thanks to Emile Joubert for reporting
+ the issue.) </p>
+ <p>
+ Own Id: OTP-10192</p>
+ </item>
+ <item>
+ <p>
+ Add test_indentation target to lib/tools/emacs/Makefile</p>
+ <p>
+ Automatically indent test.erl.orig, save to test.erl, and
+ compare to test.erl.intended. Thanks to Magnus Henoch.</p>
+ <p>
+ Own Id: OTP-10226</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Tools 2.6.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/tools/doc/src/xref_chapter.xml b/lib/tools/doc/src/xref_chapter.xml
index 39c5545af9..566776eab0 100644
--- a/lib/tools/doc/src/xref_chapter.xml
+++ b/lib/tools/doc/src/xref_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2009</year>
+ <year>2000</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -301,7 +301,7 @@
and <c>|||</c>) are the only operators that accept both
representations. This means that in order to analyze indirect
calls using restriction, the <c>closure</c> operator (which creates the
- <c>digraph</c> representation of graphs) has to been
+ <c>digraph</c> representation of graphs) has to be
applied explicitly.
</p>
<p>As an example of analyzing indirect calls, the following Erlang
diff --git a/lib/tools/emacs/Makefile b/lib/tools/emacs/Makefile
index e5ed56b412..69946be24a 100644
--- a/lib/tools/emacs/Makefile
+++ b/lib/tools/emacs/Makefile
@@ -86,3 +86,19 @@ release_docs_spec: docs
$(INSTALL_DATA) $(MAN_FILES) "$(RELEASE_PATH)/man/man3"
endif
endif
+
+EMACS ?= emacs
+
+test_indentation:
+ @rm -f test.erl
+ @rm -f test_indent.el
+ @echo '(load "erlang-start")' >> test_indent.el
+ @echo '(find-file "test.erl.orig")' >> test_indent.el
+ @echo "(require 'cl) ; required with Emacs < 23 for ignore-errors" >> test_indent.el
+ @echo '(erlang-mode)' >> test_indent.el
+ @echo '(toggle-debug-on-error)' >> test_indent.el
+ @echo '(erlang-indent-current-buffer)' >> test_indent.el
+ @echo '(write-file "test.erl")' >> test_indent.el
+ $(EMACS) --batch -Q -L . -l test_indent.el
+ diff -u test.erl.indented test.erl
+ @echo "No differences between expected and actual indentation"
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 2f6c7f554e..aae118f3db 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -1,7 +1,7 @@
;; erlang.el --- Major modes for editing and running Erlang
;; %CopyrightBegin%
;;
-;; Copyright Ericsson AB 1996-2011. All Rights Reserved.
+;; Copyright Ericsson AB 1996-2012. 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
@@ -483,10 +483,6 @@ function.")
"*Non-nil means TAB in Erlang mode should always re-indent the current line,
regardless of where in the line point is when the TAB command is used.")
-(defvar erlang-error-regexp-alist
- '(("^\\([^:( \t\n]+\\)[:(][ \t]*\\([0-9]+\\)[:) \t]" . (1 2)))
- "*Patterns for matching Erlang errors.")
-
(defvar erlang-man-inhibit (eq system-type 'windows-nt)
"Inhibit the creation of the Erlang Manual Pages menu.
@@ -1327,7 +1323,6 @@ Other commands:
(erlang-menu-init)
(erlang-mode-variables)
(erlang-check-module-name-init)
- (erlang-add-compilation-alist erlang-error-regexp-alist)
(erlang-man-init)
(erlang-tags-init)
(erlang-font-lock-init)
@@ -1443,31 +1438,6 @@ Other commands:
(set (make-local-variable 'outline-level) (lambda () 1))
(set (make-local-variable 'add-log-current-defun-function)
'erlang-current-defun))
-
-
-;; Compilation.
-;;
-;; The following code is compatible with the standard package `compilation',
-;; making it possible to go to errors using `erlang-next-error' (or just
-;; `next-error' in Emacs 21).
-;;
-;; The normal `compile' command works of course. For best result, please
-;; execute `make' with the `-w' flag.
-;;
-;; Please see the variables named `compiling-..' above.
-
-(defun erlang-add-compilation-alist (alist)
- (require 'compile)
- (cond ((boundp 'compilation-error-regexp-alist) ; Emacs 19
- (while alist
- (or (assoc (car (car alist)) compilation-error-regexp-alist)
- (setq compilation-error-regexp-alist
- (cons (car alist) compilation-error-regexp-alist)))
- (setq alist (cdr alist))))
- ((boundp 'compilation-error-regexp)
- ;; Emacs 18, Only one regexp is allowed.
- (funcall (symbol-function 'set)
- 'compilation-error-regexp (car (car alist))))))
(defun erlang-font-lock-init ()
"Initialize Font Lock for Erlang mode."
@@ -1516,7 +1486,7 @@ Other commands:
;; A dollar sign right before the double quote that ends a
;; string is not a character escape.
;;
- ;; And a "string" has with a double quote not escaped by a
+ ;; And a "string" consists of a double quote not escaped by a
;; dollar sign, any number of non-backslash non-newline
;; characters or escaped backslashes, a dollar sign
;; (otherwise we wouldn't care) and a double quote. This
@@ -1525,6 +1495,8 @@ Other commands:
;; know whether matching started inside a string: limiting
;; search to a single line keeps things sane.
. (("\\(?:^\\|[^$]\\)\"\\(?:[^\"\n]\\|\\\\\"\\)*\\(\\$\\)\"" 1 "w")
+ ;; Likewise for atoms
+ ("\\(?:^\\|[^$]\\)'\\(?:[^'\n]\\|\\\\'\\)*\\(\\$\\)'" 1 "w")
;; And the dollar sign in $\" escapes two characters, not
;; just one.
("\\(\\$\\)\\\\\\\"" 1 "'"))))))
@@ -4894,7 +4866,6 @@ The following special commands are available:
(set (make-local-variable 'compilation-old-error-list) nil))
;; Needed when compiling directly from the Erlang shell.
(setq compilation-last-buffer (current-buffer))
- (erlang-add-compilation-alist erlang-error-regexp-alist)
(setq comint-prompt-regexp "^[^>=]*> *")
(setq comint-eol-on-send t)
(setq comint-input-ignoredups t)
diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented
index e0593c6522..6c9343f6cb 100644
--- a/lib/tools/emacs/test.erl.indented
+++ b/lib/tools/emacs/test.erl.indented
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. 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
@@ -215,6 +215,11 @@ highlighting(X) % Function definitions should be highlighted
"char $in string", atom,
+ 'atom$', atom, 'atom$', atom,
+ 'atom\$', atom,
+
+ 'char $in atom', atom,
+
$[, ${, $\\, atom,
?MACRO_1,
?MACRO_2(foo),
diff --git a/lib/tools/emacs/test.erl.orig b/lib/tools/emacs/test.erl.orig
index 69356aca9e..0f8c4a9175 100644
--- a/lib/tools/emacs/test.erl.orig
+++ b/lib/tools/emacs/test.erl.orig
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. 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
@@ -215,6 +215,11 @@ highlighting(X) % Function definitions should be highlighted
"char $in string", atom,
+ 'atom$', atom, 'atom$', atom,
+ 'atom\$', atom,
+
+ 'char $in atom', atom,
+
$[, ${, $\\, atom,
?MACRO_1,
?MACRO_2(foo),
diff --git a/lib/tools/src/Makefile b/lib/tools/src/Makefile
index 4c0cc8cde7..abe1389771 100644
--- a/lib/tools/src/Makefile
+++ b/lib/tools/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2010. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/lib/tools/src/tools.app.src b/lib/tools/src/tools.app.src
index cd9b622f15..94998fb763 100644
--- a/lib/tools/src/tools.app.src
+++ b/lib/tools/src/tools.app.src
@@ -24,6 +24,7 @@
eprof,
fprof,
instrument,
+ lcnt,
make,
xref,
xref_base,
diff --git a/lib/tools/src/xref_utils.erl b/lib/tools/src/xref_utils.erl
index 9d4a175d88..680563e9df 100644
--- a/lib/tools/src/xref_utils.erl
+++ b/lib/tools/src/xref_utils.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2012. 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
@@ -141,7 +141,7 @@ is_string([], _) ->
is_string(Term, C) ->
is_string1(Term, C).
-is_string1([H | T], C) when H > C, H < 127 ->
+is_string1([H | T], C) when H > C ->
is_string1(T, C);
is_string1([], _) ->
true;
diff --git a/lib/tools/test/Makefile b/lib/tools/test/Makefile
index b9a42041a2..86c81217b6 100644
--- a/lib/tools/test/Makefile
+++ b/lib/tools/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl
index 0cd53a05db..c2c708d806 100644
--- a/lib/tools/test/cover_SUITE.erl
+++ b/lib/tools/test/cover_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2012. 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/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl
index 78e49044a5..fd3e111d8d 100644
--- a/lib/tools/test/xref_SUITE.erl
+++ b/lib/tools/test/xref_SUITE.erl
@@ -53,7 +53,7 @@
analyze/1, basic/1, md/1, q/1, variables/1, unused_locals/1]).
-export([
- format_error/1, otp_7423/1, otp_7831/1]).
+ format_error/1, otp_7423/1, otp_7831/1, otp_10192/1]).
-import(lists, [append/2, flatten/1, keysearch/3, member/2, sort/1, usort/1]).
@@ -86,7 +86,7 @@ groups() ->
fun_mfa_r14, fun_mfa_vars, qlc]},
{analyses, [],
[analyze, basic, md, q, variables, unused_locals]},
- {misc, [], [format_error, otp_7423, otp_7831]}].
+ {misc, [], [format_error, otp_7423, otp_7831, otp_10192]}].
init_per_suite(Config) ->
init(Config).
@@ -2515,6 +2515,18 @@ otp_7831(Conf) when is_list(Conf) ->
?line xref:stop(Pid2),
ok.
+otp_10192(suite) -> [];
+otp_10192(doc) ->
+ ["OTP-10192. Allow filenames with character codes greater than 126."];
+otp_10192(Conf) when is_list(Conf) ->
+ PrivDir = ?privdir,
+ {ok, _Pid} = xref:start(s),
+ Dir = filename:join(PrivDir, "�"),
+ ok = file:make_dir(Dir),
+ {ok, []} = xref:add_directory(s, Dir),
+ xref:stop(s),
+ ok.
+
%%%
%%% Utilities
%%%
diff --git a/lib/tv/doc/src/Makefile b/lib/tv/doc/src/Makefile
index e36dd84b73..413eb8f634 100644
--- a/lib/tv/doc/src/Makefile
+++ b/lib/tv/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/tv/doc/src/notes.xml b/lib/tv/doc/src/notes.xml
index 9217c018e9..fa72dd23a9 100644
--- a/lib/tv/doc/src/notes.xml
+++ b/lib/tv/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/tv/priv/Makefile b/lib/tv/priv/Makefile
index 73ea8f4ee9..679a76fe88 100644
--- a/lib/tv/priv/Makefile
+++ b/lib/tv/priv/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/tv/src/Makefile b/lib/tv/src/Makefile
index f078e5dee1..da1713e156 100644
--- a/lib/tv/src/Makefile
+++ b/lib/tv/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/lib/typer/src/Makefile b/lib/typer/src/Makefile
index c3fae0ad42..1f94d8fdc8 100644
--- a/lib/typer/src/Makefile
+++ b/lib/typer/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2011. All Rights Reserved.
+# Copyright Ericsson AB 2006-2012. 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/lib/typer/src/typer.erl b/lib/typer/src/typer.erl
index 1e40b8926e..0ace1d5fb8 100644
--- a/lib/typer/src/typer.erl
+++ b/lib/typer/src/typer.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. 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
@@ -1007,7 +1007,7 @@ msg(Msg) ->
port_command(P, Msg),
true = port_close(P),
ok;
- _ -> % win32, vxworks
+ _ -> % win32
io:format("~s", [Msg])
end.
diff --git a/lib/typer/vsn.mk b/lib/typer/vsn.mk
index 85a7c01424..18e328afc1 100644
--- a/lib/typer/vsn.mk
+++ b/lib/typer/vsn.mk
@@ -1 +1 @@
-TYPER_VSN = 0.9.3
+TYPER_VSN = 0.9.4
diff --git a/lib/webtool/src/Makefile b/lib/webtool/src/Makefile
index 45e8b2a6f0..783ffad79a 100644
--- a/lib/webtool/src/Makefile
+++ b/lib/webtool/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2009. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. 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/lib/wx/aclocal.m4 b/lib/wx/aclocal.m4
index 339a15a2bb..b1cf1fe404 100644
--- a/lib/wx/aclocal.m4
+++ b/lib/wx/aclocal.m4
@@ -1,7 +1,7 @@
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1998-2011. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2012. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
@@ -59,6 +59,7 @@ AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only us
dnl Cross compilation variables
AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)])
+AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)])
AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)])
@@ -606,6 +607,103 @@ ifelse([$5], , , [$5
fi
])
+dnl ----------------------------------------------------------------------
+dnl
+dnl AC_DOUBLE_MIDDLE_ENDIAN
+dnl
+dnl Checks whether doubles are represented in "middle-endian" format.
+dnl Sets ac_cv_double_middle_endian={no,yes,unknown} accordingly,
+dnl as well as DOUBLE_MIDDLE_ENDIAN.
+dnl
+dnl
+
+AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN],
+[AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian,
+[# It does not; compile a test program.
+AC_RUN_IFELSE(
+[AC_LANG_SOURCE([[#include <stdlib.h>
+
+int
+main(void)
+{
+ int i = 0;
+ int zero = 0;
+ int bigendian;
+ int zero_index = 0;
+
+ union
+ {
+ long int l;
+ char c[sizeof (long int)];
+ } u;
+
+ /* we'll use the one with 32-bit words */
+ union
+ {
+ double d;
+ unsigned int c[2];
+ } vint;
+
+ union
+ {
+ double d;
+ unsigned long c[2];
+ } vlong;
+
+ union
+ {
+ double d;
+ unsigned short c[2];
+ } vshort;
+
+
+ /* Are we little or big endian? From Harbison&Steele. */
+ u.l = 1;
+ bigendian = (u.c[sizeof (long int) - 1] == 1);
+
+ zero_index = bigendian ? 1 : 0;
+
+ vint.d = 1.0;
+ vlong.d = 1.0;
+ vshort.d = 1.0;
+
+ if (sizeof(unsigned int) == 4)
+ {
+ if (vint.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned long) == 4)
+ {
+ if (vlong.c[zero_index] != 0)
+ zero = 1;
+ }
+ else if (sizeof(unsigned short) == 4)
+ {
+ if (vshort.c[zero_index] != 0)
+ zero = 1;
+ }
+
+ exit (zero);
+}
+]])],
+ [ac_cv_c_double_middle_endian=no],
+ [ac_cv_c_double_middle_endian=yes],
+ [ac_cv_c_double_middle=unknown])])
+case $ac_cv_c_double_middle_endian in
+ yes)
+ m4_default([$1],
+ [AC_DEFINE([DOUBLE_MIDDLE_ENDIAN], 1,
+ [Define to 1 if your processor stores the words in a double in
+ middle-endian format (like some ARMs).])]) ;;
+ no)
+ $2 ;;
+ *)
+ m4_default([$3],
+ [AC_MSG_WARN([unknown double endianness
+presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;;
+esac
+])# AC_C_DOUBLE_MIDDLE_ENDIAN
+
dnl ----------------------------------------------------------------------
dnl
@@ -1337,6 +1435,14 @@ if test "$ac_cv_c_bigendian" = "yes"; then
AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian])
fi
+case X$erl_xcomp_double_middle_endian in
+ X) ;;
+ Xyes|Xno|Xunknown) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;;
+ *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);;
+esac
+
+AC_C_DOUBLE_MIDDLE_ENDIAN
+
AC_ARG_ENABLE(native-ethr-impls,
AS_HELP_STRING([--disable-native-ethr-impls],
[disable native ethread implementations]),
diff --git a/lib/wx/api_gen/wx_extra/wxEvtHandler.erl b/lib/wx/api_gen/wx_extra/wxEvtHandler.erl
index 23a34225ca..c5802af679 100644
--- a/lib/wx/api_gen/wx_extra/wxEvtHandler.erl
+++ b/lib/wx/api_gen/wx_extra/wxEvtHandler.erl
@@ -61,7 +61,7 @@ connect(This, EventType) ->
%% {userData, term()} An erlang term that will be sent with the event. Default: [].
-spec connect(This::wxEvtHandler(), EventType::wxEventType(), [Option]) -> ok when
Option :: {id, integer()} | {lastId, integer()} | {skip, boolean()} |
- {callback, function()} | {userData, term()}.
+ callback | {callback, function()} | {userData, term()}.
connect(This=#wx_ref{type=ThisT}, EventType, Options) ->
EvH = parse_opts(Options, #evh{et=EventType}),
?CLASS(ThisT,wxEvtHandler),
diff --git a/lib/wx/configure.in b/lib/wx/configure.in
index 8e5f696bc7..7b35fed672 100755
--- a/lib/wx/configure.in
+++ b/lib/wx/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 2008-2011. All Rights Reserved.
+dnl Copyright Ericsson AB 2008-2012. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/wx/doc/src/notes.xml b/lib/wx/doc/src/notes.xml
index 1ba7e62d7d..95036bd7f0 100644
--- a/lib/wx/doc/src/notes.xml
+++ b/lib/wx/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2009</year><year>2011</year>
+ <year>2009</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/wx/src/gen/wxEvtHandler.erl b/lib/wx/src/gen/wxEvtHandler.erl
index cf4a72da5a..22c203392c 100644
--- a/lib/wx/src/gen/wxEvtHandler.erl
+++ b/lib/wx/src/gen/wxEvtHandler.erl
@@ -80,7 +80,7 @@ connect(This, EventType) ->
%% {userData, term()} An erlang term that will be sent with the event. Default: [].
-spec connect(This::wxEvtHandler(), EventType::wxEventType(), [Option]) -> ok when
Option :: {id, integer()} | {lastId, integer()} | {skip, boolean()} |
- {callback, function()} | {userData, term()}.
+ callback | {callback, function()} | {userData, term()}.
connect(This=#wx_ref{type=ThisT}, EventType, Options) ->
EvH = parse_opts(Options, #evh{et=EventType}),
?CLASS(ThisT,wxEvtHandler),
diff --git a/lib/xmerl/doc/src/Makefile b/lib/xmerl/doc/src/Makefile
index 4de06249df..6f95e30a28 100644
--- a/lib/xmerl/doc/src/Makefile
+++ b/lib/xmerl/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2010. All Rights Reserved.
+# Copyright Ericsson AB 2004-2012. 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/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml
index bd12e688d0..056097ab2b 100644
--- a/lib/xmerl/doc/src/notes.xml
+++ b/lib/xmerl/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/xmerl/src/xmerl_sax_parser_base.erlsrc b/lib/xmerl/src/xmerl_sax_parser_base.erlsrc
index d38045f2a5..7b64d7c302 100644
--- a/lib/xmerl/src/xmerl_sax_parser_base.erlsrc
+++ b/lib/xmerl/src/xmerl_sax_parser_base.erlsrc
@@ -1,7 +1,7 @@
%%-*-erlang-*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. 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/lib/xmerl/test/Makefile b/lib/xmerl/test/Makefile
index 1fdbb7ca0d..ac3370dbda 100644
--- a/lib/xmerl/test/Makefile
+++ b/lib/xmerl/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2011. All Rights Reserved.
+# Copyright Ericsson AB 2004-2012. 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/make/emd2exml.in b/make/emd2exml.in
index 16c38379d9..b677ef8ed4 100644
--- a/make/emd2exml.in
+++ b/make/emd2exml.in
@@ -266,8 +266,11 @@ parse(#state{code = true, type = blank, line = CB, code_blank = CBs} = S) ->
parse(#state{code = true,
code_blank = CB,
list_lvl = Lvl,
- type = {text, TxtLvl},
- line = Line} = S) when TxtLvl > Lvl ->
+ type = {Type, TxtLvl},
+ line = Line} = S) when TxtLvl > Lvl,
+ (Type == text orelse
+ Type == uolist orelse
+ Type == olist) ->
Data = code(strip_lvls(Lvl+1, Line)),
parse(get_line(put_chars(S#state{code_blank = []},
[strip_code_blank(Lvl+1, CB), Data])));
@@ -994,7 +997,7 @@ resolve_link([_|Cs], start, "", "", "") ->
resolve_link(Cs, start, "", "", "");
resolve_link("]:" ++ Rest, key, Yek, "", "") ->
resolve_link(Rest, url, Yek, "", "");
-resolve_link([C|Cs], url, Yek, Lru, "") when C == $"; C == $' ->
+resolve_link([C|Cs], url, Yek, Lru, "") when C == $"; C == $' -> %"
resolve_link(Cs, {title, C}, Yek, Lru, "");
resolve_link([$(|Cs], url, Yek, Lru, "") ->
resolve_link(Cs, {title, $)}, Yek, Lru, "");
@@ -1010,9 +1013,21 @@ resolve_link([_|Cs], drop, Yek, Lru, Eltit) ->
resolve_link(Cs, drop, Yek, Lru, Eltit);
resolve_link([], _, Yek, Lru, Eltit) ->
{ws_strip(lists:reverse(Yek)),
- ws_strip(lists:reverse(Lru)),
+ ws_strip(md_strip_n_reverse(Lru)),
ws_strip(lists:reverse(Eltit))}.
+%% Remove .md at end of references.
+md_strip_n_reverse(Lru) ->
+ md_strip_n_reverse(Lru,[]).
+md_strip_n_reverse("\ndm."++Lru,Acc) ->
+ md_strip_n_reverse(Lru,Acc);
+md_strip_n_reverse("#dm."++Lru,Acc) ->
+ md_strip_n_reverse(Lru,[$#|Acc]);
+md_strip_n_reverse([C|T],Acc) ->
+ md_strip_n_reverse(T,[C|Acc]);
+md_strip_n_reverse([], Acc) ->
+ Acc.
+
%%
%% Misc
%%
diff --git a/make/otp.mk.in b/make/otp.mk.in
index 756cc85443..ed2fd8a70b 100644
--- a/make/otp.mk.in
+++ b/make/otp.mk.in
@@ -77,19 +77,13 @@ OTP_RELEASE = @OTP_RELEASE@
# Erlang language section
# ----------------------------------------------------
EMULATOR = beam
-ifeq ($(findstring vxworks,$(TARGET)),vxworks)
-# VxWorks object files should be compressed.
-# Other object files should have debug_info.
- ERL_COMPILE_FLAGS += +compressed
+ifeq ($(findstring ose_ppc750,$(TARGET)),ose_ppc750)
+ERL_COMPILE_FLAGS += +compressed
else
- ifeq ($(findstring ose_ppc750,$(TARGET)),ose_ppc750)
- ERL_COMPILE_FLAGS += +compressed
+ ifdef BOOTSTRAP
+ ERL_COMPILE_FLAGS += +slim
else
- ifdef BOOTSTRAP
- ERL_COMPILE_FLAGS += +slim
- else
- ERL_COMPILE_FLAGS += +debug_info
- endif
+ ERL_COMPILE_FLAGS += +debug_info
endif
endif
ERLC_WFLAGS = -W
diff --git a/make/otp_release_targets.mk b/make/otp_release_targets.mk
index fa87a4306a..67243b5ffd 100644
--- a/make/otp_release_targets.mk
+++ b/make/otp_release_targets.mk
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/otp_build b/otp_build
index 1fee139666..85be25a8a1 100755
--- a/otp_build
+++ b/otp_build
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+# Copyright Ericsson AB 2002-2012. 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/system/README b/system/README
index b8ff18119d..b1e18ef55c 100644
--- a/system/README
+++ b/system/README
@@ -51,10 +51,9 @@ Release of Erlang 5.9/OTP R15B
-------------------------------
3.1 The platform VxWorks is discontinued in the sense that only the
- libraries (erl_interface and ic's libraries) are supported. Running
- the Erlang emulator on VxWorks might still possible but it's no longer
- maintained. The VxWorks release is still packaged as a full release,
- but no support will be available for anything but the communication libraries.
+ libraries (erl_interface and ic's libraries) are supported. The VxWorks
+ release is still packaged as a full release, but no support will be
+ available for anything but the communication libraries.
4 NOTES ABOUT THE LINUX VERSIONS
-----------------------------
diff --git a/system/doc/design_principles/Makefile b/system/doc/design_principles/Makefile
index a9d4665ee4..73e1e2c6f7 100644
--- a/system/doc/design_principles/Makefile
+++ b/system/doc/design_principles/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/system/doc/efficiency_guide/Makefile b/system/doc/efficiency_guide/Makefile
index 1fd7b36453..655531f42b 100644
--- a/system/doc/efficiency_guide/Makefile
+++ b/system/doc/efficiency_guide/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-2012. 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/system/doc/embedded/Makefile b/system/doc/embedded/Makefile
index 825fb23cfe..2f78d7ddcd 100644
--- a/system/doc/embedded/Makefile
+++ b/system/doc/embedded/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/system/doc/embedded/vxworks.xml b/system/doc/embedded/vxworks.xml
deleted file mode 100644
index 52143a42e3..0000000000
--- a/system/doc/embedded/vxworks.xml
+++ /dev/null
@@ -1,193 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
-
-<chapter>
- <header>
- <copyright>
- <year>1997</year><year>2009</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- 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
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>VxWorks</title>
- <prepared>Patrik Winroth</prepared>
- <responsible></responsible>
- <docno></docno>
- <approved></approved>
- <checked></checked>
- <date>2000-10-17</date>
- <rev></rev>
- <file>vxworks.xml</file>
- </header>
- <p>This chapter describes the OS specific parts of OTP which relate
- to VxWorks.
- </p>
-
- <section>
- <title>Introduction</title>
- <p>The Erlang/OTP distribution for VxWorks is limited to what
- Switchboard requires (Switchboard is a general purpose
- switching hardware developed by Ericsson).
- </p>
- <p>Please consult the README file, included at root level in the
- installation, for latest information on the distribution.
- </p>
- </section>
-
- <section>
- <title>Memory Usage</title>
- <p>Memory required is 32 Mbyte.
- </p>
- </section>
-
- <section>
- <title>Disk Usage</title>
- <p>The disk space required is 22 Mbyte, the documentation included.
- </p>
- </section>
-
- <section>
- <title>Installation</title>
- <p>OTP/VxWorks is supplied in a distribution file named
- <c><![CDATA[<PREFIX>.tar.gz]]></c>; i.e. a tar archive that is
- compressed with gzip. <c><![CDATA[<PREFIX>]]></c> represents the
- name of the release,
- e.g. <c>otp_LXA12345_vxworks_cpu32_R42A</c>. Assuming you are
- installing to a Solaris file system, the installation is
- performed by following these steps: &lt;
- </p>
- <p></p>
- <list type="bulleted">
- <item>Change to the directory where you want to install
- OTP/VxWorks (<c><![CDATA[<ROOTDIR>]]></c>): <c><![CDATA[cd <ROOTDIR>]]></c></item>
- <item>Make a directory to put OTP/VxWorks in: <c>mkdir otp_vxworks_cpu32</c> (or whatever you want to call it)</item>
- <item>Change directory to the newly created one: <c>cd otp_vxworks_cpu32</c></item>
- <item>Copy the distribution file there from where it is located
- (<c><![CDATA[<RELDIR>]]></c>): <c><![CDATA[cp <RELDIR>/<PREFIX>.tar.gz .]]></c></item>
- <item>Unzip the distribution file: <c><![CDATA[gunzip <PREFIX>.tar.gz]]></c></item>
- <item>Untar <c><![CDATA[<PREFIX>.tar]]></c>: <c><![CDATA[tar xvf <PREFIX>.tar]]></c></item>
- <item>Create a bin directory: <c>mkdir bin</c></item>
- <item>Copy the VxWorks Erlang/OTP start-up script to the bin directory:
- <c>cp erts-Vsn/bin/erl bin/.</c></item>
- <item>Copy the example start scripts to the bin directory:
- <c>cp releases/R42A/*.boot bin/.</c></item>
- </list>
- <p>If you use VxWorks nfs mounting facility to mount the Solaris
- file system, this installation may be directly used. An other
- possibility is to copy the installation to a local VxWorks DOS
- file system, from where it is used.
- </p>
- </section>
-
- <section>
- <title>OS Specific Functionality/Information</title>
- <p>There are a couple of files that are unique to the VxWorks
- distribution of Erlang/OTP, these files are described here.
- </p>
- <list type="bulleted">
- <item>README - this files has some information on VxWorks
- specifics that you are advised to consult. This includes the
- latest information on what parts of OTP are included in the
- VxWorks distribution of Erlang/OTP. If you want us to
- include more parts, please contact us to discuss
- this.</item>
- <item>erts-Vsn/bin/resolv.conf - A resolver configuration EXAMPLE file.
- You have to edit this file.</item>
- <item>erts-Vsn/bin/erl - This is an EXAMPLE start script for VxWorks.
- You have to edit this file to suit your needs.</item>
- <item>erts-Vsn/bin/erl_io - One possible solution to the problem
- of competing Erlang and VxWorks shell. Contains the function
- 'start_erl' called by the erl script. Also contains the
- function 'to_erl' to be used when connecting to the Erlang
- shell from VxWorks' shell.</item>
- <item>erts-Vsn/bin/erl_exec - Rearranges command line arguments
- and starts Erlang.</item>
- <item>erts-Vsn/bin/vxcall - Allows spawning of standard VxWorks
- shell functions (which is just about any function in the
- system...) from open_port/2. E.g. open_port({spawn, 'vxcall
- func arg1 arg2'}, []) will cause the output that 'func arg1,
- arg2' would have given in the shell to be received from the
- port.</item>
- <item>erts-Vsn/bin/rdate - Set the time from a networked host,
- like the SunOS command. Nothing Erlang-specific, but nice
- if you want date/0 and time/0 to give meaningful values (you
- also need a TIMEZONE environment setting if GMT isn't
- acceptable). For example: <c>putenv "TIMEZONE=CET::-60:033002:102603"</c> sets central european
- time.</item>
- <item>erts-Vsn/src - Contains source for the above files, and
- additionally config.c, driver.h, preload.c and
- reclaim.h. Reclaim.h defines the interface to a simple
- mechanism for "resource reclamation" that is part of the
- Erlang runtime system - may be useful to "port program" writers (and
- possibly others). Take careful note of the caveats listed in
- the file!</item>
- </list>
- </section>
-
- <section>
- <title>Starting Erlang</title>
- <p>Start (and restart) of the system depends on what file system
- is used. To be able to start the system from a nfs mounted
- file system you can use VxWorks start script facility to run a
- start script similar to the example below. Note that the
- Erlang/OTP start-up script is run at the end of this script.
- </p>
- <code type="none"><![CDATA[
-# start.script v1.0 1997/09/08 patrik
-#
-# File name: start.script
-# Purpose: Starting the VxWorks/cpu32 erlang/OTP
-# Resides in: ~tornado/wind/target/config/ads360/
-
-#
-# Set shell prompt
-#
-shellPromptSet("sauron-> ")
-
-#
-# Set default gateway
-#
-hostAdd "router-20","150.236.20.251"
-routeAdd "0","router-20"
-
-#
-# Mount /home from gandalf
-#
-hostAdd "gandalf","150.236.20.16"
-usergroup=10
-nfsAuthUnixSet("gandalf", 452, 10, 1, &usergroup)
-nfsMount("gandalf", "/export/home", "/home")
-
-#
-# Load and run rdate.o to set correct date on the target
-#
-ld < /home/gandalf/tornado/wind/target/config/ads360/rdate.o
-rdate("gandalf")
-
-#
-# Setup timezone information (Central European time)
-#
-putenv "TIMEZONE=CET::-60:033002:102603"
-
-#
-# Run the Erlang/OTP start script
-#
-cd "/home/gandalf/tornado/wind/target/erlang_cpu32_R42A/bin"
-<erl
- ]]></code>
- </section>
-</chapter>
-
diff --git a/system/doc/embedded/xmlfiles.mk b/system/doc/embedded/xmlfiles.mk
index 2bdc34ae28..0001a55b9a 100644
--- a/system/doc/embedded/xmlfiles.mk
+++ b/system/doc/embedded/xmlfiles.mk
@@ -18,5 +18,4 @@
#
EMBEDDED_CHAPTER_FILES = \
embedded_solaris.xml \
- embedded_nt.xml \
- vxworks.xml
+ embedded_nt.xml
diff --git a/system/doc/getting_started/Makefile b/system/doc/getting_started/Makefile
index 972a51d3a5..ce53256b16 100644
--- a/system/doc/getting_started/Makefile
+++ b/system/doc/getting_started/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/system/doc/installation_guide/Makefile b/system/doc/installation_guide/Makefile
index 4636650570..6923f52d8a 100644
--- a/system/doc/installation_guide/Makefile
+++ b/system/doc/installation_guide/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2010. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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
@@ -58,7 +58,8 @@ XML_FILES = \
GENERATED_XML_FILES = \
INSTALL.xml \
INSTALL-CROSS.xml \
- INSTALL-WIN32.xml
+ INSTALL-WIN32.xml \
+ MARKDOWN.xml
# ----------------------------------------------------
@@ -73,7 +74,8 @@ REDIRECT_HTML_DIR = $(HTMLDIR)/source
REDIRECT_HTML_FILES = \
$(REDIRECT_HTML_DIR)/INSTALL.html \
$(REDIRECT_HTML_DIR)/INSTALL-CROSS.html \
- $(REDIRECT_HTML_DIR)/INSTALL-WIN32.html
+ $(REDIRECT_HTML_DIR)/INSTALL-WIN32.html \
+ $(REDIRECT_HTML_DIR)/MARKDOWN.html
# ----------------------------------------------------
# FLAGS
@@ -85,7 +87,7 @@ DVIPS_FLAGS +=
# Targets
# ----------------------------------------------------
-%.xml: $(ERL_TOP)/%.md $(ERL_TOP)/make/emd2exml
+%.xml: $(ERL_TOP)/HOWTO/%.md $(ERL_TOP)/make/emd2exml
$(ERL_TOP)/make/emd2exml $< $@
$(REDIRECT_HTML_DIR)/%.html: Makefile
diff --git a/system/doc/installation_guide/part.xml b/system/doc/installation_guide/part.xml
index fceacdd8f6..648beb0afd 100644
--- a/system/doc/installation_guide/part.xml
+++ b/system/doc/installation_guide/part.xml
@@ -36,5 +36,5 @@
<xi:include href="INSTALL.xml"/>
<xi:include href="INSTALL-CROSS.xml"/>
<xi:include href="INSTALL-WIN32.xml"/>
-</part>
-
+ <xi:include href="MARKDOWN.xml"/>
+</part> \ No newline at end of file
diff --git a/system/doc/installation_guide/xmlfiles.mk b/system/doc/installation_guide/xmlfiles.mk
index dee67b3c70..8ea2b296aa 100644
--- a/system/doc/installation_guide/xmlfiles.mk
+++ b/system/doc/installation_guide/xmlfiles.mk
@@ -21,4 +21,5 @@ INST_GUIDE_CHAPTER_FILES = \
verification.xml \
INSTALL.xml \
INSTALL-CROSS.xml \
- INSTALL-WIN32.xml
+ INSTALL-WIN32.xml \
+ MARKDOWN.xml
diff --git a/system/doc/oam/Makefile b/system/doc/oam/Makefile
index f459f4349f..ca07d82f75 100644
--- a/system/doc/oam/Makefile
+++ b/system/doc/oam/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/system/doc/programming_examples/Makefile b/system/doc/programming_examples/Makefile
index fcc51e3b18..41c27ed0dd 100644
--- a/system/doc/programming_examples/Makefile
+++ b/system/doc/programming_examples/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. 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/system/doc/reference_manual/Makefile b/system/doc/reference_manual/Makefile
index 87a477d14e..dbdfea2162 100644
--- a/system/doc/reference_manual/Makefile
+++ b/system/doc/reference_manual/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+# Copyright Ericsson AB 2003-2012. 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/system/doc/reference_manual/expressions.xml b/system/doc/reference_manual/expressions.xml
index d564b20ca6..357f89f731 100644
--- a/system/doc/reference_manual/expressions.xml
+++ b/system/doc/reference_manual/expressions.xml
@@ -236,10 +236,6 @@ Fun1 = fun(X) -> X+1 end
Fun1(3)
=> 4
-Fun2 = {lists,append}
-Fun2([1,2], [3,4])
-=> [1,2,3,4]
-
fun lists:append/2([1,2], [3,4])
=> [1,2,3,4]</code>
@@ -1000,13 +996,6 @@ fun (Arg1,...,ArgN) -> Name(Arg1,...,ArgN) end</pre>
<c>Module</c>. A fun defined in this way will not be dependent on
the code for module in which it is defined.
</p>
- <p>When applied to a number N of arguments, a tuple
- <c>{Module,FunctionName}</c> is interpreted as a fun, referring
- to the function <c>FunctionName</c> with arity N in the module
- <c>Module</c>. The function must be exported.
- <em>This usage is deprecated.</em> Use <c>fun Module:Name/Arity</c>
- instead.
- See <seealso marker="#calls">Function Calls</seealso> for an example.</p>
<p>More examples can be found in <em>Programming Examples</em>.</p>
</section>
diff --git a/system/doc/system_architecture_intro/Makefile b/system/doc/system_architecture_intro/Makefile
index 2de67473c1..0b5322de4f 100644
--- a/system/doc/system_architecture_intro/Makefile
+++ b/system/doc/system_architecture_intro/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2012. 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/system/doc/system_principles/Makefile b/system/doc/system_principles/Makefile
index 4b7fc8f177..b1c0fc3ddd 100644
--- a/system/doc/system_principles/Makefile
+++ b/system/doc/system_principles/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. 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/system/doc/top/Makefile b/system/doc/top/Makefile
index bbdb4d7055..9b305eb13b 100644
--- a/system/doc/top/Makefile
+++ b/system/doc/top/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+# Copyright Ericsson AB 1999-2012. 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/system/doc/top/templates/index.html.src b/system/doc/top/templates/index.html.src
index 655f532a5d..de87600ebd 100644
--- a/system/doc/top/templates/index.html.src
+++ b/system/doc/top/templates/index.html.src
@@ -2,7 +2,7 @@
<!--
%CopyrightBegin%
-Copyright Ericsson AB 2009-2010. All Rights Reserved.
+Copyright Ericsson AB 2009-2012. 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
@@ -94,7 +94,7 @@ Some hints that may get you started faster
<ul>
<li>
-The complete Erlang language is described in the
+The Erlang language is described in the
<a href="reference_manual/users_guide.html">Erlang Reference Manual</a>.
An Erlang tutorial can be found in
<a href="getting_started/users_guide.html">
@@ -109,6 +109,9 @@ In addition to the documentation here Erlang is described in several recent book
<li>
<a href="http://www.pragprog.com/titles/jaerlang">"Programming Erlang"</a> from Pragmatic.
</li>
+<li>
+<a href="http://www.manning.com/logan">"Erlang and OTP in Action"</a> from Manning.
+</li>
</ul>
<p>
These books are highly recommended as a start for learning Erlang.
@@ -136,23 +139,21 @@ tags support and more. See the <a href="#tools#/index.html">
Tools</a> application for details.
<p>
There is also an
-<a href="http://erlide.sourceforge.net">
+<a href="http://erlide.org/index.html">
Erlang plugin (ErlIDE) for Eclipse</a> if you prefer a more graphical
environment. ErlIDE is under active development with new features in almost every release.
<li>When developing with Erlang/OTP you usually test your programs
from the interactive shell (see <a href="getting_started/users_guide.html">
Getting Started With Erlang</a>) where you can call individual
functions. There is also a number of tools available, such as the graphical <a
-href="#debugger#/index.html" >Debugger</a>, the process
-manager <a href="#pman#/index.html" >Pman</a> and table
-viewer <a href="#tv#/index.html">TV</a>.
+href="#debugger#/index.html" >Debugger</a> and the <a href="#observer#/index.html" >Observer tool</a> for inspection of system information, ets and mnesia tables etc.
<p> Also note that there are some shell features like history list
(control-p and control-n), in line editing (Emacs key bindings) and
module and function name completion (tab) if the module is loaded.
<p>
<li>OpenSource users can ask questions
-and share experiences on the <a href="http://www.erlang.org/faq.html">
+and share experiences on the <a href="http://www.erlang.org/static/doc/mailinglist.html">
Erlang questions mailing list</a>. <p>
<li>Before asking a question you can browse the <a
diff --git a/system/doc/tutorial/Makefile b/system/doc/tutorial/Makefile
index e9eb800e46..682a8df4d5 100644
--- a/system/doc/tutorial/Makefile
+++ b/system/doc/tutorial/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-2012. 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/xcomp/README.md b/xcomp/README.md
index 5f4b36bdca..768efc9c7d 100644
--- a/xcomp/README.md
+++ b/xcomp/README.md
@@ -462,6 +462,10 @@ When a variable has been set, no warning will be issued.
`configure` will fail unless this variable is set. Since no default
value is used, `configure` will try to figure this out automatically.
+* `erl_xcomp_double_middle` - `yes|no`. Defaults to `no`.
+ If `yes`, the target system must have doubles in "middle-endian" format. If
+ `no`, it has "regular" endianness.
+
* `erl_xcomp_clock_gettime_cpu_time` - `yes|no`. Defaults to `no`. If `yes`,
the target system must have a working `clock_gettime()` implementation
that can be used for retrieving process CPU time.
@@ -523,7 +527,7 @@ Copyright and License
%CopyrightBegin%
-Copyright Ericsson AB 2009-2010. All Rights Reserved.
+Copyright Ericsson AB 2009-2012. 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/xcomp/erl-xcomp-TileraMDE2.0-tilepro.conf b/xcomp/erl-xcomp-TileraMDE2.0-tilepro.conf
index f9fff0fa8e..84bf735ff2 100644
--- a/xcomp/erl-xcomp-TileraMDE2.0-tilepro.conf
+++ b/xcomp/erl-xcomp-TileraMDE2.0-tilepro.conf
@@ -2,7 +2,7 @@
##
## %CopyrightBegin%
##
-## Copyright Ericsson AB 2009-2010. All Rights Reserved.
+## Copyright Ericsson AB 2009-2012. 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
@@ -187,6 +187,15 @@ erl_xcomp_sysroot="$TILERA_ROOT/tile"
# value is used, `configure' will try to figure this out automatically.
#erl_xcomp_bigendian=
+# * `erl_xcomp_double_middle_endian` - `yes|no`. No default. If `yes`, the
+# target system must have doubles in "middle-endian" format. If
+# `no`, it has "regular" endianness. This can often be automatically
+# detected, but not always. If not automatically detected,
+# `configure` will fail unless this variable is set. Since no
+# default value is used, `configure` will try to figure this out
+# automatically.
+#erl_xcomp_double_middle_endian
+
# * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes',
# the target system must have a working `clock_gettime()' implementation
# that can be used for retrieving process CPU time.
diff --git a/xcomp/erl-xcomp-avr32-atmel-linux-gnu.conf b/xcomp/erl-xcomp-avr32-atmel-linux-gnu.conf
index be22d498d6..e86043f5c8 100644
--- a/xcomp/erl-xcomp-avr32-atmel-linux-gnu.conf
+++ b/xcomp/erl-xcomp-avr32-atmel-linux-gnu.conf
@@ -190,6 +190,15 @@ erl_xcomp_sysroot="$ATMEL_SYSROOT_AVR32"
# value is used, `configure' will try to figure this out automatically.
#erl_xcomp_bigendian=
+# * `erl_xcomp_double_middle_endian` - `yes|no`. No default. If `yes`, the
+# target system must have doubles in "middle-endian" format. If
+# `no`, it has "regular" endianness. This can often be automatically
+# detected, but not always. If not automatically detected,
+# `configure` will fail unless this variable is set. Since no
+# default value is used, `configure` will try to figure this out
+# automatically.
+#erl_xcomp_double_middle_endian
+
# * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes',
# the target system must have a working `clock_gettime()' implementation
# that can be used for retrieving process CPU time.
diff --git a/xcomp/erl-xcomp-mips-linux.conf b/xcomp/erl-xcomp-mips-linux.conf
index 3ac057bb4c..e031bdcd26 100644
--- a/xcomp/erl-xcomp-mips-linux.conf
+++ b/xcomp/erl-xcomp-mips-linux.conf
@@ -2,7 +2,7 @@
##
## %CopyrightBegin%
##
-## Copyright Ericsson AB 2010. All Rights Reserved.
+## Copyright Ericsson AB 2010-2012. 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
@@ -187,6 +187,15 @@ CFLAGS=-Os
# value is used, `configure' will try to figure this out automatically.
#erl_xcomp_bigendian=
+# * `erl_xcomp_double_middle_endian` - `yes|no`. No default. If `yes`, the
+# target system must have doubles in "middle-endian" format. If
+# `no`, it has "regular" endianness. This can often be automatically
+# detected, but not always. If not automatically detected,
+# `configure` will fail unless this variable is set. Since no
+# default value is used, `configure` will try to figure this out
+# automatically.
+#erl_xcomp_double_middle_endian
+
# * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes',
# the target system must have a working `clock_gettime()' implementation
# that can be used for retrieving process CPU time.
diff --git a/xcomp/erl-xcomp-powerpc-dso-linux-gnu.conf b/xcomp/erl-xcomp-powerpc-dso-linux-gnu.conf
new file mode 100644
index 0000000000..0e0c2f2337
--- /dev/null
+++ b/xcomp/erl-xcomp-powerpc-dso-linux-gnu.conf
@@ -0,0 +1,258 @@
+## -*-shell-script-*-
+##
+## %CopyrightBegin%
+##
+## Copyright Ericsson AB 2009-2012. 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
+## compliance with the License. You should have received a copy of the
+## Erlang Public License along with this software. If not, it can be
+## retrieved online at http://www.erlang.org/.
+##
+## Software distributed under the License is distributed on an "AS IS"
+## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+## the License for the specific language governing rights and limitations
+## under the License.
+##
+## %CopyrightEnd%
+##
+## File: erl-xcomp.conf.template
+## Author: Rickard Green
+##
+## -----------------------------------------------------------------------------
+## When cross compiling Erlang/OTP using `otp_build', copy this file and set
+## the variables needed below. Then pass the path to the copy of this file as
+## an argument to `otp_build' in the configure stage:
+## `otp_build configure --xcomp-conf=<FILE>'
+## -----------------------------------------------------------------------------
+
+## Note that you cannot define arbitrary variables in a cross compilation
+## configuration file. Only the ones listed below will be guaranteed to be
+## visible throughout the whole execution of all `configure' scripts. Other
+## variables needs to be defined as arguments to `configure' or exported in
+## the environment.
+
+## -- Variables for `otp_build' Only -------------------------------------------
+
+## Variables in this section are only used, when configuring Erlang/OTP for
+## cross compilation using `$ERL_TOP/otp_build configure'.
+
+## *NOTE*! These variables currently have *no* effect if you configure using
+## the `configure' script directly.
+
+# * `erl_xcomp_build' - The build system used. This value will be passed as
+# `--build=$erl_xcomp_build' argument to the `configure' script. It does
+# not have to be a full `CPU-VENDOR-OS' triplet, but can be. The full
+# `CPU-VENDOR-OS' triplet will be created by
+# `$ERL_TOP/erts/autoconf/config.sub $erl_xcomp_build'. If set to `guess',
+# the build system will be guessed using
+# `$ERL_TOP/erts/autoconf/config.guess'.
+erl_xcomp_build=guess
+
+# * `erl_xcomp_host' - Cross host/target system to build for. This value will
+# be passed as `--host=$erl_xcomp_host' argument to the `configure' script.
+# It does not have to be a full `CPU-VENDOR-OS' triplet, but can be. The
+# full `CPU-VENDOR-OS' triplet will be created by
+# `$ERL_TOP/erts/autoconf/config.sub $erl_xcomp_host'.
+erl_xcomp_host=powerpc-dso-linux-gnu
+
+# * `erl_xcomp_configure_flags' - Extra configure flags to pass to the
+# `configure' script.
+erl_xcomp_configure_flags=--without-termcap
+
+## -- Cross Compiler and Other Tools -------------------------------------------
+
+## If the cross compilation tools are prefixed by `<HOST>-' you probably do
+## not need to set these variables (where `<HOST>' is what has been passed as
+## `--host=<HOST>' argument to `configure').
+
+## All variables in this section can also be used when native compiling.
+
+# * `CC' - C compiler.
+CC=powerpc-dso-linux-gnu-gcc
+
+# * `CFLAGS' - C compiler flags.
+#CFLAGS=
+
+# * `STATIC_CFLAGS' - Static C compiler flags.
+#STATIC_CFLAGS=
+
+# * `CFLAG_RUNTIME_LIBRARY_PATH' - This flag should set runtime library
+# search path for the shared libraries. Note that this actually is a
+# linker flag, but it needs to be passed via the compiler.
+#CFLAG_RUNTIME_LIBRARY_PATH=
+
+# * `CPP' - C pre-processor.
+#CPP=
+
+# * `CPPFLAGS' - C pre-processor flags.
+#CPPFLAGS=
+
+# * `CXX' - C++ compiler.
+CXX=powerpc-dso-linux-gnu-g++
+
+# * `CXXFLAGS' - C++ compiler flags.
+#CXXFLAGS=
+
+# * `LD' - Linker.
+LD=powerpc-dso-linux-gnu-ld
+
+# * `LDFLAGS' - Linker flags.
+#LDFLAGS=
+
+# * `LIBS' - Libraries.
+#LIBS=
+
+## -- *D*ynamic *E*rlang *D*river Linking --
+
+## *NOTE*! Either set all or none of the `DED_LD*' variables.
+
+# * `DED_LD' - Linker for Dynamically loaded Erlang Drivers.
+#DED_LD=
+
+# * `DED_LDFLAGS' - Linker flags to use with `DED_LD'.
+#DED_LDFLAGS=
+
+# * `DED_LD_FLAG_RUNTIME_LIBRARY_PATH' - This flag should set runtime library
+# search path for shared libraries when linking with `DED_LD'.
+#DED_LD_FLAG_RUNTIME_LIBRARY_PATH=
+
+## -- Large File Support --
+
+## *NOTE*! Either set all or none of the `LFS_*' variables.
+
+# * `LFS_CFLAGS' - Large file support C compiler flags.
+#LFS_CFLAGS=
+
+# * `LFS_LDFLAGS' - Large file support linker flags.
+#LFS_LDFLAGS=
+
+# * `LFS_LIBS' - Large file support libraries.
+#LFS_LIBS=
+
+## -- Other Tools --
+
+# * `RANLIB' - `ranlib' archive index tool.
+RANLIB=powerpc-dso-linux-gnu-ranlib
+
+# * `AR' - `ar' archiving tool.
+AR=powerpc-dso-linux-gnu-ar
+
+# * `GETCONF' - `getconf' system configuration inspection tool. `getconf' is
+# currently used for finding out large file support flags to use, and
+# on Linux systems for finding out if we have an NPTL thread library or
+# not.
+#GETCONF=
+
+## -- Cross System Root Locations ----------------------------------------------
+
+# * `erl_xcomp_sysroot' - The absolute path to the system root of the cross
+# compilation environment. Currently, the `crypto', `odbc', `ssh' and
+# `ssl' applications need the system root. These applications will be
+# skipped if the system root has not been set. The system root might be
+# needed for other things too. If this is the case and the system root
+# has not been set, `configure' will fail and request you to set it.
+erl_xcomp_sysroot="$DSO_SYSROOT_POWERPC"
+
+# * `erl_xcomp_isysroot' - The absolute path to the system root for includes
+# of the cross compilation environment. If not set, this value defaults
+# to `$erl_xcomp_sysroot', i.e., only set this value if the include system
+# root path is not the same as the system root path.
+#erl_xcomp_isysroot=
+
+## -- Optional Feature, and Bug Tests ------------------------------------------
+
+## These tests cannot (always) be done automatically when cross compiling. You
+## usually do not need to set these variables. Only set these if you really
+## know what you are doing.
+
+## Note that some of these values will override results of tests performed
+## by `configure', and some will not be used until `configure' is sure that
+## it cannot figure the result out.
+
+## The `configure' script will issue a warning when a default value is used.
+## When a variable has been set, no warning will be issued.
+
+# * `erl_xcomp_after_morecore_hook' - `yes|no'. Defaults to `no'. If `yes',
+# the target system must have a working `__after_morecore_hook' that can be
+# used for tracking used `malloc()' implementations core memory usage.
+# This is currently only used by unsupported features.
+erl_xcomp_after_morecore_hook=yes
+
+# * `erl_xcomp_bigendian' - `yes|no'. No default. If `yes', the target system
+# must be big endian. If `no', little endian. This can often be
+# automatically detected, but not always. If not automatically detected,
+# `configure' will fail unless this variable is set. Since no default
+# value is used, `configure' will try to figure this out automatically.
+#erl_xcomp_bigendian=
+
+# * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes',
+# the target system must have a working `clock_gettime()' implementation
+# that can be used for retrieving process CPU time.
+#erl_xcomp_clock_gettime_cpu_time=
+
+# * `erl_xcomp_getaddrinfo' - `yes|no'. Defaults to `no'. If `yes', the target
+# system must have a working `getaddrinfo()' implementation that can
+# handle both IPv4 and IPv6.
+erl_xcomp_getaddrinfo=yes
+
+# * `erl_xcomp_gethrvtime_procfs_ioctl' - `yes|no'. Defaults to `no'. If `yes',
+# the target system must have a working `gethrvtime()' implementation and
+# is used with procfs `ioctl()'.
+#erl_xcomp_gethrvtime_procfs_ioctl=
+
+# * `erl_xcomp_dlsym_brk_wrappers' - `yes|no'. Defaults to `no'. If `yes', the
+# target system must have a working `dlsym(RTLD_NEXT, <S>)' implementation
+# that can be used on `brk' and `sbrk' symbols used by the `malloc()'
+# implementation in use, and by this track the `malloc()' implementations
+# core memory usage. This is currently only used by unsupported features.
+#erl_xcomp_dlsym_brk_wrappers=
+
+# * `erl_xcomp_kqueue' - `yes|no'. Defaults to `no'. If `yes', the target
+# system must have a working `kqueue()' implementation that returns a file
+# descriptor which can be used by `poll()' and/or `select()'. If `no' and
+# the target system has not got `epoll()' or `/dev/poll', the kernel-poll
+# feature will be disabled.
+#erl_xcomp_kqueue=
+
+# * `erl_xcomp_linux_clock_gettime_correction' - `yes|no'. Defaults to `yes' on
+# Linux; otherwise, `no'. If `yes', `clock_gettime(CLOCK_MONOTONIC, _)' on
+# the target system must work. This variable is recommended to be set to
+# `no' on Linux systems with kernel versions less than 2.6.
+#erl_xcomp_linux_clock_gettime_correction=
+
+# * `erl_xcomp_linux_nptl' - `yes|no'. Defaults to `yes' on Linux; otherwise,
+# `no'. If `yes', the target system must have NPTL (Native POSIX Thread
+# Library). Older Linux systems have LinuxThreads instead of NPTL (Linux
+# kernel versions typically less than 2.6).
+#erl_xcomp_linux_nptl=
+
+# * `erl_xcomp_linux_usable_sigaltstack' - `yes|no'. Defaults to `yes' on Linux;
+# otherwise, `no'. If `yes', `sigaltstack()' must be usable on the target
+# system. `sigaltstack()' on Linux kernel versions less than 2.4 are
+# broken.
+#erl_xcomp_linux_usable_sigaltstack=
+
+# * `erl_xcomp_linux_usable_sigusrx' - `yes|no'. Defaults to `yes'. If `yes',
+# the `SIGUSR1' and `SIGUSR2' signals must be usable by the ERTS. Old
+# LinuxThreads thread libraries (Linux kernel versions typically less than
+# 2.2) used these signals and made them unusable by the ERTS.
+#erl_xcomp_linux_usable_sigusrx=
+
+# * `erl_xcomp_poll' - `yes|no'. Defaults to `no' on Darwin/MacOSX; otherwise,
+# `yes'. If `yes', the target system must have a working `poll()'
+# implementation that also can handle devices. If `no', `select()' will be
+# used instead of `poll()'.
+#erl_xcomp_poll=
+
+# * `erl_xcomp_putenv_copy' - `yes|no'. Defaults to `no'. If `yes', the target
+# system must have a `putenv()' implementation that stores a copy of the
+# key/value pair.
+#erl_xcomp_putenv_copy=
+
+# * `erl_xcomp_reliable_fpe' - `yes|no'. Defaults to `no'. If `yes', the target
+# system must have reliable floating point exceptions.
+#erl_xcomp_reliable_fpe=
+
+## -----------------------------------------------------------------------------
diff --git a/xcomp/erl-xcomp-vars.sh b/xcomp/erl-xcomp-vars.sh
index d9972eb3db..eccdff47e3 100644
--- a/xcomp/erl-xcomp-vars.sh
+++ b/xcomp/erl-xcomp-vars.sh
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010. All Rights Reserved.
+# Copyright Ericsson AB 2010-2012. 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
@@ -26,4 +26,4 @@
# and precious variables in $ERL_TOP/erts/aclocal.m4.
#
-erl_xcomp_vars="erl_xcomp_sysroot erl_xcomp_isysroot erl_xcomp_bigendian erl_xcomp_linux_clock_gettime_correction erl_xcomp_linux_nptl erl_xcomp_linux_usable_sigusrx erl_xcomp_linux_usable_sigaltstack erl_xcomp_poll erl_xcomp_kqueue erl_xcomp_putenv_copy erl_xcomp_reliable_fpe erl_xcomp_getaddrinfo erl_xcomp_gethrvtime_procfs_ioctl erl_xcomp_clock_gettime_cpu_time erl_xcomp_after_morecore_hook erl_xcomp_dlsym_brk_wrappers"
+erl_xcomp_vars="erl_xcomp_sysroot erl_xcomp_isysroot erl_xcomp_bigendian erl_xcomp_double_middle_endian erl_xcomp_linux_clock_gettime_correction erl_xcomp_linux_nptl erl_xcomp_linux_usable_sigusrx erl_xcomp_linux_usable_sigaltstack erl_xcomp_poll erl_xcomp_kqueue erl_xcomp_putenv_copy erl_xcomp_reliable_fpe erl_xcomp_getaddrinfo erl_xcomp_gethrvtime_procfs_ioctl erl_xcomp_clock_gettime_cpu_time erl_xcomp_after_morecore_hook erl_xcomp_dlsym_brk_wrappers"
diff --git a/xcomp/erl-xcomp-vxworks_ppc32.conf b/xcomp/erl-xcomp-vxworks_ppc32.conf
index ed8305ea93..7424cee496 100644
--- a/xcomp/erl-xcomp-vxworks_ppc32.conf
+++ b/xcomp/erl-xcomp-vxworks_ppc32.conf
@@ -2,7 +2,7 @@
##
## %CopyrightBegin%
##
-## Copyright Ericsson AB 2009-2010. All Rights Reserved.
+## Copyright Ericsson AB 2009-2012. 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
@@ -187,6 +187,15 @@ AR="$WIND_BASE/gnu/3.4.4-vxworks-6.3/$WIND_HOST_TYPE/bin/arppc"
# value is used, `configure' will try to figure this out automatically.
erl_xcomp_bigendian=yes
+# * `erl_xcomp_double_middle_endian` - `yes|no`. No default. If `yes`, the
+# target system must have doubles in "middle-endian" format. If
+# `no`, it has "regular" endianness. This can often be automatically
+# detected, but not always. If not automatically detected,
+# `configure` will fail unless this variable is set. Since no
+# default value is used, `configure` will try to figure this out
+# automatically.
+#erl_xcomp_double_middle_endian
+
# * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes',
# the target system must have a working `clock_gettime()' implementation
# that can be used for retrieving process CPU time.
diff --git a/xcomp/erl-xcomp-x86_64-saf-linux-gnu.conf b/xcomp/erl-xcomp-x86_64-saf-linux-gnu.conf
index f645ddbd96..d5b9d8c469 100644
--- a/xcomp/erl-xcomp-x86_64-saf-linux-gnu.conf
+++ b/xcomp/erl-xcomp-x86_64-saf-linux-gnu.conf
@@ -2,7 +2,7 @@
##
## %CopyrightBegin%
##
-## Copyright Ericsson AB 2010. All Rights Reserved.
+## Copyright Ericsson AB 2010-2012. 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
@@ -187,6 +187,15 @@ erl_xcomp_after_morecore_hook=yes
# value is used, `configure' will try to figure this out automatically.
#erl_xcomp_bigendian=
+# * `erl_xcomp_double_middle_endian` - `yes|no`. No default. If `yes`, the
+# target system must have doubles in "middle-endian" format. If
+# `no`, it has "regular" endianness. This can often be automatically
+# detected, but not always. If not automatically detected,
+# `configure` will fail unless this variable is set. Since no
+# default value is used, `configure` will try to figure this out
+# automatically.
+#erl_xcomp_double_middle_endian
+
# * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes',
# the target system must have a working `clock_gettime()' implementation
# that can be used for retrieving process CPU time.
diff --git a/xcomp/erl-xcomp.conf.template b/xcomp/erl-xcomp.conf.template
index eb331ffe92..7d70332cef 100644
--- a/xcomp/erl-xcomp.conf.template
+++ b/xcomp/erl-xcomp.conf.template
@@ -2,7 +2,7 @@
##
## %CopyrightBegin%
##
-## Copyright Ericsson AB 2009-2010. All Rights Reserved.
+## Copyright Ericsson AB 2009-2012. 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
@@ -187,6 +187,15 @@
# value is used, `configure' will try to figure this out automatically.
#erl_xcomp_bigendian=
+# * `erl_xcomp_double_middle` - `yes|no`. No default. If `yes`, the
+# target system must have doubles in "middle-endian" format. If
+# `no`, it has "regular" endianness. This can often be automatically
+# detected, but not always. If not automatically detected,
+# `configure` will fail unless this variable is set. Since no
+# default value is used, `configure` will try to figure this out
+# automatically.
+#erl_xcomp_double_middle_endian
+
# * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes',
# the target system must have a working `clock_gettime()' implementation
# that can be used for retrieving process CPU time.